From f31491dc73a6459d601da8d9a5817a31c7dfc17b Mon Sep 17 00:00:00 2001 From: Nick Cheng Date: Tue, 8 Sep 2009 19:03:07 +0800 Subject: [SCSI] mvsas: Support Areca SAS/SATA HBA, ARC-1300/1320 This is support for Areca SAS/SATA HBA, ARC-1300/1320, which quipped with 88SE6440/88SE9480 respectively. Signed-off-by: Nick Cheng< nick.cheng@areca.com.tw > Cc: Ke Wei Signed-off-by: James Bottomley --- drivers/scsi/mvsas/mv_defs.h | 4 ++++ drivers/scsi/mvsas/mv_init.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/scsi/mvsas/mv_defs.h b/drivers/scsi/mvsas/mv_defs.h index f8cb9defb961..1849da1f030d 100644 --- a/drivers/scsi/mvsas/mv_defs.h +++ b/drivers/scsi/mvsas/mv_defs.h @@ -25,6 +25,8 @@ #ifndef _MV_DEFS_H_ #define _MV_DEFS_H_ +#define PCI_DEVICE_ID_ARECA_1300 0x1300 +#define PCI_DEVICE_ID_ARECA_1320 0x1320 enum chip_flavors { chip_6320, @@ -32,6 +34,8 @@ enum chip_flavors { chip_6485, chip_9480, chip_9180, + chip_1300, + chip_1320 }; /* driver compile-time configuration */ diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index 8646a19f999d..c790d45876c4 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -32,6 +32,8 @@ static const struct mvs_chip_info mvs_chips[] = { [chip_6485] = { 1, 8, 0x800, 33, 32, 10, &mvs_64xx_dispatch, }, [chip_9180] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, }, [chip_9480] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, }, + [chip_1300] = { 1, 4, 0x400, 17, 16, 9, &mvs_64xx_dispatch, }, + [chip_1320] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, }, }; #define SOC_SAS_NUM 2 @@ -653,6 +655,8 @@ static struct pci_device_id __devinitdata mvs_pci_table[] = { { PCI_VDEVICE(MARVELL, 0x6485), chip_6485 }, { PCI_VDEVICE(MARVELL, 0x9480), chip_9480 }, { PCI_VDEVICE(MARVELL, 0x9180), chip_9180 }, + { PCI_VDEVICE(ARECA, PCI_DEVICE_ID_ARECA_1300), chip_1300 }, + { PCI_VDEVICE(ARECA, PCI_DEVICE_ID_ARECA_1320), chip_1320 }, { } /* terminate list */ }; -- cgit v1.2.3 From 348764024f1cd6e1c0e94e775a3a3cc2f8b41879 Mon Sep 17 00:00:00 2001 From: Anil Ravindranath Date: Wed, 9 Sep 2009 15:54:57 -0700 Subject: [SCSI] pmcraid: Changed driver prints to scmd/sdev_printk 1.Changed driver prints to use scmd_printk, sdev_printk 2.Changed dev_err calls to scmd_printk for scsi related print messages Signed-off-by: Anil Ravindranath Signed-off-by: James Bottomley --- drivers/scsi/pmcraid.c | 58 +++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 4302f06e4ec9..f7c70e2a8224 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -684,7 +685,7 @@ static void pmcraid_timeout_handler(struct pmcraid_cmd *cmd) struct pmcraid_instance *pinstance = cmd->drv_inst; unsigned long lock_flags; - dev_err(&pinstance->pdev->dev, + dev_info(&pinstance->pdev->dev, "Adapter being reset due to command timeout.\n"); /* Command timeouts result in hard reset sequence. The command that got @@ -815,8 +816,9 @@ static void pmcraid_erp_done(struct pmcraid_cmd *cmd) if (PMCRAID_IOASC_SENSE_KEY(ioasc) > 0) { scsi_cmd->result |= (DID_ERROR << 16); - pmcraid_err("command CDB[0] = %x failed with IOASC: 0x%08X\n", - cmd->ioa_cb->ioarcb.cdb[0], ioasc); + scmd_printk(KERN_INFO, scsi_cmd, + "command CDB[0] = %x failed with IOASC: 0x%08X\n", + cmd->ioa_cb->ioarcb.cdb[0], ioasc); } /* if we had allocated sense buffers for request sense, copy the sense @@ -1541,13 +1543,13 @@ static void pmcraid_handle_error_log(struct pmcraid_instance *pinstance) if (pinstance->ldn.hcam->notification_lost == HOSTRCB_NOTIFICATIONS_LOST) - dev_err(&pinstance->pdev->dev, "Error notifications lost\n"); + dev_info(&pinstance->pdev->dev, "Error notifications lost\n"); ioasc = le32_to_cpu(hcam_ldn->error_log.fd_ioasc); if (ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET || ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER) { - dev_err(&pinstance->pdev->dev, + dev_info(&pinstance->pdev->dev, "UnitAttention due to IOA Bus Reset\n"); scsi_report_bus_reset( pinstance->host, @@ -1584,7 +1586,7 @@ static void pmcraid_process_ccn(struct pmcraid_cmd *cmd) atomic_read(&pinstance->ccn.ignore) == 1) { return; } else if (ioasc) { - dev_err(&pinstance->pdev->dev, + dev_info(&pinstance->pdev->dev, "Host RCB (CCN) failed with IOASC: 0x%08X\n", ioasc); spin_lock_irqsave(pinstance->host->host_lock, lock_flags); pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE); @@ -1634,7 +1636,7 @@ static void pmcraid_process_ldn(struct pmcraid_cmd *cmd) return; } } else { - dev_err(&pinstance->pdev->dev, + dev_info(&pinstance->pdev->dev, "Host RCB(LDN) failed with IOASC: 0x%08X\n", ioasc); } /* send netlink message for HCAM notification if enabled */ @@ -1822,7 +1824,6 @@ static void pmcraid_fail_outstanding_cmds(struct pmcraid_instance *pinstance) scsi_dma_unmap(scsi_cmd); pmcraid_return_cmd(cmd); - pmcraid_info("failing(%d) CDB[0] = %x result: %x\n", le32_to_cpu(resp) >> 2, cmd->ioa_cb->ioarcb.cdb[0], @@ -2514,7 +2515,8 @@ static int pmcraid_reset_device( res = scsi_cmd->device->hostdata; if (!res) { - pmcraid_err("reset_device: NULL resource pointer\n"); + sdev_printk(KERN_ERR, scsi_cmd->device, + "reset_device: NULL resource pointer\n"); return FAILED; } @@ -2752,8 +2754,8 @@ static int pmcraid_eh_abort_handler(struct scsi_cmnd *scsi_cmd) pinstance = (struct pmcraid_instance *)scsi_cmd->device->host->hostdata; - dev_err(&pinstance->pdev->dev, - "I/O command timed out, aborting it.\n"); + scmd_printk(KERN_INFO, scsi_cmd, + "I/O command timed out, aborting it.\n"); res = scsi_cmd->device->hostdata; @@ -2824,7 +2826,8 @@ static int pmcraid_eh_abort_handler(struct scsi_cmnd *scsi_cmd) */ static int pmcraid_eh_device_reset_handler(struct scsi_cmnd *scmd) { - pmcraid_err("Doing device reset due to an I/O command timeout.\n"); + scmd_printk(KERN_INFO, scmd, + "resetting device due to an I/O command timeout.\n"); return pmcraid_reset_device(scmd, PMCRAID_INTERNAL_TIMEOUT, RESET_DEVICE_LUN); @@ -2832,7 +2835,8 @@ static int pmcraid_eh_device_reset_handler(struct scsi_cmnd *scmd) static int pmcraid_eh_bus_reset_handler(struct scsi_cmnd *scmd) { - pmcraid_err("Doing bus reset due to an I/O command timeout.\n"); + scmd_printk(KERN_INFO, scmd, + "Doing bus reset due to an I/O command timeout.\n"); return pmcraid_reset_device(scmd, PMCRAID_RESET_BUS_TIMEOUT, RESET_DEVICE_BUS); @@ -2840,7 +2844,8 @@ static int pmcraid_eh_bus_reset_handler(struct scsi_cmnd *scmd) static int pmcraid_eh_target_reset_handler(struct scsi_cmnd *scmd) { - pmcraid_err("Doing target reset due to an I/O command timeout.\n"); + scmd_printk(KERN_INFO, scmd, + "Doing target reset due to an I/O command timeout.\n"); return pmcraid_reset_device(scmd, PMCRAID_INTERNAL_TIMEOUT, RESET_DEVICE_TARGET); @@ -2988,11 +2993,11 @@ static int pmcraid_build_ioadl( nseg = scsi_dma_map(scsi_cmd); if (nseg < 0) { - dev_err(&pinstance->pdev->dev, "scsi_map_dma failed!\n"); + scmd_printk(KERN_ERR, scsi_cmd, "scsi_map_dma failed!\n"); return -1; } else if (nseg > PMCRAID_MAX_IOADLS) { scsi_dma_unmap(scsi_cmd); - dev_err(&pinstance->pdev->dev, + scmd_printk(KERN_ERR, scsi_cmd, "sg count is (%d) more than allowed!\n", nseg); return -1; } @@ -5040,7 +5045,7 @@ static int pmcraid_resume(struct pci_dev *pdev) rc = pci_enable_device(pdev); if (rc) { - pmcraid_err("pmcraid: Enable device failed\n"); + dev_err(&pdev->dev, "resume: Enable device failed\n"); return rc; } @@ -5054,7 +5059,7 @@ static int pmcraid_resume(struct pci_dev *pdev) rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (rc != 0) { - dev_err(&pdev->dev, "Failed to set PCI DMA mask\n"); + dev_err(&pdev->dev, "resume: Failed to set PCI DMA mask\n"); goto disable_device; } @@ -5063,7 +5068,8 @@ static int pmcraid_resume(struct pci_dev *pdev) rc = pmcraid_register_interrupt_handler(pinstance); if (rc) { - pmcraid_err("resume: couldn't register interrupt handlers\n"); + dev_err(&pdev->dev, + "resume: couldn't register interrupt handlers\n"); rc = -ENODEV; goto release_host; } @@ -5080,7 +5086,7 @@ static int pmcraid_resume(struct pci_dev *pdev) * state. */ if (pmcraid_reset_bringup(pinstance)) { - pmcraid_err("couldn't initialize IOA \n"); + dev_err(&pdev->dev, "couldn't initialize IOA \n"); rc = -ENODEV; goto release_tasklets; } @@ -5187,7 +5193,7 @@ static void pmcraid_init_res_table(struct pmcraid_cmd *cmd) LIST_HEAD(old_res); if (pinstance->cfg_table->flags & MICROCODE_UPDATE_REQUIRED) - dev_err(&pinstance->pdev->dev, "Require microcode download\n"); + pmcraid_err("IOA requires microcode download\n"); /* resource list is protected by pinstance->resource_lock. * init_res_table can be called from probe (user-thread) or runtime @@ -5224,8 +5230,7 @@ static void pmcraid_init_res_table(struct pmcraid_cmd *cmd) if (!found) { if (list_empty(&pinstance->free_res_q)) { - dev_err(&pinstance->pdev->dev, - "Too many devices attached\n"); + pmcraid_err("Too many devices attached\n"); break; } @@ -5442,7 +5447,7 @@ static int __devinit pmcraid_probe( rc = pmcraid_register_interrupt_handler(pinstance); if (rc) { - pmcraid_err("couldn't register interrupt handler\n"); + dev_err(&pdev->dev, "couldn't register interrupt handler\n"); goto out_scsi_host_put; } @@ -5466,7 +5471,7 @@ static int __devinit pmcraid_probe( */ pmcraid_info("starting IOA initialization sequence\n"); if (pmcraid_reset_bringup(pinstance)) { - pmcraid_err("couldn't initialize IOA \n"); + dev_err(&pdev->dev, "couldn't initialize IOA \n"); rc = 1; goto out_release_bufs; } @@ -5534,7 +5539,6 @@ static struct pci_driver pmcraid_driver = { .shutdown = pmcraid_shutdown }; - /** * pmcraid_init - module load entry point */ @@ -5566,7 +5570,6 @@ static int __init pmcraid_init(void) goto out_unreg_chrdev; } - error = pmcraid_netlink_init(); if (error) @@ -5584,6 +5587,7 @@ static int __init pmcraid_init(void) out_unreg_chrdev: unregister_chrdev_region(MKDEV(pmcraid_major, 0), PMCRAID_MAX_ADAPTERS); + out_init: return error; } -- cgit v1.2.3 From c0630f76d09131d606857a3da39739791d2c7b35 Mon Sep 17 00:00:00 2001 From: "Moger, Babu" Date: Fri, 28 Aug 2009 08:18:39 -0600 Subject: [SCSI] scsi_dh_rdac: Fix for returning correct mode select cmd return info The function mode_select_handle_sense returns SCSI_DH_OK even when there is a sense code which is incorrect. Removing it so that it returns SCSI_DH_IO when there is sense that is not handled by this function. Signed-off-by: Babu Moger Reviewed-by: Vijay Chauhan Reviewed-by: Bob Stankey Reviewed-by: Chandra Seetharaman Signed-off-by: James Bottomley --- drivers/scsi/device_handler/scsi_dh_rdac.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 11c89311427e..268189d31d9c 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -500,8 +500,6 @@ static int mode_select_handle_sense(struct scsi_device *sdev, if (!ret) goto done; - err = SCSI_DH_OK; - switch (sense_hdr.sense_key) { case NO_SENSE: case ABORTED_COMMAND: -- cgit v1.2.3 From 3bfc13c239fd56ebc1ac98a914c6c6b8b0045478 Mon Sep 17 00:00:00 2001 From: HighPoint Linux Team Date: Fri, 11 Sep 2009 17:21:27 +0800 Subject: [SCSI] hptiop: Add RR44xx adapter support Most code changes were made to support RR44xx adapters. - add more PCI device ID. - using PCI BAR[2] to access RR44xx IOP. - using PCI BAR[0] to check and clear RR44xx IRQ. Signed-off-by: HighPoint Linux Team Signed-off-by: James Bottomley --- Documentation/scsi/hptiop.txt | 21 ++++++++++++++++++++- drivers/scsi/hptiop.c | 37 ++++++++++++++++++++++++++----------- drivers/scsi/hptiop.h | 3 ++- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/Documentation/scsi/hptiop.txt b/Documentation/scsi/hptiop.txt index a6eb4add1be6..9605179711f4 100644 --- a/Documentation/scsi/hptiop.txt +++ b/Documentation/scsi/hptiop.txt @@ -3,6 +3,25 @@ HIGHPOINT ROCKETRAID 3xxx/4xxx ADAPTER DRIVER (hptiop) Controller Register Map ------------------------- +For RR44xx Intel IOP based adapters, the controller IOP is accessed via PCI BAR0 and BAR2: + + BAR0 offset Register + 0x11C5C Link Interface IRQ Set + 0x11C60 Link Interface IRQ Clear + + BAR2 offset Register + 0x10 Inbound Message Register 0 + 0x14 Inbound Message Register 1 + 0x18 Outbound Message Register 0 + 0x1C Outbound Message Register 1 + 0x20 Inbound Doorbell Register + 0x24 Inbound Interrupt Status Register + 0x28 Inbound Interrupt Mask Register + 0x30 Outbound Interrupt Status Register + 0x34 Outbound Interrupt Mask Register + 0x40 Inbound Queue Port + 0x44 Outbound Queue Port + For Intel IOP based adapters, the controller IOP is accessed via PCI BAR0: BAR0 offset Register @@ -93,7 +112,7 @@ The driver exposes following sysfs attributes: ----------------------------------------------------------------------------- -Copyright (C) 2006-2007 HighPoint Technologies, Inc. All Rights Reserved. +Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c index c596ab5f05c3..a0e7e711ff9d 100644 --- a/drivers/scsi/hptiop.c +++ b/drivers/scsi/hptiop.c @@ -1,6 +1,6 @@ /* * HighPoint RR3xxx/4xxx controller driver for Linux - * Copyright (C) 2006-2007 HighPoint Technologies, Inc. All Rights Reserved. + * Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,7 +41,7 @@ MODULE_DESCRIPTION("HighPoint RocketRAID 3xxx/4xxx Controller Driver"); static char driver_name[] = "hptiop"; static const char driver_name_long[] = "RocketRAID 3xxx/4xxx Controller driver"; -static const char driver_ver[] = "v1.3 (071203)"; +static const char driver_ver[] = "v1.6 (090910)"; static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec); static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag, @@ -115,9 +115,13 @@ static void hptiop_drain_outbound_queue_itl(struct hptiop_hba *hba) static int iop_intr_itl(struct hptiop_hba *hba) { struct hpt_iopmu_itl __iomem *iop = hba->u.itl.iop; + void __iomem *plx = hba->u.itl.plx; u32 status; int ret = 0; + if (plx && readl(plx + 0x11C5C) & 0xf) + writel(1, plx + 0x11C60); + status = readl(&iop->outbound_intstatus); if (status & IOPMU_OUTBOUND_INT_MSG0) { @@ -460,15 +464,25 @@ static void __iomem *hptiop_map_pci_bar(struct hptiop_hba *hba, int index) static int hptiop_map_pci_bar_itl(struct hptiop_hba *hba) { + struct pci_dev *pcidev = hba->pcidev; hba->u.itl.iop = hptiop_map_pci_bar(hba, 0); - if (hba->u.itl.iop) - return 0; - else + if (hba->u.itl.iop == NULL) return -1; + if ((pcidev->device & 0xff00) == 0x4400) { + hba->u.itl.plx = hba->u.itl.iop; + hba->u.itl.iop = hptiop_map_pci_bar(hba, 2); + if (hba->u.itl.iop == NULL) { + iounmap(hba->u.itl.plx); + return -1; + } + } + return 0; } static void hptiop_unmap_pci_bar_itl(struct hptiop_hba *hba) { + if (hba->u.itl.plx) + iounmap(hba->u.itl.plx); iounmap(hba->u.itl.iop); } @@ -1239,22 +1253,23 @@ static struct hptiop_adapter_ops hptiop_mv_ops = { static struct pci_device_id hptiop_id_table[] = { { PCI_VDEVICE(TTI, 0x3220), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3320), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x3520), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x4320), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x3410), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3510), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3511), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x3520), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3521), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3522), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x3410), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x3540), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3530), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x3540), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3560), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x4322), (kernel_ulong_t)&hptiop_itl_ops }, - { PCI_VDEVICE(TTI, 0x4321), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x4210), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x4211), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x4310), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x4311), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x4320), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x4321), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x4322), (kernel_ulong_t)&hptiop_itl_ops }, + { PCI_VDEVICE(TTI, 0x4400), (kernel_ulong_t)&hptiop_itl_ops }, { PCI_VDEVICE(TTI, 0x3120), (kernel_ulong_t)&hptiop_mv_ops }, { PCI_VDEVICE(TTI, 0x3122), (kernel_ulong_t)&hptiop_mv_ops }, { PCI_VDEVICE(TTI, 0x3020), (kernel_ulong_t)&hptiop_mv_ops }, diff --git a/drivers/scsi/hptiop.h b/drivers/scsi/hptiop.h index a0289f219752..0b871c0ae568 100644 --- a/drivers/scsi/hptiop.h +++ b/drivers/scsi/hptiop.h @@ -1,6 +1,6 @@ /* * HighPoint RR3xxx/4xxx controller driver for Linux - * Copyright (C) 2006-2007 HighPoint Technologies, Inc. All Rights Reserved. + * Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -228,6 +228,7 @@ struct hptiop_hba { union { struct { struct hpt_iopmu_itl __iomem *iop; + void __iomem *plx; } itl; struct { struct hpt_iopmv_regs *regs; -- cgit v1.2.3 From 53203244a4f9988f132ef481867ff47d6bd055b5 Mon Sep 17 00:00:00 2001 From: Anil Veerabhadrappa Date: Fri, 11 Sep 2009 10:38:26 -0700 Subject: [SCSI] bnx2i: Fix context mapping issue for architectures with PAGE_SIZE != 4096 5706/5708/5709 devices allow driver/user to set page size. By default it is set to 4096. Current drivers do not program this register based on architecture type (e.g. x86 = 4K, IA64 = 16K) and by choice lets device use the defaults. So while mapping connection context memory (doorebll registers), driver has to match page size used by the device. Included change fixes the issue we uncovered during IA64 testing Signed-off-by: Anil Veerabhadrappa Signed-off-by: James Bottomley --- drivers/scsi/bnx2i/bnx2i.h | 2 ++ drivers/scsi/bnx2i/bnx2i_hwi.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h index d7576f28c6e9..5edde1a8c04d 100644 --- a/drivers/scsi/bnx2i/bnx2i.h +++ b/drivers/scsi/bnx2i/bnx2i.h @@ -100,6 +100,8 @@ #define CTX_OFFSET 0x10000 #define MAX_CID_CNT 0x4000 +#define BNX2I_570X_PAGE_SIZE_DEFAULT 4096 + /* 5709 context registers */ #define BNX2_MQ_CONFIG2 0x00003d00 #define BNX2_MQ_CONFIG2_CONT_SZ (0x7L<<4) diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index 41e1b0e7e2ef..5c8d7630c13e 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -2386,7 +2386,7 @@ int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep) ctx_sz = (config2 & BNX2_MQ_CONFIG2_CONT_SZ) >> 3; if (ctx_sz) reg_off = CTX_OFFSET + MAX_CID_CNT * MB_KERNEL_CTX_SIZE - + PAGE_SIZE * + + BNX2I_570X_PAGE_SIZE_DEFAULT * (((cid_num - first_l4l5) / ctx_sz) + 256); else reg_off = CTX_OFFSET + (MB_KERNEL_CTX_SIZE * cid_num); -- cgit v1.2.3 From 5915136d4d3954867cced8a2297bddd16caf36a1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 4 Sep 2009 11:38:02 +0900 Subject: [SCSI] sr: consider the last written sector when determining media size On certain cases, UDF disc doesn't report capacity correctly via READ_CAPACITY but TOC or trackinfo contains valid information which can be obtained using cdrom_get_last_written(). ide-cd considers both values and uses the larger one. Do the same in sr. This fixes bko#9668. http://bugzilla.kernel.org/show_bug.cgi?id=9668 Signed-off-by: Tejun Heo Reported-by: Milan Kocian Cc: Jan Kara Signed-off-by: James Bottomley --- drivers/scsi/sr.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index eb61f7a70e1d..d6f340f48a3b 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -684,14 +684,20 @@ static void get_sectorsize(struct scsi_cd *cd) cd->capacity = 0x1fffff; sector_size = 2048; /* A guess, just in case */ } else { -#if 0 - if (cdrom_get_last_written(&cd->cdi, - &cd->capacity)) -#endif - cd->capacity = 1 + ((buffer[0] << 24) | - (buffer[1] << 16) | - (buffer[2] << 8) | - buffer[3]); + long last_written; + + cd->capacity = 1 + ((buffer[0] << 24) | (buffer[1] << 16) | + (buffer[2] << 8) | buffer[3]); + /* + * READ_CAPACITY doesn't return the correct size on + * certain UDF media. If last_written is larger, use + * it instead. + * + * http://bugzilla.kernel.org/show_bug.cgi?id=9668 + */ + if (!cdrom_get_last_written(&cd->cdi, &last_written)) + cd->capacity = max_t(long, cd->capacity, last_written); + sector_size = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; switch (sector_size) { -- cgit v1.2.3 From e27168f8c337b12b8aa8d59c3123c79d2f83603d Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Thu, 17 Sep 2009 09:10:14 +0200 Subject: [SCSI] sg: Free data buffers after calling blk_rq_unmap_user Running sg_luns on s390x with CONFIG_DEBUG_PAGEALLOC enabled fails with EFAULT from the SG_IO ioctl. The EFAULT is the result from copy_to_user failing in this call chain: sg_ioctl sg_new_read sg_finish_rem_req blk_rq_unmap_user __blk_rq_unmap_user bio_uncopy_user __bio_copy_iov copy_to_user The sg driver calls sg_remove_scat to free the memory pages before calling blk_rq_unmap_user that tries to copy the data back to userspace. Change the order to first call blk_rq_unmap_user before freeing the pages in sg_remove_scat. Acked-by: FUJITA Tomonori Cc: stable@kernel.org Signed-off-by: Christof Schmitt Acked-by: Douglas Gilbert Signed-off-by: James Bottomley --- drivers/scsi/sg.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 0cb049f5cc56..a328b5f284de 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1708,11 +1708,6 @@ static int sg_finish_rem_req(Sg_request * srp) Sg_scatter_hold *req_schp = &srp->data; SCSI_LOG_TIMEOUT(4, printk("sg_finish_rem_req: res_used=%d\n", (int) srp->res_used)); - if (srp->res_used) - sg_unlink_reserve(sfp, srp); - else - sg_remove_scat(req_schp); - if (srp->rq) { if (srp->bio) ret = blk_rq_unmap_user(srp->bio); @@ -1720,6 +1715,11 @@ static int sg_finish_rem_req(Sg_request * srp) blk_put_request(srp->rq); } + if (srp->res_used) + sg_unlink_reserve(sfp, srp); + else + sg_remove_scat(req_schp); + sg_remove_request(sfp, srp); return ret; -- cgit v1.2.3 From 6e883b0e42739aa560133cfaf41be1138c51a500 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 17 Sep 2009 17:00:26 +0200 Subject: [SCSI] Retry ADD_TO_MLQUEUE return value for EH commands A target reset when I/O is ongoing might result an eventual device offline, as scsi_eh_completed_normally() might return ADD_TO_MLQUEUE in addition to the advertised SUCCESS, FAILED, and NEEDS_RETRY. Which is unfortunate as scsi_send_eh_cmnd() will therefore map ADD_TO_MLQUEUE to FAILED instead of the more appropriate NEEDS_RETRY. Signed-off-by: Hannes Reinecke Cc: Stable Tree Signed-off-by: James Bottomley --- drivers/scsi/scsi_error.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 877204daf549..1b0060b791e8 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -725,6 +725,9 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, case NEEDS_RETRY: case FAILED: break; + case ADD_TO_MLQUEUE: + rtn = NEEDS_RETRY; + break; default: rtn = FAILED; break; -- cgit v1.2.3 From c6af404215bab0d333accbb497f835d10cb0050c Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Fri, 18 Sep 2009 17:32:59 -0400 Subject: [SCSI] Deprecate SCSI_PROT_*_CONVERT operations The checksum format is orthogonal to whether the protection information is being passed on beyond the HBA or not. It is perfectly valid to use a non-T10 CRC with WRITE_STRIP and READ_INSERT. Consequently it no longer makes sense to explicitly refer to the conversion in the protection operation. Update sd_dif and lpfc accordingly. Signed-off-by: Martin K. Petersen Acked-by: Ihab Hamadi Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_scsi.c | 15 +++------------ drivers/scsi/sd_dif.c | 20 ++++---------------- include/scsi/scsi_cmnd.h | 4 ---- 3 files changed, 7 insertions(+), 32 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 61d089703806..c88f59f0ce30 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -56,8 +56,6 @@ static char *dif_op_str[] = { "SCSI_PROT_WRITE_INSERT", "SCSI_PROT_READ_PASS", "SCSI_PROT_WRITE_PASS", - "SCSI_PROT_READ_CONVERT", - "SCSI_PROT_WRITE_CONVERT" }; static void lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); @@ -1131,13 +1129,11 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc) ret_prof = LPFC_PROF_A1; break; - case SCSI_PROT_READ_CONVERT: - case SCSI_PROT_WRITE_CONVERT: + case SCSI_PROT_READ_PASS: + case SCSI_PROT_WRITE_PASS: ret_prof = LPFC_PROF_AST1; break; - case SCSI_PROT_READ_PASS: - case SCSI_PROT_WRITE_PASS: case SCSI_PROT_NORMAL: default: printk(KERN_ERR "Bad op/guard:%d/%d combination\n", @@ -1157,8 +1153,6 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc) ret_prof = LPFC_PROF_C1; break; - case SCSI_PROT_READ_CONVERT: - case SCSI_PROT_WRITE_CONVERT: case SCSI_PROT_READ_INSERT: case SCSI_PROT_WRITE_STRIP: case SCSI_PROT_NORMAL: @@ -1209,8 +1203,7 @@ lpfc_get_cmd_dif_parms(struct scsi_cmnd *sc, uint16_t *apptagmask, static int cnt; if (protcnt && (op == SCSI_PROT_WRITE_STRIP || - op == SCSI_PROT_WRITE_PASS || - op == SCSI_PROT_WRITE_CONVERT)) { + op == SCSI_PROT_WRITE_PASS)) { cnt++; spt = page_address(sg_page(scsi_prot_sglist(sc))) + @@ -1501,8 +1494,6 @@ lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc) case SCSI_PROT_WRITE_STRIP: case SCSI_PROT_READ_PASS: case SCSI_PROT_WRITE_PASS: - case SCSI_PROT_WRITE_CONVERT: - case SCSI_PROT_READ_CONVERT: ret = LPFC_PG_TYPE_DIF_BUF; break; default: diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c index 82f14a9482d0..84224dd21acf 100644 --- a/drivers/scsi/sd_dif.c +++ b/drivers/scsi/sd_dif.c @@ -364,15 +364,9 @@ void sd_dif_config_host(struct scsi_disk *sdkp) */ void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix, unsigned int type) { - int csum_convert, prot_op; + int prot_op; - prot_op = 0; - - /* Convert checksum? */ - if (scsi_host_get_guard(scmd->device->host) != SHOST_DIX_GUARD_CRC) - csum_convert = 1; - else - csum_convert = 0; + prot_op = SCSI_PROT_NORMAL; BUG_ON(dif && (scmd->cmnd[0] == READ_6 || scmd->cmnd[0] == WRITE_6)); @@ -382,10 +376,7 @@ void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix, unsig case READ_12: case READ_16: if (dif && dix) - if (csum_convert) - prot_op = SCSI_PROT_READ_CONVERT; - else - prot_op = SCSI_PROT_READ_PASS; + prot_op = SCSI_PROT_READ_PASS; else if (dif && !dix) prot_op = SCSI_PROT_READ_STRIP; else if (!dif && dix) @@ -398,10 +389,7 @@ void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix, unsig case WRITE_12: case WRITE_16: if (dif && dix) - if (csum_convert) - prot_op = SCSI_PROT_WRITE_CONVERT; - else - prot_op = SCSI_PROT_WRITE_PASS; + prot_op = SCSI_PROT_WRITE_PASS; else if (dif && !dix) prot_op = SCSI_PROT_WRITE_INSERT; else if (!dif && dix) diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 3878d1dc7f59..a5e885a111df 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -229,10 +229,6 @@ enum scsi_prot_operations { /* OS-HBA: Protected, HBA-Target: Protected */ SCSI_PROT_READ_PASS, SCSI_PROT_WRITE_PASS, - - /* OS-HBA: Protected, HBA-Target: Protected, checksum conversion */ - SCSI_PROT_READ_CONVERT, - SCSI_PROT_WRITE_CONVERT, }; static inline void scsi_set_prot_op(struct scsi_cmnd *scmd, unsigned char op) -- cgit v1.2.3 From 35e1a5d90b66487d754ef2f2dcbf1007f806d921 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Fri, 18 Sep 2009 17:33:00 -0400 Subject: [SCSI] sd: Detach DIF from block integrity infrastructure So far we have only issued DIF commands if CONFIG_BLK_DEV_INTEGRITY is enabled. However, communication between initiator and target should be independent of protection information DMA. There are DIF-only host adapters coming out that will be able to take advantage of this. Move the relevant DIF bits to sd.c. Signed-off-by: Martin K. Petersen Signed-off-by: James Bottomley --- drivers/scsi/sd.c | 61 +++++++++++++++++++++++++++++++++--------------- drivers/scsi/sd.h | 4 ---- drivers/scsi/sd_dif.c | 53 ----------------------------------------- include/scsi/scsi_host.h | 15 ++++++++---- 4 files changed, 53 insertions(+), 80 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 8dd96dcd716c..1e0a0b07dab6 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -370,6 +370,31 @@ static void scsi_disk_put(struct scsi_disk *sdkp) mutex_unlock(&sd_ref_mutex); } +static void sd_prot_op(struct scsi_cmnd *scmd, unsigned int dif) +{ + unsigned int prot_op = SCSI_PROT_NORMAL; + unsigned int dix = scsi_prot_sg_count(scmd); + + if (scmd->sc_data_direction == DMA_FROM_DEVICE) { + if (dif && dix) + prot_op = SCSI_PROT_READ_PASS; + else if (dif && !dix) + prot_op = SCSI_PROT_READ_STRIP; + else if (!dif && dix) + prot_op = SCSI_PROT_READ_INSERT; + } else { + if (dif && dix) + prot_op = SCSI_PROT_WRITE_PASS; + else if (dif && !dix) + prot_op = SCSI_PROT_WRITE_INSERT; + else if (!dif && dix) + prot_op = SCSI_PROT_WRITE_STRIP; + } + + scsi_set_prot_op(scmd, prot_op); + scsi_set_prot_type(scmd, dif); +} + /** * sd_init_command - build a scsi (read or write) command from * information in the request structure. @@ -578,8 +603,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) /* If DIF or DIX is enabled, tell HBA how to handle request */ if (host_dif || scsi_prot_sg_count(SCpnt)) - sd_dif_op(SCpnt, host_dif, scsi_prot_sg_count(SCpnt), - sdkp->protection_type); + sd_prot_op(SCpnt, host_dif); /* * We shouldn't disconnect in the middle of a sector, so with a dumb @@ -1238,34 +1262,33 @@ void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer) u8 type; if (scsi_device_protection(sdp) == 0 || (buffer[12] & 1) == 0) - type = 0; - else - type = ((buffer[12] >> 1) & 7) + 1; /* P_TYPE 0 = Type 1 */ + return; + + type = ((buffer[12] >> 1) & 7) + 1; /* P_TYPE 0 = Type 1 */ + + if (type == sdkp->protection_type || !sdkp->first_scan) + return; sdkp->protection_type = type; switch (type) { - case SD_DIF_TYPE0_PROTECTION: case SD_DIF_TYPE1_PROTECTION: case SD_DIF_TYPE3_PROTECTION: break; - case SD_DIF_TYPE2_PROTECTION: - sd_printk(KERN_ERR, sdkp, "formatted with DIF Type 2 " \ - "protection which is currently unsupported. " \ - "Disabling disk!\n"); - goto disable; - default: - sd_printk(KERN_ERR, sdkp, "formatted with unknown " \ - "protection type %d. Disabling disk!\n", type); - goto disable; + sd_printk(KERN_ERR, sdkp, "formatted with unsupported " \ + "protection type %u. Disabling disk!\n", type); + sdkp->capacity = 0; + return; } - return; - -disable: - sdkp->capacity = 0; + if (scsi_host_dif_capable(sdp->host, type)) + sd_printk(KERN_NOTICE, sdkp, + "Enabling DIF Type %u protection\n", type); + else + sd_printk(KERN_NOTICE, sdkp, + "Disabling DIF Type %u protection\n", type); } static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp, diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 8474b5bad3fe..ce1f5f899fe3 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -101,16 +101,12 @@ struct sd_dif_tuple { #ifdef CONFIG_BLK_DEV_INTEGRITY -extern void sd_dif_op(struct scsi_cmnd *, unsigned int, unsigned int, unsigned int); extern void sd_dif_config_host(struct scsi_disk *); extern int sd_dif_prepare(struct request *rq, sector_t, unsigned int); extern void sd_dif_complete(struct scsi_cmnd *, unsigned int); #else /* CONFIG_BLK_DEV_INTEGRITY */ -static inline void sd_dif_op(struct scsi_cmnd *cmd, unsigned int a, unsigned int b, unsigned int c) -{ -} static inline void sd_dif_config_host(struct scsi_disk *disk) { } diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c index 84224dd21acf..88da97745710 100644 --- a/drivers/scsi/sd_dif.c +++ b/drivers/scsi/sd_dif.c @@ -320,15 +320,6 @@ void sd_dif_config_host(struct scsi_disk *sdkp) dif = 0; dix = 1; } - if (type) { - if (dif) - sd_printk(KERN_NOTICE, sdkp, - "Enabling DIF Type %d protection\n", type); - else - sd_printk(KERN_NOTICE, sdkp, - "Disabling DIF Type %d protection\n", type); - } - if (!dix) return; @@ -359,50 +350,6 @@ void sd_dif_config_host(struct scsi_disk *sdkp) } } -/* - * DIF DMA operation magic decoder ring. - */ -void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix, unsigned int type) -{ - int prot_op; - - prot_op = SCSI_PROT_NORMAL; - - BUG_ON(dif && (scmd->cmnd[0] == READ_6 || scmd->cmnd[0] == WRITE_6)); - - switch (scmd->cmnd[0]) { - case READ_6: - case READ_10: - case READ_12: - case READ_16: - if (dif && dix) - prot_op = SCSI_PROT_READ_PASS; - else if (dif && !dix) - prot_op = SCSI_PROT_READ_STRIP; - else if (!dif && dix) - prot_op = SCSI_PROT_READ_INSERT; - - break; - - case WRITE_6: - case WRITE_10: - case WRITE_12: - case WRITE_16: - if (dif && dix) - prot_op = SCSI_PROT_WRITE_PASS; - else if (dif && !dix) - prot_op = SCSI_PROT_WRITE_INSERT; - else if (!dif && dix) - prot_op = SCSI_PROT_WRITE_STRIP; - - break; - } - - scsi_set_prot_op(scmd, prot_op); - if (dif) - scsi_set_prot_type(scmd, type); -} - /* * The virtual start sector is the one that was originally submitted * by the block layer. Due to partitioning, MD/DM cloning, etc. the diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index b62a097b3ecb..6e728b176904 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -798,9 +798,15 @@ static inline unsigned int scsi_host_get_prot(struct Scsi_Host *shost) static inline unsigned int scsi_host_dif_capable(struct Scsi_Host *shost, unsigned int target_type) { switch (target_type) { - case 1: return shost->prot_capabilities & SHOST_DIF_TYPE1_PROTECTION; - case 2: return shost->prot_capabilities & SHOST_DIF_TYPE2_PROTECTION; - case 3: return shost->prot_capabilities & SHOST_DIF_TYPE3_PROTECTION; + case 1: + if (shost->prot_capabilities & SHOST_DIF_TYPE1_PROTECTION) + return target_type; + case 2: + if (shost->prot_capabilities & SHOST_DIF_TYPE2_PROTECTION) + return target_type; + case 3: + if (shost->prot_capabilities & SHOST_DIF_TYPE3_PROTECTION) + return target_type; } return 0; @@ -808,13 +814,14 @@ static inline unsigned int scsi_host_dif_capable(struct Scsi_Host *shost, unsign static inline unsigned int scsi_host_dix_capable(struct Scsi_Host *shost, unsigned int target_type) { +#if defined(CONFIG_BLK_DEV_INTEGRITY) switch (target_type) { case 0: return shost->prot_capabilities & SHOST_DIX_TYPE0_PROTECTION; case 1: return shost->prot_capabilities & SHOST_DIX_TYPE1_PROTECTION; case 2: return shost->prot_capabilities & SHOST_DIX_TYPE2_PROTECTION; case 3: return shost->prot_capabilities & SHOST_DIX_TYPE3_PROTECTION; } - +#endif return 0; } -- cgit v1.2.3 From b4c2554d40ceac130a8d062eaa8838ed22158c45 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Fri, 18 Sep 2009 17:33:01 -0400 Subject: [SCSI] Fix protection scsi_data_buffer leak We would leak a scsi_data_buffer if the free_list command was of the protected variety. Reported-by: Boaz Harrosh Signed-off-by: Martin K. Petersen Cc: Stable Tree Signed-off-by: James Bottomley --- drivers/scsi/scsi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index b6e03074cb8f..dd098cad337b 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -241,10 +241,7 @@ scsi_host_alloc_command(struct Scsi_Host *shost, gfp_t gfp_mask) */ struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) { - struct scsi_cmnd *cmd; - unsigned char *buf; - - cmd = scsi_host_alloc_command(shost, gfp_mask); + struct scsi_cmnd *cmd = scsi_host_alloc_command(shost, gfp_mask); if (unlikely(!cmd)) { unsigned long flags; @@ -258,9 +255,15 @@ struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask) spin_unlock_irqrestore(&shost->free_list_lock, flags); if (cmd) { + void *buf, *prot; + buf = cmd->sense_buffer; + prot = cmd->prot_sdb; + memset(cmd, 0, sizeof(*cmd)); + cmd->sense_buffer = buf; + cmd->prot_sdb = prot; } } -- cgit v1.2.3 From 4e7392ec582cf06753b0969ca9ab959923e38493 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Sun, 20 Sep 2009 16:49:38 -0400 Subject: [SCSI] sd: Support disks formatted with DIF Type 2 Disks formatted with DIF Type 2 reject READ/WRITE 6/10/12/16 commands when protection is enabled. Only the 32-byte variants are supported. Implement support for issusing 32-byte READ/WRITE and enable Type 2 drives in the protection type detection logic. Signed-off-by: Martin K. Petersen Signed-off-by: James Bottomley --- drivers/scsi/sd.c | 81 +++++++++++++++++++++++++++++++++++++++++++++-------- drivers/scsi/sd.h | 5 ++++ include/scsi/scsi.h | 3 ++ 3 files changed, 78 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 1e0a0b07dab6..9093c7261f33 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -116,6 +116,9 @@ static DEFINE_IDA(sd_index_ida); * object after last put) */ static DEFINE_MUTEX(sd_ref_mutex); +struct kmem_cache *sd_cdb_cache; +mempool_t *sd_cdb_pool; + static const char *sd_cache_types[] = { "write through", "none", "write back", "write back, no read (daft)" @@ -413,6 +416,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) sector_t threshold; unsigned int this_count = blk_rq_sectors(rq); int ret, host_dif; + unsigned char protect; if (rq->cmd_type == REQ_TYPE_BLOCK_PC) { ret = scsi_setup_blk_pc_cmnd(sdp, rq); @@ -545,13 +549,49 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) /* Set RDPROTECT/WRPROTECT if disk is formatted with DIF */ host_dif = scsi_host_dif_capable(sdp->host, sdkp->protection_type); if (host_dif) - SCpnt->cmnd[1] = 1 << 5; + protect = 1 << 5; else - SCpnt->cmnd[1] = 0; + protect = 0; + + if (host_dif == SD_DIF_TYPE2_PROTECTION) { + SCpnt->cmnd = mempool_alloc(sd_cdb_pool, GFP_ATOMIC); + + if (unlikely(SCpnt->cmnd == NULL)) { + ret = BLKPREP_DEFER; + goto out; + } - if (block > 0xffffffff) { + SCpnt->cmd_len = SD_EXT_CDB_SIZE; + memset(SCpnt->cmnd, 0, SCpnt->cmd_len); + SCpnt->cmnd[0] = VARIABLE_LENGTH_CMD; + SCpnt->cmnd[7] = 0x18; + SCpnt->cmnd[9] = (rq_data_dir(rq) == READ) ? READ_32 : WRITE_32; + SCpnt->cmnd[10] = protect | (blk_fua_rq(rq) ? 0x8 : 0); + + /* LBA */ + SCpnt->cmnd[12] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0; + SCpnt->cmnd[13] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0; + SCpnt->cmnd[14] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0; + SCpnt->cmnd[15] = sizeof(block) > 4 ? (unsigned char) (block >> 32) & 0xff : 0; + SCpnt->cmnd[16] = (unsigned char) (block >> 24) & 0xff; + SCpnt->cmnd[17] = (unsigned char) (block >> 16) & 0xff; + SCpnt->cmnd[18] = (unsigned char) (block >> 8) & 0xff; + SCpnt->cmnd[19] = (unsigned char) block & 0xff; + + /* Expected Indirect LBA */ + SCpnt->cmnd[20] = (unsigned char) (block >> 24) & 0xff; + SCpnt->cmnd[21] = (unsigned char) (block >> 16) & 0xff; + SCpnt->cmnd[22] = (unsigned char) (block >> 8) & 0xff; + SCpnt->cmnd[23] = (unsigned char) block & 0xff; + + /* Transfer length */ + SCpnt->cmnd[28] = (unsigned char) (this_count >> 24) & 0xff; + SCpnt->cmnd[29] = (unsigned char) (this_count >> 16) & 0xff; + SCpnt->cmnd[30] = (unsigned char) (this_count >> 8) & 0xff; + SCpnt->cmnd[31] = (unsigned char) this_count & 0xff; + } else if (block > 0xffffffff) { SCpnt->cmnd[0] += READ_16 - READ_6; - SCpnt->cmnd[1] |= blk_fua_rq(rq) ? 0x8 : 0; + SCpnt->cmnd[1] = protect | (blk_fua_rq(rq) ? 0x8 : 0); SCpnt->cmnd[2] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0; SCpnt->cmnd[3] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0; SCpnt->cmnd[4] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0; @@ -572,7 +612,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) this_count = 0xffff; SCpnt->cmnd[0] += READ_10 - READ_6; - SCpnt->cmnd[1] |= blk_fua_rq(rq) ? 0x8 : 0; + SCpnt->cmnd[1] = protect | (blk_fua_rq(rq) ? 0x8 : 0); SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff; SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff; SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff; @@ -1047,6 +1087,7 @@ static int sd_done(struct scsi_cmnd *SCpnt) int result = SCpnt->result; unsigned int good_bytes = result ? 0 : scsi_bufflen(SCpnt); struct scsi_sense_hdr sshdr; + struct scsi_disk *sdkp = scsi_disk(SCpnt->request->rq_disk); int sense_valid = 0; int sense_deferred = 0; @@ -1108,6 +1149,10 @@ static int sd_done(struct scsi_cmnd *SCpnt) if (rq_data_dir(SCpnt->request) == READ && scsi_prot_sg_count(SCpnt)) sd_dif_complete(SCpnt, good_bytes); + if (scsi_host_dif_capable(sdkp->device->host, sdkp->protection_type) + == SD_DIF_TYPE2_PROTECTION && SCpnt->cmnd != SCpnt->request->cmd) + mempool_free(SCpnt->cmnd, sd_cdb_pool); + return good_bytes; } @@ -1271,12 +1316,7 @@ void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer) sdkp->protection_type = type; - switch (type) { - case SD_DIF_TYPE1_PROTECTION: - case SD_DIF_TYPE3_PROTECTION: - break; - - default: + if (type > SD_DIF_TYPE3_PROTECTION) { sd_printk(KERN_ERR, sdkp, "formatted with unsupported " \ "protection type %u. Disabling disk!\n", type); sdkp->capacity = 0; @@ -2323,8 +2363,24 @@ static int __init init_sd(void) if (err) goto err_out_class; + sd_cdb_cache = kmem_cache_create("sd_ext_cdb", SD_EXT_CDB_SIZE, + 0, 0, NULL); + if (!sd_cdb_cache) { + printk(KERN_ERR "sd: can't init extended cdb cache\n"); + goto err_out_class; + } + + sd_cdb_pool = mempool_create_slab_pool(SD_MEMPOOL_SIZE, sd_cdb_cache); + if (!sd_cdb_pool) { + printk(KERN_ERR "sd: can't init extended cdb pool\n"); + goto err_out_cache; + } + return 0; +err_out_cache: + kmem_cache_destroy(sd_cdb_cache); + err_out_class: class_unregister(&sd_disk_class); err_out: @@ -2344,6 +2400,9 @@ static void __exit exit_sd(void) SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n")); + mempool_destroy(sd_cdb_pool); + kmem_cache_destroy(sd_cdb_cache); + scsi_unregister_driver(&sd_template.gendrv); class_unregister(&sd_disk_class); diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index ce1f5f899fe3..e374804d26fb 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -37,6 +37,11 @@ */ #define SD_LAST_BUGGY_SECTORS 8 +enum { + SD_EXT_CDB_SIZE = 32, /* Extended CDB size */ + SD_MEMPOOL_SIZE = 2, /* CDB pool size */ +}; + struct scsi_disk { struct scsi_driver *driver; /* always &sd_template */ struct scsi_device *device; diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 084478e14d24..34c46ab5c31b 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -129,6 +129,9 @@ struct scsi_cmnd; #define MI_REPORT_TARGET_PGS 0x0a /* values for maintenance out */ #define MO_SET_TARGET_PGS 0x0a +/* values for variable length command */ +#define READ_32 0x09 +#define WRITE_32 0x0b /* Values for T10/04-262r7 */ #define ATA_16 0x85 /* 16-byte pass-thru */ -- cgit v1.2.3 From 395cef030c99349d238563095adc63ea72641192 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Fri, 18 Sep 2009 17:33:03 -0400 Subject: [SCSI] scsi_debug: Implement support for DIF Type 2 Add support for 32-byte READ/WRITE as well as DIF Type 2 protection. Reject protected 10/12/16 byte READ/WRITE commands when Type 2 is enabled. Verify Type 2 reference tag according to Expected Initial LBA in 32-byte CDB. Signed-off-by: Martin K. Petersen Acked-by: Douglas Gilbert Signed-off-by: James Bottomley --- drivers/scsi/scsi_debug.c | 139 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 116 insertions(+), 23 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index fb9af207d61d..c4103bef41b5 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -50,6 +50,7 @@ #include #include #include +#include #include "sd.h" #include "scsi_logging.h" @@ -64,6 +65,7 @@ static const char * scsi_debug_version_date = "20070104"; #define PARAMETER_LIST_LENGTH_ERR 0x1a #define INVALID_OPCODE 0x20 #define ADDR_OUT_OF_RANGE 0x21 +#define INVALID_COMMAND_OPCODE 0x20 #define INVALID_FIELD_IN_CDB 0x24 #define INVALID_FIELD_IN_PARAM_LIST 0x26 #define POWERON_RESET 0x29 @@ -180,7 +182,7 @@ static int sdebug_sectors_per; /* sectors per cylinder */ #define SDEBUG_SENSE_LEN 32 #define SCSI_DEBUG_CANQUEUE 255 -#define SCSI_DEBUG_MAX_CMD_LEN 16 +#define SCSI_DEBUG_MAX_CMD_LEN 32 struct sdebug_dev_info { struct list_head dev_list; @@ -296,9 +298,25 @@ static void mk_sense_buffer(struct sdebug_dev_info *devip, int key, } static void get_data_transfer_info(unsigned char *cmd, - unsigned long long *lba, unsigned int *num) + unsigned long long *lba, unsigned int *num, + u32 *ei_lba) { + *ei_lba = 0; + switch (*cmd) { + case VARIABLE_LENGTH_CMD: + *lba = (u64)cmd[19] | (u64)cmd[18] << 8 | + (u64)cmd[17] << 16 | (u64)cmd[16] << 24 | + (u64)cmd[15] << 32 | (u64)cmd[14] << 40 | + (u64)cmd[13] << 48 | (u64)cmd[12] << 56; + + *ei_lba = (u32)cmd[23] | (u32)cmd[22] << 8 | + (u32)cmd[21] << 16 | (u32)cmd[20] << 24; + + *num = (u32)cmd[31] | (u32)cmd[30] << 8 | (u32)cmd[29] << 16 | + (u32)cmd[28] << 24; + break; + case WRITE_16: case READ_16: *lba = (u64)cmd[9] | (u64)cmd[8] << 8 | @@ -1589,7 +1607,7 @@ static int do_device_access(struct scsi_cmnd *scmd, } static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, - unsigned int sectors) + unsigned int sectors, u32 ei_lba) { unsigned int i, resid; struct scatterlist *psgl; @@ -1636,13 +1654,23 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, return 0x01; } - if (scsi_debug_dif != SD_DIF_TYPE3_PROTECTION && + if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION && be32_to_cpu(sdt[i].ref_tag) != (sector & 0xffffffff)) { printk(KERN_ERR "%s: REF check failed on sector %lu\n", __func__, (unsigned long)sector); dif_errors++; return 0x03; } + + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + be32_to_cpu(sdt[i].ref_tag) != ei_lba) { + printk(KERN_ERR "%s: REF check failed on sector %lu\n", + __func__, (unsigned long)sector); + dif_errors++; + return 0x03; + } + + ei_lba++; } resid = sectors * 8; /* Bytes of protection data to copy into sgl */ @@ -1670,7 +1698,8 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, } static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba, - unsigned int num, struct sdebug_dev_info *devip) + unsigned int num, struct sdebug_dev_info *devip, + u32 ei_lba) { unsigned long iflags; int ret; @@ -1699,7 +1728,7 @@ static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba, /* DIX + T10 DIF */ if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) { - int prot_ret = prot_verify_read(SCpnt, lba, num); + int prot_ret = prot_verify_read(SCpnt, lba, num, ei_lba); if (prot_ret) { mk_sense_buffer(devip, ABORTED_COMMAND, 0x10, prot_ret); @@ -1735,7 +1764,7 @@ void dump_sector(unsigned char *buf, int len) } static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, - unsigned int sectors) + unsigned int sectors, u32 ei_lba) { int i, j, ret; struct sd_dif_tuple *sdt; @@ -1749,11 +1778,6 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, sector = do_div(tmp_sec, sdebug_store_sectors); - if (((SCpnt->cmnd[1] >> 5) & 7) != 1) { - printk(KERN_WARNING "scsi_debug: WRPROTECT != 1\n"); - return 0; - } - BUG_ON(scsi_sg_count(SCpnt) == 0); BUG_ON(scsi_prot_sg_count(SCpnt) == 0); @@ -1808,7 +1832,7 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, goto out; } - if (scsi_debug_dif != SD_DIF_TYPE3_PROTECTION && + if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION && be32_to_cpu(sdt->ref_tag) != (start_sec & 0xffffffff)) { printk(KERN_ERR @@ -1819,6 +1843,16 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, goto out; } + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + be32_to_cpu(sdt->ref_tag) != ei_lba) { + printk(KERN_ERR + "%s: REF check failed on sector %lu\n", + __func__, (unsigned long)sector); + ret = 0x03; + dump_sector(daddr, scsi_debug_sector_size); + goto out; + } + /* Would be great to copy this in bigger * chunks. However, for the sake of * correctness we need to verify each sector @@ -1832,6 +1866,7 @@ static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, sector = 0; /* Force wrap */ start_sec++; + ei_lba++; daddr += scsi_debug_sector_size; ppage_offset += sizeof(struct sd_dif_tuple); } @@ -1853,7 +1888,8 @@ out: } static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, - unsigned int num, struct sdebug_dev_info *devip) + unsigned int num, struct sdebug_dev_info *devip, + u32 ei_lba) { unsigned long iflags; int ret; @@ -1864,7 +1900,7 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, /* DIX + T10 DIF */ if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) { - int prot_ret = prot_verify_write(SCpnt, lba, num); + int prot_ret = prot_verify_write(SCpnt, lba, num, ei_lba); if (prot_ret) { mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x10, prot_ret); @@ -2872,11 +2908,12 @@ static int __init scsi_debug_init(void) case SD_DIF_TYPE0_PROTECTION: case SD_DIF_TYPE1_PROTECTION: + case SD_DIF_TYPE2_PROTECTION: case SD_DIF_TYPE3_PROTECTION: break; default: - printk(KERN_ERR "scsi_debug_init: dif must be 0, 1 or 3\n"); + printk(KERN_ERR "scsi_debug_init: dif must be 0, 1, 2 or 3\n"); return -EINVAL; } @@ -3121,6 +3158,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done) int len, k; unsigned int num; unsigned long long lba; + u32 ei_lba; int errsts = 0; int target = SCpnt->device->id; struct sdebug_dev_info *devip = NULL; @@ -3254,14 +3292,30 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done) case READ_16: case READ_12: case READ_10: + /* READ{10,12,16} and DIF Type 2 are natural enemies */ + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + cmd[1] & 0xe0) { + mk_sense_buffer(devip, ILLEGAL_REQUEST, + INVALID_COMMAND_OPCODE, 0); + errsts = check_condition_result; + break; + } + + if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || + scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && + (cmd[1] & 0xe0) == 0) + printk(KERN_ERR "Unprotected RD/WR to DIF device\n"); + + /* fall through */ case READ_6: +read: errsts = check_readiness(SCpnt, 0, devip); if (errsts) break; if (scsi_debug_fake_rw) break; - get_data_transfer_info(cmd, &lba, &num); - errsts = resp_read(SCpnt, lba, num, devip); + get_data_transfer_info(cmd, &lba, &num, &ei_lba); + errsts = resp_read(SCpnt, lba, num, devip, ei_lba); if (inj_recovered && (0 == errsts)) { mk_sense_buffer(devip, RECOVERED_ERROR, THRESHOLD_EXCEEDED, 0); @@ -3288,14 +3342,30 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done) case WRITE_16: case WRITE_12: case WRITE_10: + /* WRITE{10,12,16} and DIF Type 2 are natural enemies */ + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && + cmd[1] & 0xe0) { + mk_sense_buffer(devip, ILLEGAL_REQUEST, + INVALID_COMMAND_OPCODE, 0); + errsts = check_condition_result; + break; + } + + if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || + scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && + (cmd[1] & 0xe0) == 0) + printk(KERN_ERR "Unprotected RD/WR to DIF device\n"); + + /* fall through */ case WRITE_6: +write: errsts = check_readiness(SCpnt, 0, devip); if (errsts) break; if (scsi_debug_fake_rw) break; - get_data_transfer_info(cmd, &lba, &num); - errsts = resp_write(SCpnt, lba, num, devip); + get_data_transfer_info(cmd, &lba, &num, &ei_lba); + errsts = resp_write(SCpnt, lba, num, devip, ei_lba); if (inj_recovered && (0 == errsts)) { mk_sense_buffer(devip, RECOVERED_ERROR, THRESHOLD_EXCEEDED, 0); @@ -3341,15 +3411,38 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done) break; if (scsi_debug_fake_rw) break; - get_data_transfer_info(cmd, &lba, &num); - errsts = resp_read(SCpnt, lba, num, devip); + get_data_transfer_info(cmd, &lba, &num, &ei_lba); + errsts = resp_read(SCpnt, lba, num, devip, ei_lba); if (errsts) break; - errsts = resp_write(SCpnt, lba, num, devip); + errsts = resp_write(SCpnt, lba, num, devip, ei_lba); if (errsts) break; errsts = resp_xdwriteread(SCpnt, lba, num, devip); break; + case VARIABLE_LENGTH_CMD: + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION) { + + if ((cmd[10] & 0xe0) == 0) + printk(KERN_ERR + "Unprotected RD/WR to DIF device\n"); + + if (cmd[9] == READ_32) { + BUG_ON(SCpnt->cmd_len < 32); + goto read; + } + + if (cmd[9] == WRITE_32) { + BUG_ON(SCpnt->cmd_len < 32); + goto write; + } + } + + mk_sense_buffer(devip, ILLEGAL_REQUEST, + INVALID_FIELD_IN_CDB, 0); + errsts = check_condition_result; + break; + default: if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: Opcode: 0x%x not " -- cgit v1.2.3 From 5415907af1f5ef80c95147bacbd321b0d4236dd5 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 18 Jun 2009 19:39:57 -0700 Subject: [SCSI] scsi_transport_fc: fix missing kernel-doc Add missing kernel-doc notation in scsi_transport_fc.c: Warning(drivers/scsi/scsi_transport_fc.c:3593): No description found for parameter 'q' Warning(drivers/scsi/scsi_transport_fc.c:3700): No description found for parameter 'q' Signed-off-by: Randy Dunlap Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_fc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index b98885de6876..a67fed10598a 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -3586,6 +3586,7 @@ enum fc_dispatch_result { /** * fc_bsg_host_dispatch - process fc host bsg requests and dispatch to LLDD + * @q: fc host request queue * @shost: scsi host rport attached to * @job: bsg job to be processed */ @@ -3693,6 +3694,7 @@ fc_bsg_goose_queue(struct fc_rport *rport) /** * fc_bsg_rport_dispatch - process rport bsg requests and dispatch to LLDD + * @q: rport request queue * @shost: scsi host rport attached to * @rport: rport request destined to * @job: bsg job to be processed -- cgit v1.2.3 From 7725ccfda59715ecf8f99e3b520a0b84cc2ea79e Mon Sep 17 00:00:00 2001 From: Jing Huang Date: Wed, 23 Sep 2009 17:46:15 -0700 Subject: [SCSI] bfa: Brocade BFA FC SCSI driver Add new driver for Brocade Hardware Signed-off-by: Jing Huang Signed-off-by: James Bottomley --- MAINTAINERS | 7 + drivers/scsi/Kconfig | 10 + drivers/scsi/Makefile | 1 + drivers/scsi/bfa/Makefile | 15 + drivers/scsi/bfa/bfa_callback_priv.h | 57 + drivers/scsi/bfa/bfa_cb_ioim_macros.h | 205 ++ drivers/scsi/bfa/bfa_cee.c | 492 ++++ drivers/scsi/bfa/bfa_core.c | 402 +++ drivers/scsi/bfa/bfa_csdebug.c | 58 + drivers/scsi/bfa/bfa_fcpim.c | 175 ++ drivers/scsi/bfa/bfa_fcpim_priv.h | 188 ++ drivers/scsi/bfa/bfa_fcport.c | 1671 +++++++++++++ drivers/scsi/bfa/bfa_fcs.c | 182 ++ drivers/scsi/bfa/bfa_fcs_lport.c | 940 +++++++ drivers/scsi/bfa/bfa_fcs_port.c | 68 + drivers/scsi/bfa/bfa_fcs_uf.c | 105 + drivers/scsi/bfa/bfa_fcxp.c | 782 ++++++ drivers/scsi/bfa/bfa_fcxp_priv.h | 138 ++ drivers/scsi/bfa/bfa_fwimg_priv.h | 31 + drivers/scsi/bfa/bfa_hw_cb.c | 142 ++ drivers/scsi/bfa/bfa_hw_ct.c | 162 ++ drivers/scsi/bfa/bfa_intr.c | 218 ++ drivers/scsi/bfa/bfa_intr_priv.h | 115 + drivers/scsi/bfa/bfa_ioc.c | 2382 ++++++++++++++++++ drivers/scsi/bfa/bfa_ioc.h | 259 ++ drivers/scsi/bfa/bfa_iocfc.c | 872 +++++++ drivers/scsi/bfa/bfa_iocfc.h | 168 ++ drivers/scsi/bfa/bfa_iocfc_q.c | 44 + drivers/scsi/bfa/bfa_ioim.c | 1311 ++++++++++ drivers/scsi/bfa/bfa_itnim.c | 1088 ++++++++ drivers/scsi/bfa/bfa_log.c | 346 +++ drivers/scsi/bfa/bfa_log_module.c | 451 ++++ drivers/scsi/bfa/bfa_lps.c | 782 ++++++ drivers/scsi/bfa/bfa_lps_priv.h | 38 + drivers/scsi/bfa/bfa_module.c | 90 + drivers/scsi/bfa/bfa_modules_priv.h | 43 + drivers/scsi/bfa/bfa_os_inc.h | 222 ++ drivers/scsi/bfa/bfa_port.c | 460 ++++ drivers/scsi/bfa/bfa_port_priv.h | 90 + drivers/scsi/bfa/bfa_priv.h | 113 + drivers/scsi/bfa/bfa_rport.c | 911 +++++++ drivers/scsi/bfa/bfa_rport_priv.h | 45 + drivers/scsi/bfa/bfa_sgpg.c | 231 ++ drivers/scsi/bfa/bfa_sgpg_priv.h | 79 + drivers/scsi/bfa/bfa_sm.c | 38 + drivers/scsi/bfa/bfa_timer.c | 90 + drivers/scsi/bfa/bfa_trcmod_priv.h | 66 + drivers/scsi/bfa/bfa_tskim.c | 689 ++++++ drivers/scsi/bfa/bfa_uf.c | 345 +++ drivers/scsi/bfa/bfa_uf_priv.h | 47 + drivers/scsi/bfa/bfad.c | 1182 +++++++++ drivers/scsi/bfa/bfad_attr.c | 649 +++++ drivers/scsi/bfa/bfad_attr.h | 65 + drivers/scsi/bfa/bfad_drv.h | 295 +++ drivers/scsi/bfa/bfad_fwimg.c | 95 + drivers/scsi/bfa/bfad_im.c | 1230 +++++++++ drivers/scsi/bfa/bfad_im.h | 150 ++ drivers/scsi/bfa/bfad_im_compat.h | 46 + drivers/scsi/bfa/bfad_intr.c | 214 ++ drivers/scsi/bfa/bfad_ipfc.h | 42 + drivers/scsi/bfa/bfad_os.c | 50 + drivers/scsi/bfa/bfad_tm.h | 59 + drivers/scsi/bfa/bfad_trcmod.h | 52 + drivers/scsi/bfa/fab.c | 62 + drivers/scsi/bfa/fabric.c | 1278 ++++++++++ drivers/scsi/bfa/fcbuild.c | 1449 +++++++++++ drivers/scsi/bfa/fcbuild.h | 273 ++ drivers/scsi/bfa/fcpim.c | 844 +++++++ drivers/scsi/bfa/fcptm.c | 68 + drivers/scsi/bfa/fcs.h | 30 + drivers/scsi/bfa/fcs_auth.h | 37 + drivers/scsi/bfa/fcs_fabric.h | 61 + drivers/scsi/bfa/fcs_fcpim.h | 44 + drivers/scsi/bfa/fcs_fcptm.h | 45 + drivers/scsi/bfa/fcs_fcxp.h | 29 + drivers/scsi/bfa/fcs_lport.h | 117 + drivers/scsi/bfa/fcs_ms.h | 35 + drivers/scsi/bfa/fcs_port.h | 32 + drivers/scsi/bfa/fcs_rport.h | 61 + drivers/scsi/bfa/fcs_trcmod.h | 56 + drivers/scsi/bfa/fcs_uf.h | 32 + drivers/scsi/bfa/fcs_vport.h | 39 + drivers/scsi/bfa/fdmi.c | 1223 +++++++++ drivers/scsi/bfa/include/aen/bfa_aen.h | 92 + drivers/scsi/bfa/include/aen/bfa_aen_adapter.h | 31 + drivers/scsi/bfa/include/aen/bfa_aen_audit.h | 31 + drivers/scsi/bfa/include/aen/bfa_aen_ethport.h | 35 + drivers/scsi/bfa/include/aen/bfa_aen_ioc.h | 37 + drivers/scsi/bfa/include/aen/bfa_aen_itnim.h | 33 + drivers/scsi/bfa/include/aen/bfa_aen_lport.h | 51 + drivers/scsi/bfa/include/aen/bfa_aen_port.h | 57 + drivers/scsi/bfa/include/aen/bfa_aen_rport.h | 37 + drivers/scsi/bfa/include/bfa.h | 177 ++ drivers/scsi/bfa/include/bfa_fcpim.h | 159 ++ drivers/scsi/bfa/include/bfa_fcptm.h | 47 + drivers/scsi/bfa/include/bfa_svc.h | 324 +++ drivers/scsi/bfa/include/bfa_timer.h | 53 + drivers/scsi/bfa/include/bfi/bfi.h | 174 ++ drivers/scsi/bfa/include/bfi/bfi_boot.h | 34 + drivers/scsi/bfa/include/bfi/bfi_cbreg.h | 305 +++ drivers/scsi/bfa/include/bfi/bfi_cee.h | 119 + drivers/scsi/bfa/include/bfi/bfi_ctreg.h | 611 +++++ drivers/scsi/bfa/include/bfi/bfi_fabric.h | 92 + drivers/scsi/bfa/include/bfi/bfi_fcpim.h | 301 +++ drivers/scsi/bfa/include/bfi/bfi_fcxp.h | 71 + drivers/scsi/bfa/include/bfi/bfi_ioc.h | 202 ++ drivers/scsi/bfa/include/bfi/bfi_iocfc.h | 177 ++ drivers/scsi/bfa/include/bfi/bfi_lport.h | 89 + drivers/scsi/bfa/include/bfi/bfi_lps.h | 96 + drivers/scsi/bfa/include/bfi/bfi_port.h | 115 + drivers/scsi/bfa/include/bfi/bfi_pport.h | 184 ++ drivers/scsi/bfa/include/bfi/bfi_rport.h | 104 + drivers/scsi/bfa/include/bfi/bfi_uf.h | 52 + drivers/scsi/bfa/include/cna/bfa_cna_trcmod.h | 36 + drivers/scsi/bfa/include/cna/cee/bfa_cee.h | 77 + drivers/scsi/bfa/include/cna/port/bfa_port.h | 69 + drivers/scsi/bfa/include/cna/pstats/ethport_defs.h | 36 + drivers/scsi/bfa/include/cna/pstats/phyport_defs.h | 218 ++ drivers/scsi/bfa/include/cs/bfa_checksum.h | 60 + drivers/scsi/bfa/include/cs/bfa_debug.h | 44 + drivers/scsi/bfa/include/cs/bfa_log.h | 184 ++ drivers/scsi/bfa/include/cs/bfa_perf.h | 34 + drivers/scsi/bfa/include/cs/bfa_plog.h | 162 ++ drivers/scsi/bfa/include/cs/bfa_q.h | 81 + drivers/scsi/bfa/include/cs/bfa_sm.h | 69 + drivers/scsi/bfa/include/cs/bfa_trc.h | 176 ++ drivers/scsi/bfa/include/cs/bfa_wc.h | 68 + drivers/scsi/bfa/include/defs/bfa_defs_adapter.h | 82 + drivers/scsi/bfa/include/defs/bfa_defs_aen.h | 73 + drivers/scsi/bfa/include/defs/bfa_defs_audit.h | 38 + drivers/scsi/bfa/include/defs/bfa_defs_auth.h | 112 + drivers/scsi/bfa/include/defs/bfa_defs_boot.h | 71 + drivers/scsi/bfa/include/defs/bfa_defs_cee.h | 159 ++ drivers/scsi/bfa/include/defs/bfa_defs_driver.h | 40 + drivers/scsi/bfa/include/defs/bfa_defs_ethport.h | 98 + drivers/scsi/bfa/include/defs/bfa_defs_fcpim.h | 45 + drivers/scsi/bfa/include/defs/bfa_defs_im_common.h | 32 + drivers/scsi/bfa/include/defs/bfa_defs_im_team.h | 72 + drivers/scsi/bfa/include/defs/bfa_defs_ioc.h | 152 ++ drivers/scsi/bfa/include/defs/bfa_defs_iocfc.h | 310 +++ drivers/scsi/bfa/include/defs/bfa_defs_ipfc.h | 70 + drivers/scsi/bfa/include/defs/bfa_defs_itnim.h | 126 + drivers/scsi/bfa/include/defs/bfa_defs_led.h | 35 + drivers/scsi/bfa/include/defs/bfa_defs_lport.h | 68 + drivers/scsi/bfa/include/defs/bfa_defs_mfg.h | 58 + drivers/scsi/bfa/include/defs/bfa_defs_pci.h | 41 + drivers/scsi/bfa/include/defs/bfa_defs_pm.h | 33 + drivers/scsi/bfa/include/defs/bfa_defs_pom.h | 56 + drivers/scsi/bfa/include/defs/bfa_defs_port.h | 245 ++ drivers/scsi/bfa/include/defs/bfa_defs_pport.h | 383 +++ drivers/scsi/bfa/include/defs/bfa_defs_qos.h | 99 + drivers/scsi/bfa/include/defs/bfa_defs_rport.h | 199 ++ drivers/scsi/bfa/include/defs/bfa_defs_status.h | 255 ++ drivers/scsi/bfa/include/defs/bfa_defs_tin.h | 118 + drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h | 43 + drivers/scsi/bfa/include/defs/bfa_defs_types.h | 30 + drivers/scsi/bfa/include/defs/bfa_defs_version.h | 22 + drivers/scsi/bfa/include/defs/bfa_defs_vf.h | 74 + drivers/scsi/bfa/include/defs/bfa_defs_vport.h | 91 + drivers/scsi/bfa/include/fcb/bfa_fcb.h | 33 + drivers/scsi/bfa/include/fcb/bfa_fcb_fcpim.h | 76 + drivers/scsi/bfa/include/fcb/bfa_fcb_port.h | 113 + drivers/scsi/bfa/include/fcb/bfa_fcb_rport.h | 80 + drivers/scsi/bfa/include/fcb/bfa_fcb_vf.h | 47 + drivers/scsi/bfa/include/fcb/bfa_fcb_vport.h | 47 + drivers/scsi/bfa/include/fcs/bfa_fcs.h | 73 + drivers/scsi/bfa/include/fcs/bfa_fcs_auth.h | 82 + drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h | 112 + drivers/scsi/bfa/include/fcs/bfa_fcs_fcpim.h | 131 + drivers/scsi/bfa/include/fcs/bfa_fcs_fdmi.h | 63 + drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h | 226 ++ drivers/scsi/bfa/include/fcs/bfa_fcs_rport.h | 104 + drivers/scsi/bfa/include/fcs/bfa_fcs_vport.h | 63 + drivers/scsi/bfa/include/log/bfa_log_fcs.h | 28 + drivers/scsi/bfa/include/log/bfa_log_hal.h | 30 + drivers/scsi/bfa/include/log/bfa_log_linux.h | 44 + drivers/scsi/bfa/include/log/bfa_log_wdrv.h | 36 + drivers/scsi/bfa/include/protocol/ct.h | 492 ++++ drivers/scsi/bfa/include/protocol/fc.h | 1105 +++++++++ drivers/scsi/bfa/include/protocol/fc_sp.h | 224 ++ drivers/scsi/bfa/include/protocol/fcp.h | 186 ++ drivers/scsi/bfa/include/protocol/fdmi.h | 163 ++ drivers/scsi/bfa/include/protocol/pcifw.h | 75 + drivers/scsi/bfa/include/protocol/scsi.h | 1648 ++++++++++++ drivers/scsi/bfa/include/protocol/types.h | 42 + drivers/scsi/bfa/loop.c | 422 ++++ drivers/scsi/bfa/lport_api.c | 291 +++ drivers/scsi/bfa/lport_priv.h | 82 + drivers/scsi/bfa/ms.c | 759 ++++++ drivers/scsi/bfa/n2n.c | 105 + drivers/scsi/bfa/ns.c | 1243 ++++++++++ drivers/scsi/bfa/plog.c | 184 ++ drivers/scsi/bfa/rport.c | 2618 ++++++++++++++++++++ drivers/scsi/bfa/rport_api.c | 180 ++ drivers/scsi/bfa/rport_ftrs.c | 375 +++ drivers/scsi/bfa/scn.c | 482 ++++ drivers/scsi/bfa/vfapi.c | 292 +++ drivers/scsi/bfa/vport.c | 891 +++++++ 198 files changed, 49189 insertions(+) create mode 100644 drivers/scsi/bfa/Makefile create mode 100644 drivers/scsi/bfa/bfa_callback_priv.h create mode 100644 drivers/scsi/bfa/bfa_cb_ioim_macros.h create mode 100644 drivers/scsi/bfa/bfa_cee.c create mode 100644 drivers/scsi/bfa/bfa_core.c create mode 100644 drivers/scsi/bfa/bfa_csdebug.c create mode 100644 drivers/scsi/bfa/bfa_fcpim.c create mode 100644 drivers/scsi/bfa/bfa_fcpim_priv.h create mode 100644 drivers/scsi/bfa/bfa_fcport.c create mode 100644 drivers/scsi/bfa/bfa_fcs.c create mode 100644 drivers/scsi/bfa/bfa_fcs_lport.c create mode 100644 drivers/scsi/bfa/bfa_fcs_port.c create mode 100644 drivers/scsi/bfa/bfa_fcs_uf.c create mode 100644 drivers/scsi/bfa/bfa_fcxp.c create mode 100644 drivers/scsi/bfa/bfa_fcxp_priv.h create mode 100644 drivers/scsi/bfa/bfa_fwimg_priv.h create mode 100644 drivers/scsi/bfa/bfa_hw_cb.c create mode 100644 drivers/scsi/bfa/bfa_hw_ct.c create mode 100644 drivers/scsi/bfa/bfa_intr.c create mode 100644 drivers/scsi/bfa/bfa_intr_priv.h create mode 100644 drivers/scsi/bfa/bfa_ioc.c create mode 100644 drivers/scsi/bfa/bfa_ioc.h create mode 100644 drivers/scsi/bfa/bfa_iocfc.c create mode 100644 drivers/scsi/bfa/bfa_iocfc.h create mode 100644 drivers/scsi/bfa/bfa_iocfc_q.c create mode 100644 drivers/scsi/bfa/bfa_ioim.c create mode 100644 drivers/scsi/bfa/bfa_itnim.c create mode 100644 drivers/scsi/bfa/bfa_log.c create mode 100644 drivers/scsi/bfa/bfa_log_module.c create mode 100644 drivers/scsi/bfa/bfa_lps.c create mode 100644 drivers/scsi/bfa/bfa_lps_priv.h create mode 100644 drivers/scsi/bfa/bfa_module.c create mode 100644 drivers/scsi/bfa/bfa_modules_priv.h create mode 100644 drivers/scsi/bfa/bfa_os_inc.h create mode 100644 drivers/scsi/bfa/bfa_port.c create mode 100644 drivers/scsi/bfa/bfa_port_priv.h create mode 100644 drivers/scsi/bfa/bfa_priv.h create mode 100644 drivers/scsi/bfa/bfa_rport.c create mode 100644 drivers/scsi/bfa/bfa_rport_priv.h create mode 100644 drivers/scsi/bfa/bfa_sgpg.c create mode 100644 drivers/scsi/bfa/bfa_sgpg_priv.h create mode 100644 drivers/scsi/bfa/bfa_sm.c create mode 100644 drivers/scsi/bfa/bfa_timer.c create mode 100644 drivers/scsi/bfa/bfa_trcmod_priv.h create mode 100644 drivers/scsi/bfa/bfa_tskim.c create mode 100644 drivers/scsi/bfa/bfa_uf.c create mode 100644 drivers/scsi/bfa/bfa_uf_priv.h create mode 100644 drivers/scsi/bfa/bfad.c create mode 100644 drivers/scsi/bfa/bfad_attr.c create mode 100644 drivers/scsi/bfa/bfad_attr.h create mode 100644 drivers/scsi/bfa/bfad_drv.h create mode 100644 drivers/scsi/bfa/bfad_fwimg.c create mode 100644 drivers/scsi/bfa/bfad_im.c create mode 100644 drivers/scsi/bfa/bfad_im.h create mode 100644 drivers/scsi/bfa/bfad_im_compat.h create mode 100644 drivers/scsi/bfa/bfad_intr.c create mode 100644 drivers/scsi/bfa/bfad_ipfc.h create mode 100644 drivers/scsi/bfa/bfad_os.c create mode 100644 drivers/scsi/bfa/bfad_tm.h create mode 100644 drivers/scsi/bfa/bfad_trcmod.h create mode 100644 drivers/scsi/bfa/fab.c create mode 100644 drivers/scsi/bfa/fabric.c create mode 100644 drivers/scsi/bfa/fcbuild.c create mode 100644 drivers/scsi/bfa/fcbuild.h create mode 100644 drivers/scsi/bfa/fcpim.c create mode 100644 drivers/scsi/bfa/fcptm.c create mode 100644 drivers/scsi/bfa/fcs.h create mode 100644 drivers/scsi/bfa/fcs_auth.h create mode 100644 drivers/scsi/bfa/fcs_fabric.h create mode 100644 drivers/scsi/bfa/fcs_fcpim.h create mode 100644 drivers/scsi/bfa/fcs_fcptm.h create mode 100644 drivers/scsi/bfa/fcs_fcxp.h create mode 100644 drivers/scsi/bfa/fcs_lport.h create mode 100644 drivers/scsi/bfa/fcs_ms.h create mode 100644 drivers/scsi/bfa/fcs_port.h create mode 100644 drivers/scsi/bfa/fcs_rport.h create mode 100644 drivers/scsi/bfa/fcs_trcmod.h create mode 100644 drivers/scsi/bfa/fcs_uf.h create mode 100644 drivers/scsi/bfa/fcs_vport.h create mode 100644 drivers/scsi/bfa/fdmi.c create mode 100644 drivers/scsi/bfa/include/aen/bfa_aen.h create mode 100644 drivers/scsi/bfa/include/aen/bfa_aen_adapter.h create mode 100644 drivers/scsi/bfa/include/aen/bfa_aen_audit.h create mode 100644 drivers/scsi/bfa/include/aen/bfa_aen_ethport.h create mode 100644 drivers/scsi/bfa/include/aen/bfa_aen_ioc.h create mode 100644 drivers/scsi/bfa/include/aen/bfa_aen_itnim.h create mode 100644 drivers/scsi/bfa/include/aen/bfa_aen_lport.h create mode 100644 drivers/scsi/bfa/include/aen/bfa_aen_port.h create mode 100644 drivers/scsi/bfa/include/aen/bfa_aen_rport.h create mode 100644 drivers/scsi/bfa/include/bfa.h create mode 100644 drivers/scsi/bfa/include/bfa_fcpim.h create mode 100644 drivers/scsi/bfa/include/bfa_fcptm.h create mode 100644 drivers/scsi/bfa/include/bfa_svc.h create mode 100644 drivers/scsi/bfa/include/bfa_timer.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_boot.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_cbreg.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_cee.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_ctreg.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_fabric.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_fcpim.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_fcxp.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_ioc.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_iocfc.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_lport.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_lps.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_port.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_pport.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_rport.h create mode 100644 drivers/scsi/bfa/include/bfi/bfi_uf.h create mode 100644 drivers/scsi/bfa/include/cna/bfa_cna_trcmod.h create mode 100644 drivers/scsi/bfa/include/cna/cee/bfa_cee.h create mode 100644 drivers/scsi/bfa/include/cna/port/bfa_port.h create mode 100644 drivers/scsi/bfa/include/cna/pstats/ethport_defs.h create mode 100644 drivers/scsi/bfa/include/cna/pstats/phyport_defs.h create mode 100644 drivers/scsi/bfa/include/cs/bfa_checksum.h create mode 100644 drivers/scsi/bfa/include/cs/bfa_debug.h create mode 100644 drivers/scsi/bfa/include/cs/bfa_log.h create mode 100644 drivers/scsi/bfa/include/cs/bfa_perf.h create mode 100644 drivers/scsi/bfa/include/cs/bfa_plog.h create mode 100644 drivers/scsi/bfa/include/cs/bfa_q.h create mode 100644 drivers/scsi/bfa/include/cs/bfa_sm.h create mode 100644 drivers/scsi/bfa/include/cs/bfa_trc.h create mode 100644 drivers/scsi/bfa/include/cs/bfa_wc.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_adapter.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_aen.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_audit.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_auth.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_boot.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_cee.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_driver.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_ethport.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_fcpim.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_im_common.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_im_team.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_ioc.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_iocfc.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_ipfc.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_itnim.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_led.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_lport.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_mfg.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_pci.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_pm.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_pom.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_port.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_pport.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_qos.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_rport.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_status.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_tin.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_types.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_version.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_vf.h create mode 100644 drivers/scsi/bfa/include/defs/bfa_defs_vport.h create mode 100644 drivers/scsi/bfa/include/fcb/bfa_fcb.h create mode 100644 drivers/scsi/bfa/include/fcb/bfa_fcb_fcpim.h create mode 100644 drivers/scsi/bfa/include/fcb/bfa_fcb_port.h create mode 100644 drivers/scsi/bfa/include/fcb/bfa_fcb_rport.h create mode 100644 drivers/scsi/bfa/include/fcb/bfa_fcb_vf.h create mode 100644 drivers/scsi/bfa/include/fcb/bfa_fcb_vport.h create mode 100644 drivers/scsi/bfa/include/fcs/bfa_fcs.h create mode 100644 drivers/scsi/bfa/include/fcs/bfa_fcs_auth.h create mode 100644 drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h create mode 100644 drivers/scsi/bfa/include/fcs/bfa_fcs_fcpim.h create mode 100644 drivers/scsi/bfa/include/fcs/bfa_fcs_fdmi.h create mode 100644 drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h create mode 100644 drivers/scsi/bfa/include/fcs/bfa_fcs_rport.h create mode 100644 drivers/scsi/bfa/include/fcs/bfa_fcs_vport.h create mode 100644 drivers/scsi/bfa/include/log/bfa_log_fcs.h create mode 100644 drivers/scsi/bfa/include/log/bfa_log_hal.h create mode 100644 drivers/scsi/bfa/include/log/bfa_log_linux.h create mode 100644 drivers/scsi/bfa/include/log/bfa_log_wdrv.h create mode 100644 drivers/scsi/bfa/include/protocol/ct.h create mode 100644 drivers/scsi/bfa/include/protocol/fc.h create mode 100644 drivers/scsi/bfa/include/protocol/fc_sp.h create mode 100644 drivers/scsi/bfa/include/protocol/fcp.h create mode 100644 drivers/scsi/bfa/include/protocol/fdmi.h create mode 100644 drivers/scsi/bfa/include/protocol/pcifw.h create mode 100644 drivers/scsi/bfa/include/protocol/scsi.h create mode 100644 drivers/scsi/bfa/include/protocol/types.h create mode 100644 drivers/scsi/bfa/loop.c create mode 100644 drivers/scsi/bfa/lport_api.c create mode 100644 drivers/scsi/bfa/lport_priv.h create mode 100644 drivers/scsi/bfa/ms.c create mode 100644 drivers/scsi/bfa/n2n.c create mode 100644 drivers/scsi/bfa/ns.c create mode 100644 drivers/scsi/bfa/plog.c create mode 100644 drivers/scsi/bfa/rport.c create mode 100644 drivers/scsi/bfa/rport_api.c create mode 100644 drivers/scsi/bfa/rport_ftrs.c create mode 100644 drivers/scsi/bfa/scn.c create mode 100644 drivers/scsi/bfa/vfapi.c create mode 100644 drivers/scsi/bfa/vport.c diff --git a/MAINTAINERS b/MAINTAINERS index c450f3abb8c9..c233f6fe0fff 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1211,6 +1211,13 @@ L: netdev@vger.kernel.org S: Supported F: drivers/net/tg3.* +BROCADE BFA FC SCSI DRIVER +P: Jing Huang +M: huangj@brocade.com +L: linux-scsi@vger.kernel.org +S: Supported +F: drivers/scsi/bfa/ + BSG (block layer generic sg v4 driver) M: FUJITA Tomonori L: linux-scsi@vger.kernel.org diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 82bb3b2d207a..a8ea01b07868 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1827,6 +1827,16 @@ config SCSI_SRP To compile this driver as a module, choose M here: the module will be called libsrp. +config SCSI_BFA_FC + tristate "Brocade BFA Fibre Channel Support" + depends on PCI && SCSI + select SCSI_FC_ATTRS + help + This bfa driver supports all Brocade PCIe FC/FCOE host adapters. + + To compile this driver as a module, choose M here. The module will + be called bfa. + endif # SCSI_LOWLEVEL source "drivers/scsi/pcmcia/Kconfig" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 61a94af3cee7..316f0a1a7986 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx/ obj-$(CONFIG_SCSI_QLA_ISCSI) += qla4xxx/ obj-$(CONFIG_SCSI_LPFC) += lpfc/ +obj-$(CONFIG_SCSI_BFA_FC) += bfa/ obj-$(CONFIG_SCSI_PAS16) += pas16.o obj-$(CONFIG_SCSI_T128) += t128.o obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o diff --git a/drivers/scsi/bfa/Makefile b/drivers/scsi/bfa/Makefile new file mode 100644 index 000000000000..1d6009490d1c --- /dev/null +++ b/drivers/scsi/bfa/Makefile @@ -0,0 +1,15 @@ +obj-$(CONFIG_SCSI_BFA_FC) := bfa.o + +bfa-y := bfad.o bfad_intr.o bfad_os.o bfad_im.o bfad_attr.o bfad_fwimg.o + +bfa-y += bfa_core.o bfa_ioc.o bfa_iocfc.o bfa_fcxp.o bfa_lps.o +bfa-y += bfa_hw_cb.o bfa_hw_ct.o bfa_intr.o bfa_timer.o bfa_rport.o +bfa-y += bfa_fcport.o bfa_port.o bfa_uf.o bfa_sgpg.o bfa_module.o bfa_ioim.o +bfa-y += bfa_itnim.o bfa_fcpim.o bfa_tskim.o bfa_log.o bfa_log_module.o +bfa-y += bfa_csdebug.o bfa_sm.o plog.o + +bfa-y += fcbuild.o fabric.o fcpim.o vfapi.o fcptm.o bfa_fcs.o bfa_fcs_port.o +bfa-y += bfa_fcs_uf.o bfa_fcs_lport.o fab.o fdmi.o ms.o ns.o scn.o loop.o +bfa-y += lport_api.o n2n.o rport.o rport_api.o rport_ftrs.o vport.o + +ccflags-y := -I$(obj) -I$(obj)/include -I$(obj)/include/cna diff --git a/drivers/scsi/bfa/bfa_callback_priv.h b/drivers/scsi/bfa/bfa_callback_priv.h new file mode 100644 index 000000000000..1e3265c9f7d4 --- /dev/null +++ b/drivers/scsi/bfa/bfa_callback_priv.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef __BFA_CALLBACK_PRIV_H__ +#define __BFA_CALLBACK_PRIV_H__ + +#include + +typedef void (*bfa_cb_cbfn_t) (void *cbarg, bfa_boolean_t complete); + +/** + * Generic BFA callback element. + */ +struct bfa_cb_qe_s { + struct list_head qe; + bfa_cb_cbfn_t cbfn; + bfa_boolean_t once; + u32 rsvd; + void *cbarg; +}; + +#define bfa_cb_queue(__bfa, __hcb_qe, __cbfn, __cbarg) do { \ + (__hcb_qe)->cbfn = (__cbfn); \ + (__hcb_qe)->cbarg = (__cbarg); \ + list_add_tail(&(__hcb_qe)->qe, &(__bfa)->comp_q); \ +} while (0) + +#define bfa_cb_dequeue(__hcb_qe) list_del(&(__hcb_qe)->qe) + +#define bfa_cb_queue_once(__bfa, __hcb_qe, __cbfn, __cbarg) do { \ + (__hcb_qe)->cbfn = (__cbfn); \ + (__hcb_qe)->cbarg = (__cbarg); \ + if (!(__hcb_qe)->once) { \ + list_add_tail((__hcb_qe), &(__bfa)->comp_q); \ + (__hcb_qe)->once = BFA_TRUE; \ + } \ +} while (0) + +#define bfa_cb_queue_done(__hcb_qe) do { \ + (__hcb_qe)->once = BFA_FALSE; \ +} while (0) + +#endif /* __BFA_CALLBACK_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_cb_ioim_macros.h b/drivers/scsi/bfa/bfa_cb_ioim_macros.h new file mode 100644 index 000000000000..0050c838c358 --- /dev/null +++ b/drivers/scsi/bfa/bfa_cb_ioim_macros.h @@ -0,0 +1,205 @@ +/* + * 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. + */ + +/** + * bfa_cb_ioim_macros.h BFA IOIM driver interface macros. + */ + +#ifndef __BFA_HCB_IOIM_MACROS_H__ +#define __BFA_HCB_IOIM_MACROS_H__ + +#include +/* + * #include + * + * #include #include #include + * #include + */ +#include "bfad_im_compat.h" + +/* + * task attribute values in FCP-2 FCP_CMND IU + */ +#define SIMPLE_Q 0 +#define HEAD_OF_Q 1 +#define ORDERED_Q 2 +#define ACA_Q 4 +#define UNTAGGED 5 + +static inline lun_t +bfad_int_to_lun(u32 luno) +{ + union { + u16 scsi_lun[4]; + lun_t bfa_lun; + } lun; + + lun.bfa_lun = 0; + lun.scsi_lun[0] = bfa_os_htons(luno); + + return (lun.bfa_lun); +} + +/** + * Get LUN for the I/O request + */ +#define bfa_cb_ioim_get_lun(__dio) \ + bfad_int_to_lun(((struct scsi_cmnd *)__dio)->device->lun) + +/** + * Get CDB for the I/O request + */ +static inline u8 * +bfa_cb_ioim_get_cdb(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + + return ((u8 *) cmnd->cmnd); +} + +/** + * Get I/O direction (read/write) for the I/O request + */ +static inline enum fcp_iodir +bfa_cb_ioim_get_iodir(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + enum dma_data_direction dmadir; + + dmadir = cmnd->sc_data_direction; + if (dmadir == DMA_TO_DEVICE) + return FCP_IODIR_WRITE; + else if (dmadir == DMA_FROM_DEVICE) + return FCP_IODIR_READ; + else + return FCP_IODIR_NONE; +} + +/** + * Get IO size in bytes for the I/O request + */ +static inline u32 +bfa_cb_ioim_get_size(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + + return (scsi_bufflen(cmnd)); +} + +/** + * Get timeout for the I/O request + */ +static inline u8 +bfa_cb_ioim_get_timeout(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + /* + * TBD: need a timeout for scsi passthru + */ + if (cmnd->device->host == NULL) + return 4; + + return 0; +} + +/** + * Get SG element for the I/O request given the SG element index + */ +static inline union bfi_addr_u +bfa_cb_ioim_get_sgaddr(struct bfad_ioim_s *dio, int sgeid) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + struct scatterlist *sge; + u64 addr; + + sge = (struct scatterlist *)scsi_sglist(cmnd) + sgeid; + addr = (u64) sg_dma_address(sge); + + return (*(union bfi_addr_u *) &addr); +} + +static inline u32 +bfa_cb_ioim_get_sglen(struct bfad_ioim_s *dio, int sgeid) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + struct scatterlist *sge; + u32 len; + + sge = (struct scatterlist *)scsi_sglist(cmnd) + sgeid; + len = sg_dma_len(sge); + + return len; +} + +/** + * Get Command Reference Number for the I/O request. 0 if none. + */ +static inline u8 +bfa_cb_ioim_get_crn(struct bfad_ioim_s *dio) +{ + return 0; +} + +/** + * Get SAM-3 priority for the I/O request. 0 is default. + */ +static inline u8 +bfa_cb_ioim_get_priority(struct bfad_ioim_s *dio) +{ + return 0; +} + +/** + * Get task attributes for the I/O request. Default is FCP_TASK_ATTR_SIMPLE(0). + */ +static inline u8 +bfa_cb_ioim_get_taskattr(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + u8 task_attr = UNTAGGED; + + if (cmnd->device->tagged_supported) { + switch (cmnd->tag) { + case HEAD_OF_QUEUE_TAG: + task_attr = HEAD_OF_Q; + break; + case ORDERED_QUEUE_TAG: + task_attr = ORDERED_Q; + break; + default: + task_attr = SIMPLE_Q; + break; + } + } + + return task_attr; +} + +/** + * Get CDB length in bytes for the I/O request. Default is FCP_CMND_CDB_LEN(16). + */ +static inline u8 +bfa_cb_ioim_get_cdblen(struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + + return (cmnd->cmd_len); +} + + + +#endif /* __BFA_HCB_IOIM_MACROS_H__ */ diff --git a/drivers/scsi/bfa/bfa_cee.c b/drivers/scsi/bfa/bfa_cee.c new file mode 100644 index 000000000000..7a959c34e789 --- /dev/null +++ b/drivers/scsi/bfa/bfa_cee.c @@ -0,0 +1,492 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +BFA_TRC_FILE(CNA, CEE); + +#define bfa_ioc_portid(__ioc) ((__ioc)->port_id) +#define bfa_lpuid(__arg) bfa_ioc_portid(&(__arg)->ioc) + +static void bfa_cee_format_lldp_cfg(struct bfa_cee_lldp_cfg_s *lldp_cfg); +static void bfa_cee_format_dcbcx_stats(struct bfa_cee_dcbx_stats_s + *dcbcx_stats); +static void bfa_cee_format_lldp_stats(struct bfa_cee_lldp_stats_s + *lldp_stats); +static void bfa_cee_format_cfg_stats(struct bfa_cee_cfg_stats_s *cfg_stats); +static void bfa_cee_format_cee_cfg(void *buffer); +static void bfa_cee_format_cee_stats(void *buffer); + +static void +bfa_cee_format_cee_stats(void *buffer) +{ + struct bfa_cee_stats_s *cee_stats = buffer; + bfa_cee_format_dcbcx_stats(&cee_stats->dcbx_stats); + bfa_cee_format_lldp_stats(&cee_stats->lldp_stats); + bfa_cee_format_cfg_stats(&cee_stats->cfg_stats); +} + +static void +bfa_cee_format_cee_cfg(void *buffer) +{ + struct bfa_cee_attr_s *cee_cfg = buffer; + bfa_cee_format_lldp_cfg(&cee_cfg->lldp_remote); +} + +static void +bfa_cee_format_dcbcx_stats(struct bfa_cee_dcbx_stats_s *dcbcx_stats) +{ + dcbcx_stats->subtlvs_unrecognized = + bfa_os_ntohl(dcbcx_stats->subtlvs_unrecognized); + dcbcx_stats->negotiation_failed = + bfa_os_ntohl(dcbcx_stats->negotiation_failed); + dcbcx_stats->remote_cfg_changed = + bfa_os_ntohl(dcbcx_stats->remote_cfg_changed); + dcbcx_stats->tlvs_received = bfa_os_ntohl(dcbcx_stats->tlvs_received); + dcbcx_stats->tlvs_invalid = bfa_os_ntohl(dcbcx_stats->tlvs_invalid); + dcbcx_stats->seqno = bfa_os_ntohl(dcbcx_stats->seqno); + dcbcx_stats->ackno = bfa_os_ntohl(dcbcx_stats->ackno); + dcbcx_stats->recvd_seqno = bfa_os_ntohl(dcbcx_stats->recvd_seqno); + dcbcx_stats->recvd_ackno = bfa_os_ntohl(dcbcx_stats->recvd_ackno); +} + +static void +bfa_cee_format_lldp_stats(struct bfa_cee_lldp_stats_s *lldp_stats) +{ + lldp_stats->frames_transmitted = + bfa_os_ntohl(lldp_stats->frames_transmitted); + lldp_stats->frames_aged_out = bfa_os_ntohl(lldp_stats->frames_aged_out); + lldp_stats->frames_discarded = + bfa_os_ntohl(lldp_stats->frames_discarded); + lldp_stats->frames_in_error = bfa_os_ntohl(lldp_stats->frames_in_error); + lldp_stats->frames_rcvd = bfa_os_ntohl(lldp_stats->frames_rcvd); + lldp_stats->tlvs_discarded = bfa_os_ntohl(lldp_stats->tlvs_discarded); + lldp_stats->tlvs_unrecognized = + bfa_os_ntohl(lldp_stats->tlvs_unrecognized); +} + +static void +bfa_cee_format_cfg_stats(struct bfa_cee_cfg_stats_s *cfg_stats) +{ + cfg_stats->cee_status_down = bfa_os_ntohl(cfg_stats->cee_status_down); + cfg_stats->cee_status_up = bfa_os_ntohl(cfg_stats->cee_status_up); + cfg_stats->cee_hw_cfg_changed = + bfa_os_ntohl(cfg_stats->cee_hw_cfg_changed); + cfg_stats->recvd_invalid_cfg = + bfa_os_ntohl(cfg_stats->recvd_invalid_cfg); +} + +static void +bfa_cee_format_lldp_cfg(struct bfa_cee_lldp_cfg_s *lldp_cfg) +{ + lldp_cfg->time_to_interval = bfa_os_ntohs(lldp_cfg->time_to_interval); + lldp_cfg->enabled_system_cap = + bfa_os_ntohs(lldp_cfg->enabled_system_cap); +} + +/** + * bfa_cee_attr_meminfo() + * + * + * @param[in] void + * + * @return Size of DMA region + */ +static u32 +bfa_cee_attr_meminfo(void) +{ + return BFA_ROUNDUP(sizeof(struct bfa_cee_attr_s), BFA_DMA_ALIGN_SZ); +} + +/** + * bfa_cee_stats_meminfo() + * + * + * @param[in] void + * + * @return Size of DMA region + */ +static u32 +bfa_cee_stats_meminfo(void) +{ + return BFA_ROUNDUP(sizeof(struct bfa_cee_stats_s), BFA_DMA_ALIGN_SZ); +} + +/** + * bfa_cee_get_attr_isr() + * + * + * @param[in] cee - Pointer to the CEE module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_cee_get_attr_isr(struct bfa_cee_s *cee, bfa_status_t status) +{ + cee->get_attr_status = status; + bfa_trc(cee, 0); + if (status == BFA_STATUS_OK) { + bfa_trc(cee, 0); + /* + * The requested data has been copied to the DMA area, *process + * it. + */ + memcpy(cee->attr, cee->attr_dma.kva, + sizeof(struct bfa_cee_attr_s)); + bfa_cee_format_cee_cfg(cee->attr); + } + cee->get_attr_pending = BFA_FALSE; + if (cee->cbfn.get_attr_cbfn) { + bfa_trc(cee, 0); + cee->cbfn.get_attr_cbfn(cee->cbfn.get_attr_cbarg, status); + } + bfa_trc(cee, 0); +} + +/** + * bfa_cee_get_attr_isr() + * + * + * @param[in] cee - Pointer to the CEE module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_cee_get_stats_isr(struct bfa_cee_s *cee, bfa_status_t status) +{ + cee->get_stats_status = status; + bfa_trc(cee, 0); + if (status == BFA_STATUS_OK) { + bfa_trc(cee, 0); + /* + * The requested data has been copied to the DMA area, process + * it. + */ + memcpy(cee->stats, cee->stats_dma.kva, + sizeof(struct bfa_cee_stats_s)); + bfa_cee_format_cee_stats(cee->stats); + } + cee->get_stats_pending = BFA_FALSE; + bfa_trc(cee, 0); + if (cee->cbfn.get_stats_cbfn) { + bfa_trc(cee, 0); + cee->cbfn.get_stats_cbfn(cee->cbfn.get_stats_cbarg, status); + } + bfa_trc(cee, 0); +} + +/** + * bfa_cee_get_attr_isr() + * + * + * @param[in] cee - Pointer to the CEE module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_cee_reset_stats_isr(struct bfa_cee_s *cee, bfa_status_t status) +{ + cee->reset_stats_status = status; + cee->reset_stats_pending = BFA_FALSE; + if (cee->cbfn.reset_stats_cbfn) + cee->cbfn.reset_stats_cbfn(cee->cbfn.reset_stats_cbarg, status); +} + +/** + * bfa_cee_meminfo() + * + * + * @param[in] void + * + * @return Size of DMA region + */ +u32 +bfa_cee_meminfo(void) +{ + return (bfa_cee_attr_meminfo() + bfa_cee_stats_meminfo()); +} + +/** + * bfa_cee_mem_claim() + * + * + * @param[in] cee CEE module pointer + * dma_kva Kernel Virtual Address of CEE DMA Memory + * dma_pa Physical Address of CEE DMA Memory + * + * @return void + */ +void +bfa_cee_mem_claim(struct bfa_cee_s *cee, u8 *dma_kva, u64 dma_pa) +{ + cee->attr_dma.kva = dma_kva; + cee->attr_dma.pa = dma_pa; + cee->stats_dma.kva = dma_kva + bfa_cee_attr_meminfo(); + cee->stats_dma.pa = dma_pa + bfa_cee_attr_meminfo(); + cee->attr = (struct bfa_cee_attr_s *)dma_kva; + cee->stats = + (struct bfa_cee_stats_s *)(dma_kva + bfa_cee_attr_meminfo()); +} + +/** + * bfa_cee_get_attr() + * + * Send the request to the f/w to fetch CEE attributes. + * + * @param[in] Pointer to the CEE module data structure. + * + * @return Status + */ + +bfa_status_t +bfa_cee_get_attr(struct bfa_cee_s *cee, struct bfa_cee_attr_s *attr, + bfa_cee_get_attr_cbfn_t cbfn, void *cbarg) +{ + struct bfi_cee_get_req_s *cmd; + + bfa_assert((cee != NULL) && (cee->ioc != NULL)); + bfa_trc(cee, 0); + if (!bfa_ioc_is_operational(cee->ioc)) { + bfa_trc(cee, 0); + return BFA_STATUS_IOC_FAILURE; + } + if (cee->get_attr_pending == BFA_TRUE) { + bfa_trc(cee, 0); + return BFA_STATUS_DEVBUSY; + } + cee->get_attr_pending = BFA_TRUE; + cmd = (struct bfi_cee_get_req_s *)cee->get_cfg_mb.msg; + cee->attr = attr; + cee->cbfn.get_attr_cbfn = cbfn; + cee->cbfn.get_attr_cbarg = cbarg; + bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_GET_CFG_REQ, + bfa_ioc_portid(cee->ioc)); + bfa_dma_be_addr_set(cmd->dma_addr, cee->attr_dma.pa); + bfa_ioc_mbox_queue(cee->ioc, &cee->get_cfg_mb); + bfa_trc(cee, 0); + + return BFA_STATUS_OK; +} + +/** + * bfa_cee_get_stats() + * + * Send the request to the f/w to fetch CEE statistics. + * + * @param[in] Pointer to the CEE module data structure. + * + * @return Status + */ + +bfa_status_t +bfa_cee_get_stats(struct bfa_cee_s *cee, struct bfa_cee_stats_s *stats, + bfa_cee_get_stats_cbfn_t cbfn, void *cbarg) +{ + struct bfi_cee_get_req_s *cmd; + + bfa_assert((cee != NULL) && (cee->ioc != NULL)); + + if (!bfa_ioc_is_operational(cee->ioc)) { + bfa_trc(cee, 0); + return BFA_STATUS_IOC_FAILURE; + } + if (cee->get_stats_pending == BFA_TRUE) { + bfa_trc(cee, 0); + return BFA_STATUS_DEVBUSY; + } + cee->get_stats_pending = BFA_TRUE; + cmd = (struct bfi_cee_get_req_s *)cee->get_stats_mb.msg; + cee->stats = stats; + cee->cbfn.get_stats_cbfn = cbfn; + cee->cbfn.get_stats_cbarg = cbarg; + bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_GET_STATS_REQ, + bfa_ioc_portid(cee->ioc)); + bfa_dma_be_addr_set(cmd->dma_addr, cee->stats_dma.pa); + bfa_ioc_mbox_queue(cee->ioc, &cee->get_stats_mb); + bfa_trc(cee, 0); + + return BFA_STATUS_OK; +} + +/** + * bfa_cee_reset_stats() + * + * + * @param[in] Pointer to the CEE module data structure. + * + * @return Status + */ + +bfa_status_t +bfa_cee_reset_stats(struct bfa_cee_s *cee, bfa_cee_reset_stats_cbfn_t cbfn, + void *cbarg) +{ + struct bfi_cee_reset_stats_s *cmd; + + bfa_assert((cee != NULL) && (cee->ioc != NULL)); + if (!bfa_ioc_is_operational(cee->ioc)) { + bfa_trc(cee, 0); + return BFA_STATUS_IOC_FAILURE; + } + if (cee->reset_stats_pending == BFA_TRUE) { + bfa_trc(cee, 0); + return BFA_STATUS_DEVBUSY; + } + cee->reset_stats_pending = BFA_TRUE; + cmd = (struct bfi_cee_reset_stats_s *)cee->reset_stats_mb.msg; + cee->cbfn.reset_stats_cbfn = cbfn; + cee->cbfn.reset_stats_cbarg = cbarg; + bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_RESET_STATS, + bfa_ioc_portid(cee->ioc)); + bfa_ioc_mbox_queue(cee->ioc, &cee->reset_stats_mb); + bfa_trc(cee, 0); + return BFA_STATUS_OK; +} + +/** + * bfa_cee_isrs() + * + * + * @param[in] Pointer to the CEE module data structure. + * + * @return void + */ + +void +bfa_cee_isr(void *cbarg, struct bfi_mbmsg_s *m) +{ + union bfi_cee_i2h_msg_u *msg; + struct bfi_cee_get_rsp_s *get_rsp; + struct bfa_cee_s *cee = (struct bfa_cee_s *)cbarg; + msg = (union bfi_cee_i2h_msg_u *)m; + get_rsp = (struct bfi_cee_get_rsp_s *)m; + bfa_trc(cee, msg->mh.msg_id); + switch (msg->mh.msg_id) { + case BFI_CEE_I2H_GET_CFG_RSP: + bfa_trc(cee, get_rsp->cmd_status); + bfa_cee_get_attr_isr(cee, get_rsp->cmd_status); + break; + case BFI_CEE_I2H_GET_STATS_RSP: + bfa_cee_get_stats_isr(cee, get_rsp->cmd_status); + break; + case BFI_CEE_I2H_RESET_STATS_RSP: + bfa_cee_reset_stats_isr(cee, get_rsp->cmd_status); + break; + default: + bfa_assert(0); + } +} + +/** + * bfa_cee_hbfail() + * + * + * @param[in] Pointer to the CEE module data structure. + * + * @return void + */ + +void +bfa_cee_hbfail(void *arg) +{ + struct bfa_cee_s *cee; + cee = (struct bfa_cee_s *)arg; + + if (cee->get_attr_pending == BFA_TRUE) { + cee->get_attr_status = BFA_STATUS_FAILED; + cee->get_attr_pending = BFA_FALSE; + if (cee->cbfn.get_attr_cbfn) { + cee->cbfn.get_attr_cbfn(cee->cbfn.get_attr_cbarg, + BFA_STATUS_FAILED); + } + } + if (cee->get_stats_pending == BFA_TRUE) { + cee->get_stats_status = BFA_STATUS_FAILED; + cee->get_stats_pending = BFA_FALSE; + if (cee->cbfn.get_stats_cbfn) { + cee->cbfn.get_stats_cbfn(cee->cbfn.get_stats_cbarg, + BFA_STATUS_FAILED); + } + } + if (cee->reset_stats_pending == BFA_TRUE) { + cee->reset_stats_status = BFA_STATUS_FAILED; + cee->reset_stats_pending = BFA_FALSE; + if (cee->cbfn.reset_stats_cbfn) { + cee->cbfn.reset_stats_cbfn(cee->cbfn.reset_stats_cbarg, + BFA_STATUS_FAILED); + } + } +} + +/** + * bfa_cee_attach() + * + * + * @param[in] cee - Pointer to the CEE module data structure + * ioc - Pointer to the ioc module data structure + * dev - Pointer to the device driver module data structure + * The device driver specific mbox ISR functions have + * this pointer as one of the parameters. + * trcmod - + * logmod - + * + * @return void + */ +void +bfa_cee_attach(struct bfa_cee_s *cee, struct bfa_ioc_s *ioc, void *dev, + struct bfa_trc_mod_s *trcmod, struct bfa_log_mod_s *logmod) +{ + bfa_assert(cee != NULL); + cee->dev = dev; + cee->trcmod = trcmod; + cee->logmod = logmod; + cee->ioc = ioc; + + bfa_ioc_mbox_regisr(cee->ioc, BFI_MC_CEE, bfa_cee_isr, cee); + bfa_ioc_hbfail_init(&cee->hbfail, bfa_cee_hbfail, cee); + bfa_ioc_hbfail_register(cee->ioc, &cee->hbfail); + bfa_trc(cee, 0); +} + +/** + * bfa_cee_detach() + * + * + * @param[in] cee - Pointer to the CEE module data structure + * + * @return void + */ +void +bfa_cee_detach(struct bfa_cee_s *cee) +{ + /* + * For now, just check if there is some ioctl pending and mark that as + * failed? + */ + /* bfa_cee_hbfail(cee); */ +} diff --git a/drivers/scsi/bfa/bfa_core.c b/drivers/scsi/bfa/bfa_core.c new file mode 100644 index 000000000000..44e2d1155c51 --- /dev/null +++ b/drivers/scsi/bfa/bfa_core.c @@ -0,0 +1,402 @@ +/* + * 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. + */ + +#include +#include +#include +#include + +#define DEF_CFG_NUM_FABRICS 1 +#define DEF_CFG_NUM_LPORTS 256 +#define DEF_CFG_NUM_CQS 4 +#define DEF_CFG_NUM_IOIM_REQS (BFA_IOIM_MAX) +#define DEF_CFG_NUM_TSKIM_REQS 128 +#define DEF_CFG_NUM_FCXP_REQS 64 +#define DEF_CFG_NUM_UF_BUFS 64 +#define DEF_CFG_NUM_RPORTS 1024 +#define DEF_CFG_NUM_ITNIMS (DEF_CFG_NUM_RPORTS) +#define DEF_CFG_NUM_TINS 256 + +#define DEF_CFG_NUM_SGPGS 2048 +#define DEF_CFG_NUM_REQQ_ELEMS 256 +#define DEF_CFG_NUM_RSPQ_ELEMS 64 +#define DEF_CFG_NUM_SBOOT_TGTS 16 +#define DEF_CFG_NUM_SBOOT_LUNS 16 + +/** + * Use this function query the memory requirement of the BFA library. + * This function needs to be called before bfa_attach() to get the + * memory required of the BFA layer for a given driver configuration. + * + * This call will fail, if the cap is out of range compared to pre-defined + * values within the BFA library + * + * @param[in] cfg - pointer to bfa_ioc_cfg_t. Driver layer should indicate + * its configuration in this structure. + * The default values for struct bfa_iocfc_cfg_s can be + * fetched using bfa_cfg_get_default() API. + * + * If cap's boundary check fails, the library will use + * the default bfa_cap_t values (and log a warning msg). + * + * @param[out] meminfo - pointer to bfa_meminfo_t. This content + * indicates the memory type (see bfa_mem_type_t) and + * amount of memory required. + * + * Driver should allocate the memory, populate the + * starting address for each block and provide the same + * structure as input parameter to bfa_attach() call. + * + * @return void + * + * Special Considerations: @note + */ +void +bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo) +{ + int i; + u32 km_len = 0, dm_len = 0; + + bfa_assert((cfg != NULL) && (meminfo != NULL)); + + bfa_os_memset((void *)meminfo, 0, sizeof(struct bfa_meminfo_s)); + meminfo->meminfo[BFA_MEM_TYPE_KVA - 1].mem_type = + BFA_MEM_TYPE_KVA; + meminfo->meminfo[BFA_MEM_TYPE_DMA - 1].mem_type = + BFA_MEM_TYPE_DMA; + + bfa_iocfc_meminfo(cfg, &km_len, &dm_len); + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->meminfo(cfg, &km_len, &dm_len); + + + meminfo->meminfo[BFA_MEM_TYPE_KVA - 1].mem_len = km_len; + meminfo->meminfo[BFA_MEM_TYPE_DMA - 1].mem_len = dm_len; +} + +/** + * Use this function to do attach the driver instance with the BFA + * library. This function will not trigger any HW initialization + * process (which will be done in bfa_init() call) + * + * This call will fail, if the cap is out of range compared to + * pre-defined values within the BFA library + * + * @param[out] bfa Pointer to bfa_t. + * @param[in] bfad Opaque handle back to the driver's IOC structure + * @param[in] cfg Pointer to bfa_ioc_cfg_t. Should be same structure + * that was used in bfa_cfg_get_meminfo(). + * @param[in] meminfo Pointer to bfa_meminfo_t. The driver should + * use the bfa_cfg_get_meminfo() call to + * find the memory blocks required, allocate the + * required memory and provide the starting addresses. + * @param[in] pcidev pointer to struct bfa_pcidev_s + * + * @return + * void + * + * Special Considerations: + * + * @note + * + */ +void +bfa_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + int i; + struct bfa_mem_elem_s *melem; + + bfa->fcs = BFA_FALSE; + + bfa_assert((cfg != NULL) && (meminfo != NULL)); + + /** + * initialize all memory pointers for iterative allocation + */ + for (i = 0; i < BFA_MEM_TYPE_MAX; i++) { + melem = meminfo->meminfo + i; + melem->kva_curp = melem->kva; + melem->dma_curp = melem->dma; + } + + bfa_iocfc_attach(bfa, bfad, cfg, meminfo, pcidev); + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->attach(bfa, bfad, cfg, meminfo, pcidev); + +} + +/** + * Use this function to delete a BFA IOC. IOC should be stopped (by + * calling bfa_stop()) before this function call. + * + * @param[in] bfa - pointer to bfa_t. + * + * @return + * void + * + * Special Considerations: + * + * @note + */ +void +bfa_detach(struct bfa_s *bfa) +{ + int i; + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->detach(bfa); + + bfa_iocfc_detach(bfa); +} + + +void +bfa_init_trc(struct bfa_s *bfa, struct bfa_trc_mod_s *trcmod) +{ + bfa->trcmod = trcmod; +} + + +void +bfa_init_log(struct bfa_s *bfa, struct bfa_log_mod_s *logmod) +{ + bfa->logm = logmod; +} + + +void +bfa_init_aen(struct bfa_s *bfa, struct bfa_aen_s *aen) +{ + bfa->aen = aen; +} + +void +bfa_init_plog(struct bfa_s *bfa, struct bfa_plog_s *plog) +{ + bfa->plog = plog; +} + +/** + * Initialize IOC. + * + * This function will return immediately, when the IOC initialization is + * completed, the bfa_cb_init() will be called. + * + * @param[in] bfa instance + * + * @return void + * + * Special Considerations: + * + * @note + * When this function returns, the driver should register the interrupt service + * routine(s) and enable the device interrupts. If this is not done, + * bfa_cb_init() will never get called + */ +void +bfa_init(struct bfa_s *bfa) +{ + bfa_iocfc_init(bfa); +} + +/** + * Use this function initiate the IOC configuration setup. This function + * will return immediately. + * + * @param[in] bfa instance + * + * @return None + */ +void +bfa_start(struct bfa_s *bfa) +{ + bfa_iocfc_start(bfa); +} + +/** + * Use this function quiese the IOC. This function will return immediately, + * when the IOC is actually stopped, the bfa_cb_stop() will be called. + * + * @param[in] bfa - pointer to bfa_t. + * + * @return None + * + * Special Considerations: + * bfa_cb_stop() could be called before or after bfa_stop() returns. + * + * @note + * In case of any failure, we could handle it automatically by doing a + * reset and then succeed the bfa_stop() call. + */ +void +bfa_stop(struct bfa_s *bfa) +{ + bfa_iocfc_stop(bfa); +} + +void +bfa_comp_deq(struct bfa_s *bfa, struct list_head *comp_q) +{ + INIT_LIST_HEAD(comp_q); + list_splice_tail_init(&bfa->comp_q, comp_q); +} + +void +bfa_comp_process(struct bfa_s *bfa, struct list_head *comp_q) +{ + struct list_head *qe; + struct list_head *qen; + struct bfa_cb_qe_s *hcb_qe; + + list_for_each_safe(qe, qen, comp_q) { + hcb_qe = (struct bfa_cb_qe_s *) qe; + hcb_qe->cbfn(hcb_qe->cbarg, BFA_TRUE); + } +} + +void +bfa_comp_free(struct bfa_s *bfa, struct list_head *comp_q) +{ + struct list_head *qe; + struct bfa_cb_qe_s *hcb_qe; + + while (!list_empty(comp_q)) { + bfa_q_deq(comp_q, &qe); + hcb_qe = (struct bfa_cb_qe_s *) qe; + hcb_qe->cbfn(hcb_qe->cbarg, BFA_FALSE); + } +} + +void +bfa_attach_fcs(struct bfa_s *bfa) +{ + bfa->fcs = BFA_TRUE; +} + +/** + * Periodic timer heart beat from driver + */ +void +bfa_timer_tick(struct bfa_s *bfa) +{ + bfa_timer_beat(&bfa->timer_mod); +} + +#ifndef BFA_BIOS_BUILD +/** + * Return the list of PCI vendor/device id lists supported by this + * BFA instance. + */ +void +bfa_get_pciids(struct bfa_pciid_s **pciids, int *npciids) +{ + static struct bfa_pciid_s __pciids[] = { + {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_FC_8G2P}, + {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_FC_8G1P}, + {BFA_PCI_VENDOR_ID_BROCADE, BFA_PCI_DEVICE_ID_CT}, + }; + + *npciids = sizeof(__pciids) / sizeof(__pciids[0]); + *pciids = __pciids; +} + +/** + * Use this function query the default struct bfa_iocfc_cfg_s value (compiled + * into BFA layer). The OS driver can then turn back and overwrite entries that + * have been configured by the user. + * + * @param[in] cfg - pointer to bfa_ioc_cfg_t + * + * @return + * void + * + * Special Considerations: + * note + */ +void +bfa_cfg_get_default(struct bfa_iocfc_cfg_s *cfg) +{ + cfg->fwcfg.num_fabrics = DEF_CFG_NUM_FABRICS; + cfg->fwcfg.num_lports = DEF_CFG_NUM_LPORTS; + cfg->fwcfg.num_rports = DEF_CFG_NUM_RPORTS; + cfg->fwcfg.num_ioim_reqs = DEF_CFG_NUM_IOIM_REQS; + cfg->fwcfg.num_tskim_reqs = DEF_CFG_NUM_TSKIM_REQS; + cfg->fwcfg.num_fcxp_reqs = DEF_CFG_NUM_FCXP_REQS; + cfg->fwcfg.num_uf_bufs = DEF_CFG_NUM_UF_BUFS; + cfg->fwcfg.num_cqs = DEF_CFG_NUM_CQS; + + cfg->drvcfg.num_reqq_elems = DEF_CFG_NUM_REQQ_ELEMS; + cfg->drvcfg.num_rspq_elems = DEF_CFG_NUM_RSPQ_ELEMS; + cfg->drvcfg.num_sgpgs = DEF_CFG_NUM_SGPGS; + cfg->drvcfg.num_sboot_tgts = DEF_CFG_NUM_SBOOT_TGTS; + cfg->drvcfg.num_sboot_luns = DEF_CFG_NUM_SBOOT_LUNS; + cfg->drvcfg.path_tov = BFA_FCPIM_PATHTOV_DEF; + cfg->drvcfg.ioc_recover = BFA_FALSE; + cfg->drvcfg.delay_comp = BFA_FALSE; + +} + +void +bfa_cfg_get_min(struct bfa_iocfc_cfg_s *cfg) +{ + bfa_cfg_get_default(cfg); + cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MIN; + cfg->fwcfg.num_tskim_reqs = BFA_TSKIM_MIN; + cfg->fwcfg.num_fcxp_reqs = BFA_FCXP_MIN; + cfg->fwcfg.num_uf_bufs = BFA_UF_MIN; + cfg->fwcfg.num_rports = BFA_RPORT_MIN; + + cfg->drvcfg.num_sgpgs = BFA_SGPG_MIN; + cfg->drvcfg.num_reqq_elems = BFA_REQQ_NELEMS_MIN; + cfg->drvcfg.num_rspq_elems = BFA_RSPQ_NELEMS_MIN; + cfg->drvcfg.min_cfg = BFA_TRUE; +} + +void +bfa_get_attr(struct bfa_s *bfa, struct bfa_ioc_attr_s *ioc_attr) +{ + bfa_ioc_get_attr(&bfa->ioc, ioc_attr); +} + +/** + * Retrieve firmware trace information on IOC failure. + */ +bfa_status_t +bfa_debug_fwsave(struct bfa_s *bfa, void *trcdata, int *trclen) +{ + return bfa_ioc_debug_fwsave(&bfa->ioc, trcdata, trclen); +} + +/** + * Fetch firmware trace data. + * + * @param[in] bfa BFA instance + * @param[out] trcdata Firmware trace buffer + * @param[in,out] trclen Firmware trace buffer len + * + * @retval BFA_STATUS_OK Firmware trace is fetched. + * @retval BFA_STATUS_INPROGRESS Firmware trace fetch is in progress. + */ +bfa_status_t +bfa_debug_fwtrc(struct bfa_s *bfa, void *trcdata, int *trclen) +{ + return bfa_ioc_debug_fwtrc(&bfa->ioc, trcdata, trclen); +} +#endif diff --git a/drivers/scsi/bfa/bfa_csdebug.c b/drivers/scsi/bfa/bfa_csdebug.c new file mode 100644 index 000000000000..1b71d349451a --- /dev/null +++ b/drivers/scsi/bfa/bfa_csdebug.c @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#include +#include +#include +#include + +/** + * cs_debug_api + */ + + +void +bfa_panic(int line, char *file, char *panicstr) +{ + bfa_log(NULL, BFA_LOG_HAL_ASSERT, file, line, panicstr); + bfa_os_panic(); +} + +void +bfa_sm_panic(struct bfa_log_mod_s *logm, int line, char *file, int event) +{ + bfa_log(logm, BFA_LOG_HAL_SM_ASSERT, file, line, event); + bfa_os_panic(); +} + +int +bfa_q_is_on_q_func(struct list_head *q, struct list_head *qe) +{ + struct list_head *tqe; + + tqe = bfa_q_next(q); + while (tqe != q) { + if (tqe == qe) + return (1); + tqe = bfa_q_next(tqe); + if (tqe == NULL) + break; + } + return (0); +} + + diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c new file mode 100644 index 000000000000..401babe3494e --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcpim.c @@ -0,0 +1,175 @@ +/* + * 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. + */ + +#include +#include + +BFA_TRC_FILE(HAL, FCPIM); +BFA_MODULE(fcpim); + +/** + * hal_fcpim_mod BFA FCP Initiator Mode module + */ + +/** + * Compute and return memory needed by FCP(im) module. + */ +static void +bfa_fcpim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len) +{ + bfa_itnim_meminfo(cfg, km_len, dm_len); + + /** + * IO memory + */ + if (cfg->fwcfg.num_ioim_reqs < BFA_IOIM_MIN) + cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MIN; + else if (cfg->fwcfg.num_ioim_reqs > BFA_IOIM_MAX) + cfg->fwcfg.num_ioim_reqs = BFA_IOIM_MAX; + + *km_len += cfg->fwcfg.num_ioim_reqs * + (sizeof(struct bfa_ioim_s) + sizeof(struct bfa_ioim_sp_s)); + + *dm_len += cfg->fwcfg.num_ioim_reqs * BFI_IOIM_SNSLEN; + + /** + * task management command memory + */ + if (cfg->fwcfg.num_tskim_reqs < BFA_TSKIM_MIN) + cfg->fwcfg.num_tskim_reqs = BFA_TSKIM_MIN; + *km_len += cfg->fwcfg.num_tskim_reqs * sizeof(struct bfa_tskim_s); +} + + +static void +bfa_fcpim_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + bfa_trc(bfa, cfg->drvcfg.path_tov); + bfa_trc(bfa, cfg->fwcfg.num_rports); + bfa_trc(bfa, cfg->fwcfg.num_ioim_reqs); + bfa_trc(bfa, cfg->fwcfg.num_tskim_reqs); + + fcpim->bfa = bfa; + fcpim->num_itnims = cfg->fwcfg.num_rports; + fcpim->num_ioim_reqs = cfg->fwcfg.num_ioim_reqs; + fcpim->num_tskim_reqs = cfg->fwcfg.num_tskim_reqs; + fcpim->path_tov = cfg->drvcfg.path_tov; + fcpim->delay_comp = cfg->drvcfg.delay_comp; + + bfa_itnim_attach(fcpim, meminfo); + bfa_tskim_attach(fcpim, meminfo); + bfa_ioim_attach(fcpim, meminfo); +} + +static void +bfa_fcpim_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_fcpim_detach(struct bfa_s *bfa) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + bfa_ioim_detach(fcpim); + bfa_tskim_detach(fcpim); +} + +static void +bfa_fcpim_start(struct bfa_s *bfa) +{ +} + +static void +bfa_fcpim_stop(struct bfa_s *bfa) +{ +} + +static void +bfa_fcpim_iocdisable(struct bfa_s *bfa) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfa_itnim_s *itnim; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &fcpim->itnim_q) { + itnim = (struct bfa_itnim_s *) qe; + bfa_itnim_iocdisable(itnim); + } +} + +void +bfa_fcpim_path_tov_set(struct bfa_s *bfa, u16 path_tov) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + fcpim->path_tov = path_tov * 1000; + if (fcpim->path_tov > BFA_FCPIM_PATHTOV_MAX) + fcpim->path_tov = BFA_FCPIM_PATHTOV_MAX; +} + +u16 +bfa_fcpim_path_tov_get(struct bfa_s *bfa) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + return (fcpim->path_tov / 1000); +} + +bfa_status_t +bfa_fcpim_get_modstats(struct bfa_s *bfa, struct bfa_fcpim_stats_s *modstats) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + *modstats = fcpim->stats; + + return BFA_STATUS_OK; +} + +bfa_status_t +bfa_fcpim_clr_modstats(struct bfa_s *bfa) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + memset(&fcpim->stats, 0, sizeof(struct bfa_fcpim_stats_s)); + + return BFA_STATUS_OK; +} + +void +bfa_fcpim_qdepth_set(struct bfa_s *bfa, u16 q_depth) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + bfa_assert(q_depth <= BFA_IOCFC_QDEPTH_MAX); + + fcpim->q_depth = q_depth; +} + +u16 +bfa_fcpim_qdepth_get(struct bfa_s *bfa) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + + return (fcpim->q_depth); +} + + diff --git a/drivers/scsi/bfa/bfa_fcpim_priv.h b/drivers/scsi/bfa/bfa_fcpim_priv.h new file mode 100644 index 000000000000..153206cfb37a --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcpim_priv.h @@ -0,0 +1,188 @@ +/* + * 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. + */ + +#ifndef __BFA_FCPIM_PRIV_H__ +#define __BFA_FCPIM_PRIV_H__ + +#include +#include +#include +#include "bfa_sgpg_priv.h" + +#define BFA_ITNIM_MIN 32 +#define BFA_ITNIM_MAX 1024 + +#define BFA_IOIM_MIN 8 +#define BFA_IOIM_MAX 2000 + +#define BFA_TSKIM_MIN 4 +#define BFA_TSKIM_MAX 512 +#define BFA_FCPIM_PATHTOV_DEF (30 * 1000) /* in millisecs */ +#define BFA_FCPIM_PATHTOV_MAX (90 * 1000) /* in millisecs */ + +#define bfa_fcpim_stats(__fcpim, __stats) \ + (__fcpim)->stats.__stats ++ + +struct bfa_fcpim_mod_s { + struct bfa_s *bfa; + struct bfa_itnim_s *itnim_arr; + struct bfa_ioim_s *ioim_arr; + struct bfa_ioim_sp_s *ioim_sp_arr; + struct bfa_tskim_s *tskim_arr; + struct bfa_dma_s snsbase; + int num_itnims; + int num_ioim_reqs; + int num_tskim_reqs; + u32 path_tov; + u16 q_depth; + u16 rsvd; + struct list_head itnim_q; /* queue of active itnim */ + struct list_head ioim_free_q; /* free IO resources */ + struct list_head ioim_resfree_q; /* IOs waiting for f/w */ + struct list_head ioim_comp_q; /* IO global comp Q */ + struct list_head tskim_free_q; + u32 ios_active; /* current active IOs */ + u32 delay_comp; + struct bfa_fcpim_stats_s stats; +}; + +struct bfa_ioim_s; +struct bfa_tskim_s; + +/** + * BFA IO (initiator mode) + */ +struct bfa_ioim_s { + struct list_head qe; /* queue elememt */ + bfa_sm_t sm; /* BFA ioim state machine */ + struct bfa_s *bfa; /* BFA module */ + struct bfa_fcpim_mod_s *fcpim; /* parent fcpim module */ + struct bfa_itnim_s *itnim; /* i-t-n nexus for this IO */ + struct bfad_ioim_s *dio; /* driver IO handle */ + u16 iotag; /* FWI IO tag */ + u16 abort_tag; /* unqiue abort request tag */ + u16 nsges; /* number of SG elements */ + u16 nsgpgs; /* number of SG pages */ + struct bfa_sgpg_s *sgpg; /* first SG page */ + struct list_head sgpg_q; /* allocated SG pages */ + struct bfa_cb_qe_s hcb_qe; /* bfa callback qelem */ + bfa_cb_cbfn_t io_cbfn; /* IO completion handler */ + struct bfa_ioim_sp_s *iosp; /* slow-path IO handling */ +}; + +struct bfa_ioim_sp_s { + struct bfi_msg_s comp_rspmsg; /* IO comp f/w response */ + u8 *snsinfo; /* sense info for this IO */ + struct bfa_sgpg_wqe_s sgpg_wqe; /* waitq elem for sgpg */ + struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */ + bfa_boolean_t abort_explicit; /* aborted by OS */ + struct bfa_tskim_s *tskim; /* Relevant TM cmd */ +}; + +/** + * BFA Task management command (initiator mode) + */ +struct bfa_tskim_s { + struct list_head qe; + bfa_sm_t sm; + struct bfa_s *bfa; /* BFA module */ + struct bfa_fcpim_mod_s *fcpim; /* parent fcpim module */ + struct bfa_itnim_s *itnim; /* i-t-n nexus for this IO */ + struct bfad_tskim_s *dtsk; /* driver task mgmt cmnd */ + bfa_boolean_t notify; /* notify itnim on TM comp */ + lun_t lun; /* lun if applicable */ + enum fcp_tm_cmnd tm_cmnd; /* task management command */ + u16 tsk_tag; /* FWI IO tag */ + u8 tsecs; /* timeout in seconds */ + struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */ + struct list_head io_q; /* queue of affected IOs */ + struct bfa_wc_s wc; /* waiting counter */ + struct bfa_cb_qe_s hcb_qe; /* bfa callback qelem */ + enum bfi_tskim_status tsk_status; /* TM status */ +}; + +/** + * BFA i-t-n (initiator mode) + */ +struct bfa_itnim_s { + struct list_head qe; /* queue element */ + bfa_sm_t sm; /* i-t-n im BFA state machine */ + struct bfa_s *bfa; /* bfa instance */ + struct bfa_rport_s *rport; /* bfa rport */ + void *ditn; /* driver i-t-n structure */ + struct bfi_mhdr_s mhdr; /* pre-built mhdr */ + u8 msg_no; /* itnim/rport firmware handle */ + u8 reqq; /* CQ for requests */ + struct bfa_cb_qe_s hcb_qe; /* bfa callback qelem */ + struct list_head pending_q; /* queue of pending IO requests*/ + struct list_head io_q; /* queue of active IO requests */ + struct list_head io_cleanup_q; /* IO being cleaned up */ + struct list_head tsk_q; /* queue of active TM commands */ + struct list_head delay_comp_q;/* queue of failed inflight cmds */ + bfa_boolean_t seq_rec; /* SQER supported */ + bfa_boolean_t is_online; /* itnim is ONLINE for IO */ + bfa_boolean_t iotov_active; /* IO TOV timer is active */ + struct bfa_wc_s wc; /* waiting counter */ + struct bfa_timer_s timer; /* pending IO TOV */ + struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */ + struct bfa_fcpim_mod_s *fcpim; /* fcpim module */ + struct bfa_itnim_hal_stats_s stats; +}; + +#define bfa_itnim_is_online(_itnim) (_itnim)->is_online +#define BFA_FCPIM_MOD(_hal) (&(_hal)->modules.fcpim_mod) +#define BFA_IOIM_FROM_TAG(_fcpim, _iotag) \ + (&fcpim->ioim_arr[_iotag]) +#define BFA_TSKIM_FROM_TAG(_fcpim, _tmtag) \ + (&fcpim->tskim_arr[_tmtag & (fcpim->num_tskim_reqs - 1)]) + +/* + * function prototypes + */ +void bfa_ioim_attach(struct bfa_fcpim_mod_s *fcpim, + struct bfa_meminfo_s *minfo); +void bfa_ioim_detach(struct bfa_fcpim_mod_s *fcpim); +void bfa_ioim_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +void bfa_ioim_good_comp_isr(struct bfa_s *bfa, + struct bfi_msg_s *msg); +void bfa_ioim_cleanup(struct bfa_ioim_s *ioim); +void bfa_ioim_cleanup_tm(struct bfa_ioim_s *ioim, + struct bfa_tskim_s *tskim); +void bfa_ioim_iocdisable(struct bfa_ioim_s *ioim); +void bfa_ioim_tov(struct bfa_ioim_s *ioim); + +void bfa_tskim_attach(struct bfa_fcpim_mod_s *fcpim, + struct bfa_meminfo_s *minfo); +void bfa_tskim_detach(struct bfa_fcpim_mod_s *fcpim); +void bfa_tskim_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +void bfa_tskim_iodone(struct bfa_tskim_s *tskim); +void bfa_tskim_iocdisable(struct bfa_tskim_s *tskim); +void bfa_tskim_cleanup(struct bfa_tskim_s *tskim); + +void bfa_itnim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len); +void bfa_itnim_attach(struct bfa_fcpim_mod_s *fcpim, + struct bfa_meminfo_s *minfo); +void bfa_itnim_detach(struct bfa_fcpim_mod_s *fcpim); +void bfa_itnim_iocdisable(struct bfa_itnim_s *itnim); +void bfa_itnim_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +void bfa_itnim_iodone(struct bfa_itnim_s *itnim); +void bfa_itnim_tskdone(struct bfa_itnim_s *itnim); +bfa_boolean_t bfa_itnim_hold_io(struct bfa_itnim_s *itnim); + +#endif /* __BFA_FCPIM_PRIV_H__ */ + diff --git a/drivers/scsi/bfa/bfa_fcport.c b/drivers/scsi/bfa/bfa_fcport.c new file mode 100644 index 000000000000..992435987deb --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcport.c @@ -0,0 +1,1671 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +BFA_TRC_FILE(HAL, PPORT); +BFA_MODULE(pport); + +#define bfa_pport_callback(__pport, __event) do { \ + if ((__pport)->bfa->fcs) { \ + (__pport)->event_cbfn((__pport)->event_cbarg, (__event)); \ + } else { \ + (__pport)->hcb_event = (__event); \ + bfa_cb_queue((__pport)->bfa, &(__pport)->hcb_qe, \ + __bfa_cb_port_event, (__pport)); \ + } \ +} while (0) + +/* + * The port is considered disabled if corresponding physical port or IOC are + * disabled explicitly + */ +#define BFA_PORT_IS_DISABLED(bfa) \ + ((bfa_pport_is_disabled(bfa) == BFA_TRUE) || \ + (bfa_ioc_is_disabled(&bfa->ioc) == BFA_TRUE)) + +/* + * forward declarations + */ +static bfa_boolean_t bfa_pport_send_enable(struct bfa_pport_s *port); +static bfa_boolean_t bfa_pport_send_disable(struct bfa_pport_s *port); +static void bfa_pport_update_linkinfo(struct bfa_pport_s *pport); +static void bfa_pport_reset_linkinfo(struct bfa_pport_s *pport); +static void bfa_pport_set_wwns(struct bfa_pport_s *port); +static void __bfa_cb_port_event(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_port_stats(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_port_stats_clr(void *cbarg, bfa_boolean_t complete); +static void bfa_port_stats_timeout(void *cbarg); +static void bfa_port_stats_clr_timeout(void *cbarg); + +/** + * bfa_pport_private + */ + +/** + * BFA port state machine events + */ +enum bfa_pport_sm_event { + BFA_PPORT_SM_START = 1, /* start port state machine */ + BFA_PPORT_SM_STOP = 2, /* stop port state machine */ + BFA_PPORT_SM_ENABLE = 3, /* enable port */ + BFA_PPORT_SM_DISABLE = 4, /* disable port state machine */ + BFA_PPORT_SM_FWRSP = 5, /* firmware enable/disable rsp */ + BFA_PPORT_SM_LINKUP = 6, /* firmware linkup event */ + BFA_PPORT_SM_LINKDOWN = 7, /* firmware linkup down */ + BFA_PPORT_SM_QRESUME = 8, /* CQ space available */ + BFA_PPORT_SM_HWFAIL = 9, /* IOC h/w failure */ +}; + +static void bfa_pport_sm_uninit(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_enabling_qwait(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_enabling(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_linkdown(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_linkup(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_disabling(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_disabling_qwait(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_disabled(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_stopped(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_iocdown(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); +static void bfa_pport_sm_iocfail(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event); + +static struct bfa_sm_table_s hal_pport_sm_table[] = { + {BFA_SM(bfa_pport_sm_uninit), BFA_PPORT_ST_UNINIT}, + {BFA_SM(bfa_pport_sm_enabling_qwait), BFA_PPORT_ST_ENABLING_QWAIT}, + {BFA_SM(bfa_pport_sm_enabling), BFA_PPORT_ST_ENABLING}, + {BFA_SM(bfa_pport_sm_linkdown), BFA_PPORT_ST_LINKDOWN}, + {BFA_SM(bfa_pport_sm_linkup), BFA_PPORT_ST_LINKUP}, + {BFA_SM(bfa_pport_sm_disabling_qwait), + BFA_PPORT_ST_DISABLING_QWAIT}, + {BFA_SM(bfa_pport_sm_disabling), BFA_PPORT_ST_DISABLING}, + {BFA_SM(bfa_pport_sm_disabled), BFA_PPORT_ST_DISABLED}, + {BFA_SM(bfa_pport_sm_stopped), BFA_PPORT_ST_STOPPED}, + {BFA_SM(bfa_pport_sm_iocdown), BFA_PPORT_ST_IOCDOWN}, + {BFA_SM(bfa_pport_sm_iocfail), BFA_PPORT_ST_IOCDOWN}, +}; + +static void +bfa_pport_aen_post(struct bfa_pport_s *pport, enum bfa_port_aen_event event) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = pport->bfa->logm; + wwn_t pwwn = pport->pwwn; + char pwwn_ptr[BFA_STRING_32]; + struct bfa_ioc_attr_s ioc_attr; + + wwn2str(pwwn_ptr, pwwn); + switch (event) { + case BFA_PORT_AEN_ONLINE: + bfa_log(logmod, BFA_AEN_PORT_ONLINE, pwwn_ptr); + break; + case BFA_PORT_AEN_OFFLINE: + bfa_log(logmod, BFA_AEN_PORT_OFFLINE, pwwn_ptr); + break; + case BFA_PORT_AEN_ENABLE: + bfa_log(logmod, BFA_AEN_PORT_ENABLE, pwwn_ptr); + break; + case BFA_PORT_AEN_DISABLE: + bfa_log(logmod, BFA_AEN_PORT_DISABLE, pwwn_ptr); + break; + case BFA_PORT_AEN_DISCONNECT: + bfa_log(logmod, BFA_AEN_PORT_DISCONNECT, pwwn_ptr); + break; + case BFA_PORT_AEN_QOS_NEG: + bfa_log(logmod, BFA_AEN_PORT_QOS_NEG, pwwn_ptr); + break; + default: + break; + } + + bfa_ioc_get_attr(&pport->bfa->ioc, &ioc_attr); + aen_data.port.ioc_type = ioc_attr.ioc_type; + aen_data.port.pwwn = pwwn; +} + +static void +bfa_pport_sm_uninit(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_START: + /** + * Start event after IOC is configured and BFA is started. + */ + if (bfa_pport_send_enable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_enabling_qwait); + break; + + case BFA_PPORT_SM_ENABLE: + /** + * Port is persistently configured to be in enabled state. Do + * not change state. Port enabling is done when START event is + * received. + */ + break; + + case BFA_PPORT_SM_DISABLE: + /** + * If a port is persistently configured to be disabled, the + * first event will a port disable request. + */ + bfa_sm_set_state(pport, bfa_pport_sm_disabled); + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_enabling_qwait(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_QRESUME: + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + bfa_pport_send_enable(pport); + break; + + case BFA_PPORT_SM_STOP: + bfa_reqq_wcancel(&pport->reqq_wait); + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + break; + + case BFA_PPORT_SM_ENABLE: + /** + * Already enable is in progress. + */ + break; + + case BFA_PPORT_SM_DISABLE: + /** + * Just send disable request to firmware when room becomes + * available in request queue. + */ + bfa_sm_set_state(pport, bfa_pport_sm_disabled); + bfa_reqq_wcancel(&pport->reqq_wait); + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_DISABLE, 0, "Port Disable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISABLE); + break; + + case BFA_PPORT_SM_LINKUP: + case BFA_PPORT_SM_LINKDOWN: + /** + * Possible to get link events when doing back-to-back + * enable/disables. + */ + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_reqq_wcancel(&pport->reqq_wait); + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_enabling(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_FWRSP: + case BFA_PPORT_SM_LINKDOWN: + bfa_sm_set_state(pport, bfa_pport_sm_linkdown); + break; + + case BFA_PPORT_SM_LINKUP: + bfa_pport_update_linkinfo(pport); + bfa_sm_set_state(pport, bfa_pport_sm_linkup); + + bfa_assert(pport->event_cbfn); + bfa_pport_callback(pport, BFA_PPORT_LINKUP); + break; + + case BFA_PPORT_SM_ENABLE: + /** + * Already being enabled. + */ + break; + + case BFA_PPORT_SM_DISABLE: + if (bfa_pport_send_disable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_disabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_disabling_qwait); + + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_DISABLE, 0, "Port Disable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISABLE); + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_linkdown(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_LINKUP: + bfa_pport_update_linkinfo(pport); + bfa_sm_set_state(pport, bfa_pport_sm_linkup); + bfa_assert(pport->event_cbfn); + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_ST_CHANGE, 0, "Port Linkup"); + bfa_pport_callback(pport, BFA_PPORT_LINKUP); + bfa_pport_aen_post(pport, BFA_PORT_AEN_ONLINE); + /** + * If QoS is enabled and it is not online, + * Send a separate event. + */ + if ((pport->cfg.qos_enabled) + && (bfa_os_ntohl(pport->qos_attr.state) != BFA_QOS_ONLINE)) + bfa_pport_aen_post(pport, BFA_PORT_AEN_QOS_NEG); + + break; + + case BFA_PPORT_SM_LINKDOWN: + /** + * Possible to get link down event. + */ + break; + + case BFA_PPORT_SM_ENABLE: + /** + * Already enabled. + */ + break; + + case BFA_PPORT_SM_DISABLE: + if (bfa_pport_send_disable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_disabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_disabling_qwait); + + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_DISABLE, 0, "Port Disable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISABLE); + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_linkup(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_ENABLE: + /** + * Already enabled. + */ + break; + + case BFA_PPORT_SM_DISABLE: + if (bfa_pport_send_disable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_disabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_disabling_qwait); + + bfa_pport_reset_linkinfo(pport); + bfa_pport_callback(pport, BFA_PPORT_LINKDOWN); + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_DISABLE, 0, "Port Disable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE); + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISABLE); + break; + + case BFA_PPORT_SM_LINKDOWN: + bfa_sm_set_state(pport, bfa_pport_sm_linkdown); + bfa_pport_reset_linkinfo(pport); + bfa_pport_callback(pport, BFA_PPORT_LINKDOWN); + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_ST_CHANGE, 0, "Port Linkdown"); + if (BFA_PORT_IS_DISABLED(pport->bfa)) { + bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE); + } else { + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT); + } + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + bfa_pport_reset_linkinfo(pport); + if (BFA_PORT_IS_DISABLED(pport->bfa)) { + bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE); + } else { + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT); + } + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + bfa_pport_reset_linkinfo(pport); + bfa_pport_callback(pport, BFA_PPORT_LINKDOWN); + if (BFA_PORT_IS_DISABLED(pport->bfa)) { + bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE); + } else { + bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT); + } + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_disabling_qwait(struct bfa_pport_s *pport, + enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_QRESUME: + bfa_sm_set_state(pport, bfa_pport_sm_disabling); + bfa_pport_send_disable(pport); + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + bfa_reqq_wcancel(&pport->reqq_wait); + break; + + case BFA_PPORT_SM_DISABLE: + /** + * Already being disabled. + */ + break; + + case BFA_PPORT_SM_LINKUP: + case BFA_PPORT_SM_LINKDOWN: + /** + * Possible to get link events when doing back-to-back + * enable/disables. + */ + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocfail); + bfa_reqq_wcancel(&pport->reqq_wait); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_disabling(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_FWRSP: + bfa_sm_set_state(pport, bfa_pport_sm_disabled); + break; + + case BFA_PPORT_SM_DISABLE: + /** + * Already being disabled. + */ + break; + + case BFA_PPORT_SM_ENABLE: + if (bfa_pport_send_enable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_enabling_qwait); + + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_ENABLE, 0, "Port Enable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_ENABLE); + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + break; + + case BFA_PPORT_SM_LINKUP: + case BFA_PPORT_SM_LINKDOWN: + /** + * Possible to get link events when doing back-to-back + * enable/disables. + */ + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocfail); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_disabled(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_START: + /** + * Ignore start event for a port that is disabled. + */ + break; + + case BFA_PPORT_SM_STOP: + bfa_sm_set_state(pport, bfa_pport_sm_stopped); + break; + + case BFA_PPORT_SM_ENABLE: + if (bfa_pport_send_enable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_enabling_qwait); + + bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_ENABLE, 0, "Port Enable"); + bfa_pport_aen_post(pport, BFA_PORT_AEN_ENABLE); + break; + + case BFA_PPORT_SM_DISABLE: + /** + * Already disabled. + */ + break; + + case BFA_PPORT_SM_HWFAIL: + bfa_sm_set_state(pport, bfa_pport_sm_iocfail); + break; + + default: + bfa_sm_fault(pport->bfa, event); + } +} + +static void +bfa_pport_sm_stopped(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_START: + if (bfa_pport_send_enable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_enabling_qwait); + break; + + default: + /** + * Ignore all other events. + */ + ; + } +} + +/** + * Port is enabled. IOC is down/failed. + */ +static void +bfa_pport_sm_iocdown(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_START: + if (bfa_pport_send_enable(pport)) + bfa_sm_set_state(pport, bfa_pport_sm_enabling); + else + bfa_sm_set_state(pport, bfa_pport_sm_enabling_qwait); + break; + + default: + /** + * Ignore all events. + */ + ; + } +} + +/** + * Port is disabled. IOC is down/failed. + */ +static void +bfa_pport_sm_iocfail(struct bfa_pport_s *pport, enum bfa_pport_sm_event event) +{ + bfa_trc(pport->bfa, event); + + switch (event) { + case BFA_PPORT_SM_START: + bfa_sm_set_state(pport, bfa_pport_sm_disabled); + break; + + case BFA_PPORT_SM_ENABLE: + bfa_sm_set_state(pport, bfa_pport_sm_iocdown); + break; + + default: + /** + * Ignore all events. + */ + ; + } +} + + + +/** + * bfa_pport_private + */ + +static void +__bfa_cb_port_event(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_pport_s *pport = cbarg; + + if (complete) + pport->event_cbfn(pport->event_cbarg, pport->hcb_event); +} + +#define PPORT_STATS_DMA_SZ (BFA_ROUNDUP(sizeof(union bfa_pport_stats_u), \ + BFA_CACHELINE_SZ)) + +static void +bfa_pport_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, + u32 *dm_len) +{ + *dm_len += PPORT_STATS_DMA_SZ; +} + +static void +bfa_pport_qresume(void *cbarg) +{ + struct bfa_pport_s *port = cbarg; + + bfa_sm_send_event(port, BFA_PPORT_SM_QRESUME); +} + +static void +bfa_pport_mem_claim(struct bfa_pport_s *pport, struct bfa_meminfo_s *meminfo) +{ + u8 *dm_kva; + u64 dm_pa; + + dm_kva = bfa_meminfo_dma_virt(meminfo); + dm_pa = bfa_meminfo_dma_phys(meminfo); + + pport->stats_kva = dm_kva; + pport->stats_pa = dm_pa; + pport->stats = (union bfa_pport_stats_u *)dm_kva; + + dm_kva += PPORT_STATS_DMA_SZ; + dm_pa += PPORT_STATS_DMA_SZ; + + bfa_meminfo_dma_virt(meminfo) = dm_kva; + bfa_meminfo_dma_phys(meminfo) = dm_pa; +} + +/** + * Memory initialization. + */ +static void +bfa_pport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + struct bfa_pport_cfg_s *port_cfg = &pport->cfg; + + bfa_os_memset(pport, 0, sizeof(struct bfa_pport_s)); + pport->bfa = bfa; + + bfa_pport_mem_claim(pport, meminfo); + + bfa_sm_set_state(pport, bfa_pport_sm_uninit); + + /** + * initialize and set default configuration + */ + port_cfg->topology = BFA_PPORT_TOPOLOGY_P2P; + port_cfg->speed = BFA_PPORT_SPEED_AUTO; + port_cfg->trunked = BFA_FALSE; + port_cfg->maxfrsize = 0; + + port_cfg->trl_def_speed = BFA_PPORT_SPEED_1GBPS; + + bfa_reqq_winit(&pport->reqq_wait, bfa_pport_qresume, pport); +} + +static void +bfa_pport_initdone(struct bfa_s *bfa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + /** + * Initialize port attributes from IOC hardware data. + */ + bfa_pport_set_wwns(pport); + if (pport->cfg.maxfrsize == 0) + pport->cfg.maxfrsize = bfa_ioc_maxfrsize(&bfa->ioc); + pport->cfg.rx_bbcredit = bfa_ioc_rx_bbcredit(&bfa->ioc); + pport->speed_sup = bfa_ioc_speed_sup(&bfa->ioc); + + bfa_assert(pport->cfg.maxfrsize); + bfa_assert(pport->cfg.rx_bbcredit); + bfa_assert(pport->speed_sup); +} + +static void +bfa_pport_detach(struct bfa_s *bfa) +{ +} + +/** + * Called when IOC is ready. + */ +static void +bfa_pport_start(struct bfa_s *bfa) +{ + bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_START); +} + +/** + * Called before IOC is stopped. + */ +static void +bfa_pport_stop(struct bfa_s *bfa) +{ + bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_STOP); +} + +/** + * Called when IOC failure is detected. + */ +static void +bfa_pport_iocdisable(struct bfa_s *bfa) +{ + bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_HWFAIL); +} + +static void +bfa_pport_update_linkinfo(struct bfa_pport_s *pport) +{ + struct bfi_pport_event_s *pevent = pport->event_arg.i2hmsg.event; + + pport->speed = pevent->link_state.speed; + pport->topology = pevent->link_state.topology; + + if (pport->topology == BFA_PPORT_TOPOLOGY_LOOP) + pport->myalpa = pevent->link_state.tl.loop_info.myalpa; + + /* + * QoS Details + */ + bfa_os_assign(pport->qos_attr, pevent->link_state.qos_attr); + bfa_os_assign(pport->qos_vc_attr, pevent->link_state.qos_vc_attr); + + bfa_trc(pport->bfa, pport->speed); + bfa_trc(pport->bfa, pport->topology); +} + +static void +bfa_pport_reset_linkinfo(struct bfa_pport_s *pport) +{ + pport->speed = BFA_PPORT_SPEED_UNKNOWN; + pport->topology = BFA_PPORT_TOPOLOGY_NONE; +} + +/** + * Send port enable message to firmware. + */ +static bfa_boolean_t +bfa_pport_send_enable(struct bfa_pport_s *port) +{ + struct bfi_pport_enable_req_s *m; + + /** + * Increment message tag before queue check, so that responses to old + * requests are discarded. + */ + port->msgtag++; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + if (!m) { + bfa_reqq_wait(port->bfa, BFA_REQQ_PORT, &port->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_ENABLE_REQ, + bfa_lpuid(port->bfa)); + m->nwwn = port->nwwn; + m->pwwn = port->pwwn; + m->port_cfg = port->cfg; + m->msgtag = port->msgtag; + m->port_cfg.maxfrsize = bfa_os_htons(port->cfg.maxfrsize); + bfa_dma_be_addr_set(m->stats_dma_addr, port->stats_pa); + bfa_trc(port->bfa, m->stats_dma_addr.a32.addr_lo); + bfa_trc(port->bfa, m->stats_dma_addr.a32.addr_hi); + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); + return BFA_TRUE; +} + +/** + * Send port disable message to firmware. + */ +static bfa_boolean_t +bfa_pport_send_disable(struct bfa_pport_s *port) +{ + bfi_pport_disable_req_t *m; + + /** + * Increment message tag before queue check, so that responses to old + * requests are discarded. + */ + port->msgtag++; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + if (!m) { + bfa_reqq_wait(port->bfa, BFA_REQQ_PORT, &port->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_DISABLE_REQ, + bfa_lpuid(port->bfa)); + m->msgtag = port->msgtag; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); + + return BFA_TRUE; +} + +static void +bfa_pport_set_wwns(struct bfa_pport_s *port) +{ + port->pwwn = bfa_ioc_get_pwwn(&port->bfa->ioc); + port->nwwn = bfa_ioc_get_nwwn(&port->bfa->ioc); + + bfa_trc(port->bfa, port->pwwn); + bfa_trc(port->bfa, port->nwwn); +} + +static void +bfa_port_send_txcredit(void *port_cbarg) +{ + + struct bfa_pport_s *port = port_cbarg; + struct bfi_pport_set_svc_params_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + if (!m) { + bfa_trc(port->bfa, port->cfg.tx_bbcredit); + return; + } + + bfi_h2i_set(m->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_SET_SVC_PARAMS_REQ, + bfa_lpuid(port->bfa)); + m->tx_bbcredit = bfa_os_htons((u16) port->cfg.tx_bbcredit); + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); +} + + + +/** + * bfa_pport_public + */ + +/** + * Firmware message handler. + */ +void +bfa_pport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + union bfi_pport_i2h_msg_u i2hmsg; + + i2hmsg.msg = msg; + pport->event_arg.i2hmsg = i2hmsg; + + switch (msg->mhdr.msg_id) { + case BFI_PPORT_I2H_ENABLE_RSP: + if (pport->msgtag == i2hmsg.enable_rsp->msgtag) + bfa_sm_send_event(pport, BFA_PPORT_SM_FWRSP); + break; + + case BFI_PPORT_I2H_DISABLE_RSP: + if (pport->msgtag == i2hmsg.enable_rsp->msgtag) + bfa_sm_send_event(pport, BFA_PPORT_SM_FWRSP); + break; + + case BFI_PPORT_I2H_EVENT: + switch (i2hmsg.event->link_state.linkstate) { + case BFA_PPORT_LINKUP: + bfa_sm_send_event(pport, BFA_PPORT_SM_LINKUP); + break; + case BFA_PPORT_LINKDOWN: + bfa_sm_send_event(pport, BFA_PPORT_SM_LINKDOWN); + break; + case BFA_PPORT_TRUNK_LINKDOWN: + /** todo: event notification */ + break; + } + break; + + case BFI_PPORT_I2H_GET_STATS_RSP: + case BFI_PPORT_I2H_GET_QOS_STATS_RSP: + /* + * check for timer pop before processing the rsp + */ + if (pport->stats_busy == BFA_FALSE + || pport->stats_status == BFA_STATUS_ETIMER) + break; + + bfa_timer_stop(&pport->timer); + pport->stats_status = i2hmsg.getstats_rsp->status; + bfa_cb_queue(pport->bfa, &pport->hcb_qe, __bfa_cb_port_stats, + pport); + break; + case BFI_PPORT_I2H_CLEAR_STATS_RSP: + case BFI_PPORT_I2H_CLEAR_QOS_STATS_RSP: + /* + * check for timer pop before processing the rsp + */ + if (pport->stats_busy == BFA_FALSE + || pport->stats_status == BFA_STATUS_ETIMER) + break; + + bfa_timer_stop(&pport->timer); + pport->stats_status = BFA_STATUS_OK; + bfa_cb_queue(pport->bfa, &pport->hcb_qe, + __bfa_cb_port_stats_clr, pport); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * bfa_pport_api + */ + +/** + * Registered callback for port events. + */ +void +bfa_pport_event_register(struct bfa_s *bfa, + void (*cbfn) (void *cbarg, bfa_pport_event_t event), + void *cbarg) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + pport->event_cbfn = cbfn; + pport->event_cbarg = cbarg; +} + +bfa_status_t +bfa_pport_enable(struct bfa_s *bfa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + if (pport->diag_busy) + return (BFA_STATUS_DIAG_BUSY); + else if (bfa_sm_cmp_state + (BFA_PORT_MOD(bfa), bfa_pport_sm_disabling_qwait)) + return (BFA_STATUS_DEVBUSY); + + bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_ENABLE); + return BFA_STATUS_OK; +} + +bfa_status_t +bfa_pport_disable(struct bfa_s *bfa) +{ + bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_DISABLE); + return BFA_STATUS_OK; +} + +/** + * Configure port speed. + */ +bfa_status_t +bfa_pport_cfg_speed(struct bfa_s *bfa, enum bfa_pport_speed speed) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, speed); + + if ((speed != BFA_PPORT_SPEED_AUTO) && (speed > pport->speed_sup)) { + bfa_trc(bfa, pport->speed_sup); + return BFA_STATUS_UNSUPP_SPEED; + } + + pport->cfg.speed = speed; + + return (BFA_STATUS_OK); +} + +/** + * Get current speed. + */ +enum bfa_pport_speed +bfa_pport_get_speed(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->speed; +} + +/** + * Configure port topology. + */ +bfa_status_t +bfa_pport_cfg_topology(struct bfa_s *bfa, enum bfa_pport_topology topology) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, topology); + bfa_trc(bfa, pport->cfg.topology); + + switch (topology) { + case BFA_PPORT_TOPOLOGY_P2P: + case BFA_PPORT_TOPOLOGY_LOOP: + case BFA_PPORT_TOPOLOGY_AUTO: + break; + + default: + return BFA_STATUS_EINVAL; + } + + pport->cfg.topology = topology; + return (BFA_STATUS_OK); +} + +/** + * Get current topology. + */ +enum bfa_pport_topology +bfa_pport_get_topology(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->topology; +} + +bfa_status_t +bfa_pport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, alpa); + bfa_trc(bfa, pport->cfg.cfg_hardalpa); + bfa_trc(bfa, pport->cfg.hardalpa); + + pport->cfg.cfg_hardalpa = BFA_TRUE; + pport->cfg.hardalpa = alpa; + + return (BFA_STATUS_OK); +} + +bfa_status_t +bfa_pport_clr_hardalpa(struct bfa_s *bfa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, pport->cfg.cfg_hardalpa); + bfa_trc(bfa, pport->cfg.hardalpa); + + pport->cfg.cfg_hardalpa = BFA_FALSE; + return (BFA_STATUS_OK); +} + +bfa_boolean_t +bfa_pport_get_hardalpa(struct bfa_s *bfa, u8 *alpa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + *alpa = port->cfg.hardalpa; + return port->cfg.cfg_hardalpa; +} + +u8 +bfa_pport_get_myalpa(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->myalpa; +} + +bfa_status_t +bfa_pport_cfg_maxfrsize(struct bfa_s *bfa, u16 maxfrsize) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, maxfrsize); + bfa_trc(bfa, pport->cfg.maxfrsize); + + /* + * with in range + */ + if ((maxfrsize > FC_MAX_PDUSZ) || (maxfrsize < FC_MIN_PDUSZ)) + return (BFA_STATUS_INVLD_DFSZ); + + /* + * power of 2, if not the max frame size of 2112 + */ + if ((maxfrsize != FC_MAX_PDUSZ) && (maxfrsize & (maxfrsize - 1))) + return (BFA_STATUS_INVLD_DFSZ); + + pport->cfg.maxfrsize = maxfrsize; + return (BFA_STATUS_OK); +} + +u16 +bfa_pport_get_maxfrsize(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->cfg.maxfrsize; +} + +u32 +bfa_pport_mypid(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->mypid; +} + +u8 +bfa_pport_get_rx_bbcredit(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return port->cfg.rx_bbcredit; +} + +void +bfa_pport_set_tx_bbcredit(struct bfa_s *bfa, u16 tx_bbcredit) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + port->cfg.tx_bbcredit = (u8) tx_bbcredit; + bfa_port_send_txcredit(port); +} + +/** + * Get port attributes. + */ + +wwn_t +bfa_pport_get_wwn(struct bfa_s *bfa, bfa_boolean_t node) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + if (node) + return pport->nwwn; + else + return pport->pwwn; +} + +void +bfa_pport_get_attr(struct bfa_s *bfa, struct bfa_pport_attr_s *attr) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_os_memset(attr, 0, sizeof(struct bfa_pport_attr_s)); + + attr->nwwn = pport->nwwn; + attr->pwwn = pport->pwwn; + + bfa_os_memcpy(&attr->pport_cfg, &pport->cfg, + sizeof(struct bfa_pport_cfg_s)); + /* + * speed attributes + */ + attr->pport_cfg.speed = pport->cfg.speed; + attr->speed_supported = pport->speed_sup; + attr->speed = pport->speed; + attr->cos_supported = FC_CLASS_3; + + /* + * topology attributes + */ + attr->pport_cfg.topology = pport->cfg.topology; + attr->topology = pport->topology; + + /* + * beacon attributes + */ + attr->beacon = pport->beacon; + attr->link_e2e_beacon = pport->link_e2e_beacon; + attr->plog_enabled = bfa_plog_get_setting(pport->bfa->plog); + + attr->pport_cfg.path_tov = bfa_fcpim_path_tov_get(bfa); + attr->pport_cfg.q_depth = bfa_fcpim_qdepth_get(bfa); + attr->port_state = bfa_sm_to_state(hal_pport_sm_table, pport->sm); + if (bfa_ioc_is_disabled(&pport->bfa->ioc)) + attr->port_state = BFA_PPORT_ST_IOCDIS; + else if (bfa_ioc_fw_mismatch(&pport->bfa->ioc)) + attr->port_state = BFA_PPORT_ST_FWMISMATCH; +} + +static void +bfa_port_stats_query(void *cbarg) +{ + struct bfa_pport_s *port = (struct bfa_pport_s *)cbarg; + bfi_pport_get_stats_req_t *msg; + + msg = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + + if (!msg) { + port->stats_qfull = BFA_TRUE; + bfa_reqq_winit(&port->stats_reqq_wait, bfa_port_stats_query, + port); + bfa_reqq_wait(port->bfa, BFA_REQQ_PORT, &port->stats_reqq_wait); + return; + } + port->stats_qfull = BFA_FALSE; + + bfa_os_memset(msg, 0, sizeof(bfi_pport_get_stats_req_t)); + bfi_h2i_set(msg->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_GET_STATS_REQ, + bfa_lpuid(port->bfa)); + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); + + return; +} + +static void +bfa_port_stats_clear(void *cbarg) +{ + struct bfa_pport_s *port = (struct bfa_pport_s *)cbarg; + bfi_pport_clear_stats_req_t *msg; + + msg = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + + if (!msg) { + port->stats_qfull = BFA_TRUE; + bfa_reqq_winit(&port->stats_reqq_wait, bfa_port_stats_clear, + port); + bfa_reqq_wait(port->bfa, BFA_REQQ_PORT, &port->stats_reqq_wait); + return; + } + port->stats_qfull = BFA_FALSE; + + bfa_os_memset(msg, 0, sizeof(bfi_pport_clear_stats_req_t)); + bfi_h2i_set(msg->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_CLEAR_STATS_REQ, + bfa_lpuid(port->bfa)); + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); + return; +} + +static void +bfa_port_qos_stats_clear(void *cbarg) +{ + struct bfa_pport_s *port = (struct bfa_pport_s *)cbarg; + bfi_pport_clear_qos_stats_req_t *msg; + + msg = bfa_reqq_next(port->bfa, BFA_REQQ_PORT); + + if (!msg) { + port->stats_qfull = BFA_TRUE; + bfa_reqq_winit(&port->stats_reqq_wait, bfa_port_qos_stats_clear, + port); + bfa_reqq_wait(port->bfa, BFA_REQQ_PORT, &port->stats_reqq_wait); + return; + } + port->stats_qfull = BFA_FALSE; + + bfa_os_memset(msg, 0, sizeof(bfi_pport_clear_qos_stats_req_t)); + bfi_h2i_set(msg->mh, BFI_MC_FC_PORT, BFI_PPORT_H2I_CLEAR_QOS_STATS_REQ, + bfa_lpuid(port->bfa)); + bfa_reqq_produce(port->bfa, BFA_REQQ_PORT); + return; +} + +static void +bfa_pport_stats_swap(union bfa_pport_stats_u *d, union bfa_pport_stats_u *s) +{ + u32 *dip = (u32 *) d; + u32 *sip = (u32 *) s; + int i; + + /* + * Do 64 bit fields swap first + */ + for (i = 0; + i < + ((sizeof(union bfa_pport_stats_u) - + sizeof(struct bfa_qos_stats_s)) / sizeof(u32)); i = i + 2) { +#ifdef __BIGENDIAN + dip[i] = bfa_os_ntohl(sip[i]); + dip[i + 1] = bfa_os_ntohl(sip[i + 1]); +#else + dip[i] = bfa_os_ntohl(sip[i + 1]); + dip[i + 1] = bfa_os_ntohl(sip[i]); +#endif + } + + /* + * Now swap the 32 bit fields + */ + for (; i < (sizeof(union bfa_pport_stats_u) / sizeof(u32)); ++i) + dip[i] = bfa_os_ntohl(sip[i]); +} + +static void +__bfa_cb_port_stats_clr(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_pport_s *port = cbarg; + + if (complete) { + port->stats_cbfn(port->stats_cbarg, port->stats_status); + } else { + port->stats_busy = BFA_FALSE; + port->stats_status = BFA_STATUS_OK; + } +} + +static void +bfa_port_stats_clr_timeout(void *cbarg) +{ + struct bfa_pport_s *port = (struct bfa_pport_s *)cbarg; + + bfa_trc(port->bfa, port->stats_qfull); + + if (port->stats_qfull) { + bfa_reqq_wcancel(&port->stats_reqq_wait); + port->stats_qfull = BFA_FALSE; + } + + port->stats_status = BFA_STATUS_ETIMER; + bfa_cb_queue(port->bfa, &port->hcb_qe, __bfa_cb_port_stats_clr, port); +} + +static void +__bfa_cb_port_stats(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_pport_s *port = cbarg; + + if (complete) { + if (port->stats_status == BFA_STATUS_OK) + bfa_pport_stats_swap(port->stats_ret, port->stats); + port->stats_cbfn(port->stats_cbarg, port->stats_status); + } else { + port->stats_busy = BFA_FALSE; + port->stats_status = BFA_STATUS_OK; + } +} + +static void +bfa_port_stats_timeout(void *cbarg) +{ + struct bfa_pport_s *port = (struct bfa_pport_s *)cbarg; + + bfa_trc(port->bfa, port->stats_qfull); + + if (port->stats_qfull) { + bfa_reqq_wcancel(&port->stats_reqq_wait); + port->stats_qfull = BFA_FALSE; + } + + port->stats_status = BFA_STATUS_ETIMER; + bfa_cb_queue(port->bfa, &port->hcb_qe, __bfa_cb_port_stats, port); +} + +#define BFA_PORT_STATS_TOV 1000 + +/** + * Fetch port attributes. + */ +bfa_status_t +bfa_pport_get_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats, + bfa_cb_pport_t cbfn, void *cbarg) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + if (port->stats_busy) { + bfa_trc(bfa, port->stats_busy); + return (BFA_STATUS_DEVBUSY); + } + + port->stats_busy = BFA_TRUE; + port->stats_ret = stats; + port->stats_cbfn = cbfn; + port->stats_cbarg = cbarg; + + bfa_port_stats_query(port); + + bfa_timer_start(bfa, &port->timer, bfa_port_stats_timeout, port, + BFA_PORT_STATS_TOV); + return (BFA_STATUS_OK); +} + +bfa_status_t +bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + if (port->stats_busy) { + bfa_trc(bfa, port->stats_busy); + return (BFA_STATUS_DEVBUSY); + } + + port->stats_busy = BFA_TRUE; + port->stats_cbfn = cbfn; + port->stats_cbarg = cbarg; + + bfa_port_stats_clear(port); + + bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port, + BFA_PORT_STATS_TOV); + return (BFA_STATUS_OK); +} + +bfa_status_t +bfa_pport_trunk_enable(struct bfa_s *bfa, u8 bitmap) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, bitmap); + bfa_trc(bfa, pport->cfg.trunked); + bfa_trc(bfa, pport->cfg.trunk_ports); + + if (!bitmap || (bitmap & (bitmap - 1))) + return BFA_STATUS_EINVAL; + + pport->cfg.trunked = BFA_TRUE; + pport->cfg.trunk_ports = bitmap; + + return BFA_STATUS_OK; +} + +void +bfa_pport_qos_get_attr(struct bfa_s *bfa, struct bfa_qos_attr_s *qos_attr) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + qos_attr->state = bfa_os_ntohl(pport->qos_attr.state); + qos_attr->total_bb_cr = bfa_os_ntohl(pport->qos_attr.total_bb_cr); +} + +void +bfa_pport_qos_get_vc_attr(struct bfa_s *bfa, + struct bfa_qos_vc_attr_s *qos_vc_attr) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + struct bfa_qos_vc_attr_s *bfa_vc_attr = &pport->qos_vc_attr; + u32 i = 0; + + qos_vc_attr->total_vc_count = bfa_os_ntohs(bfa_vc_attr->total_vc_count); + qos_vc_attr->shared_credit = bfa_os_ntohs(bfa_vc_attr->shared_credit); + qos_vc_attr->elp_opmode_flags = + bfa_os_ntohl(bfa_vc_attr->elp_opmode_flags); + + /* + * Individual VC info + */ + while (i < qos_vc_attr->total_vc_count) { + qos_vc_attr->vc_info[i].vc_credit = + bfa_vc_attr->vc_info[i].vc_credit; + qos_vc_attr->vc_info[i].borrow_credit = + bfa_vc_attr->vc_info[i].borrow_credit; + qos_vc_attr->vc_info[i].priority = + bfa_vc_attr->vc_info[i].priority; + ++i; + } +} + +/** + * Fetch QoS Stats. + */ +bfa_status_t +bfa_pport_get_qos_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats, + bfa_cb_pport_t cbfn, void *cbarg) +{ + /* + * QoS stats is embedded in port stats + */ + return (bfa_pport_get_stats(bfa, stats, cbfn, cbarg)); +} + +bfa_status_t +bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + if (port->stats_busy) { + bfa_trc(bfa, port->stats_busy); + return (BFA_STATUS_DEVBUSY); + } + + port->stats_busy = BFA_TRUE; + port->stats_cbfn = cbfn; + port->stats_cbarg = cbarg; + + bfa_port_qos_stats_clear(port); + + bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port, + BFA_PORT_STATS_TOV); + return (BFA_STATUS_OK); +} + +/** + * Fetch port attributes. + */ +bfa_status_t +bfa_pport_trunk_disable(struct bfa_s *bfa) +{ + return (BFA_STATUS_OK); +} + +bfa_boolean_t +bfa_pport_trunk_query(struct bfa_s *bfa, u32 *bitmap) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + *bitmap = port->cfg.trunk_ports; + return port->cfg.trunked; +} + +bfa_boolean_t +bfa_pport_is_disabled(struct bfa_s *bfa) +{ + struct bfa_pport_s *port = BFA_PORT_MOD(bfa); + + return (bfa_sm_to_state(hal_pport_sm_table, port->sm) == + BFA_PPORT_ST_DISABLED); + +} + +bfa_boolean_t +bfa_pport_is_ratelim(struct bfa_s *bfa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + +return (pport->cfg.ratelimit ? BFA_TRUE : BFA_FALSE); + +} + +void +bfa_pport_cfg_qos(struct bfa_s *bfa, bfa_boolean_t on_off) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, on_off); + bfa_trc(bfa, pport->cfg.qos_enabled); + + pport->cfg.qos_enabled = on_off; +} + +void +bfa_pport_cfg_ratelim(struct bfa_s *bfa, bfa_boolean_t on_off) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, on_off); + bfa_trc(bfa, pport->cfg.ratelimit); + + pport->cfg.ratelimit = on_off; + if (pport->cfg.trl_def_speed == BFA_PPORT_SPEED_UNKNOWN) + pport->cfg.trl_def_speed = BFA_PPORT_SPEED_1GBPS; +} + +/** + * Configure default minimum ratelim speed + */ +bfa_status_t +bfa_pport_cfg_ratelim_speed(struct bfa_s *bfa, enum bfa_pport_speed speed) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, speed); + + /* + * Auto and speeds greater than the supported speed, are invalid + */ + if ((speed == BFA_PPORT_SPEED_AUTO) || (speed > pport->speed_sup)) { + bfa_trc(bfa, pport->speed_sup); + return BFA_STATUS_UNSUPP_SPEED; + } + + pport->cfg.trl_def_speed = speed; + + return (BFA_STATUS_OK); +} + +/** + * Get default minimum ratelim speed + */ +enum bfa_pport_speed +bfa_pport_get_ratelim_speed(struct bfa_s *bfa) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, pport->cfg.trl_def_speed); + return (pport->cfg.trl_def_speed); + +} + +void +bfa_pport_busy(struct bfa_s *bfa, bfa_boolean_t status) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, status); + bfa_trc(bfa, pport->diag_busy); + + pport->diag_busy = status; +} + +void +bfa_pport_beacon(struct bfa_s *bfa, bfa_boolean_t beacon, + bfa_boolean_t link_e2e_beacon) +{ + struct bfa_pport_s *pport = BFA_PORT_MOD(bfa); + + bfa_trc(bfa, beacon); + bfa_trc(bfa, link_e2e_beacon); + bfa_trc(bfa, pport->beacon); + bfa_trc(bfa, pport->link_e2e_beacon); + + pport->beacon = beacon; + pport->link_e2e_beacon = link_e2e_beacon; +} + +bfa_boolean_t +bfa_pport_is_linkup(struct bfa_s *bfa) +{ + return bfa_sm_cmp_state(BFA_PORT_MOD(bfa), bfa_pport_sm_linkup); +} + + diff --git a/drivers/scsi/bfa/bfa_fcs.c b/drivers/scsi/bfa/bfa_fcs.c new file mode 100644 index 000000000000..7cb39a306ea9 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcs.c @@ -0,0 +1,182 @@ +/* + * 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. + */ + +/** + * bfa_fcs.c BFA FCS main + */ + +#include +#include "fcs_port.h" +#include "fcs_uf.h" +#include "fcs_vport.h" +#include "fcs_rport.h" +#include "fcs_fabric.h" +#include "fcs_fcpim.h" +#include "fcs_fcptm.h" +#include "fcbuild.h" +#include "fcs.h" +#include "bfad_drv.h" +#include + +/** + * FCS sub-modules + */ +struct bfa_fcs_mod_s { + void (*modinit) (struct bfa_fcs_s *fcs); + void (*modexit) (struct bfa_fcs_s *fcs); +}; + +#define BFA_FCS_MODULE(_mod) { _mod ## _modinit, _mod ## _modexit } + +static struct bfa_fcs_mod_s fcs_modules[] = { + BFA_FCS_MODULE(bfa_fcs_pport), + BFA_FCS_MODULE(bfa_fcs_uf), + BFA_FCS_MODULE(bfa_fcs_fabric), + BFA_FCS_MODULE(bfa_fcs_vport), + BFA_FCS_MODULE(bfa_fcs_rport), + BFA_FCS_MODULE(bfa_fcs_fcpim), +}; + +/** + * fcs_api BFA FCS API + */ + +static void +bfa_fcs_exit_comp(void *fcs_cbarg) +{ + struct bfa_fcs_s *fcs = fcs_cbarg; + struct bfad_s *bfad = fcs->bfad; + + complete(&bfad->comp); +} + + + +/** + * fcs_api BFA FCS API + */ + +/** + * FCS instance initialization. + * + * param[in] fcs FCS instance + * param[in] bfa BFA instance + * param[in] bfad BFA driver instance + * + * return None + */ +void +bfa_fcs_init(struct bfa_fcs_s *fcs, struct bfa_s *bfa, struct bfad_s *bfad, + bfa_boolean_t min_cfg) +{ + int i; + struct bfa_fcs_mod_s *mod; + + fcs->bfa = bfa; + fcs->bfad = bfad; + fcs->min_cfg = min_cfg; + + bfa_attach_fcs(bfa); + fcbuild_init(); + + for (i = 0; i < sizeof(fcs_modules) / sizeof(fcs_modules[0]); i++) { + mod = &fcs_modules[i]; + mod->modinit(fcs); + } +} + +/** + * Start FCS operations. + */ +void +bfa_fcs_start(struct bfa_fcs_s *fcs) +{ + bfa_fcs_fabric_modstart(fcs); +} + +/** + * FCS driver details initialization. + * + * param[in] fcs FCS instance + * param[in] driver_info Driver Details + * + * return None + */ +void +bfa_fcs_driver_info_init(struct bfa_fcs_s *fcs, + struct bfa_fcs_driver_info_s *driver_info) +{ + + fcs->driver_info = *driver_info; + + bfa_fcs_fabric_psymb_init(&fcs->fabric); +} + +/** + * FCS instance cleanup and exit. + * + * param[in] fcs FCS instance + * return None + */ +void +bfa_fcs_exit(struct bfa_fcs_s *fcs) +{ + struct bfa_fcs_mod_s *mod; + int nmods, i; + + bfa_wc_init(&fcs->wc, bfa_fcs_exit_comp, fcs); + + nmods = sizeof(fcs_modules) / sizeof(fcs_modules[0]); + + for (i = 0; i < nmods; i++) { + bfa_wc_up(&fcs->wc); + + mod = &fcs_modules[i]; + mod->modexit(fcs); + } + + bfa_wc_wait(&fcs->wc); +} + + +void +bfa_fcs_trc_init(struct bfa_fcs_s *fcs, struct bfa_trc_mod_s *trcmod) +{ + fcs->trcmod = trcmod; +} + + +void +bfa_fcs_log_init(struct bfa_fcs_s *fcs, struct bfa_log_mod_s *logmod) +{ + fcs->logm = logmod; +} + + +void +bfa_fcs_aen_init(struct bfa_fcs_s *fcs, struct bfa_aen_s *aen) +{ + fcs->aen = aen; +} + +void +bfa_fcs_modexit_comp(struct bfa_fcs_s *fcs) +{ + bfa_wc_down(&fcs->wc); +} + + diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c new file mode 100644 index 000000000000..8975ed041dc0 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcs_lport.c @@ -0,0 +1,940 @@ +/* + * 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. + */ + +/** + * bfa_fcs_port.c BFA FCS port + */ + +#include +#include +#include +#include +#include +#include +#include "fcs.h" +#include "fcs_lport.h" +#include "fcs_vport.h" +#include "fcs_rport.h" +#include "fcs_fcxp.h" +#include "fcs_trcmod.h" +#include "lport_priv.h" +#include + +BFA_TRC_FILE(FCS, PORT); + +/** + * Forward declarations + */ + +static void bfa_fcs_port_aen_post(struct bfa_fcs_port_s *port, + enum bfa_lport_aen_event event); +static void bfa_fcs_port_send_ls_rjt(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs, u8 reason_code, + u8 reason_code_expl); +static void bfa_fcs_port_plogi(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs, + struct fc_logi_s *plogi); +static void bfa_fcs_port_online_actions(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_offline_actions(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_unknown_init(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_unknown_online(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_unknown_offline(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_deleted(struct bfa_fcs_port_s *port); +static void bfa_fcs_port_echo(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs, + struct fc_echo_s *echo, u16 len); +static void bfa_fcs_port_rnid(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs, + struct fc_rnid_cmd_s *rnid, u16 len); +static void bfa_fs_port_get_gen_topo_data(struct bfa_fcs_port_s *port, + struct fc_rnid_general_topology_data_s *gen_topo_data); + +static struct { + void (*init) (struct bfa_fcs_port_s *port); + void (*online) (struct bfa_fcs_port_s *port); + void (*offline) (struct bfa_fcs_port_s *port); +} __port_action[] = { + { + bfa_fcs_port_unknown_init, bfa_fcs_port_unknown_online, + bfa_fcs_port_unknown_offline}, { + bfa_fcs_port_fab_init, bfa_fcs_port_fab_online, + bfa_fcs_port_fab_offline}, { + bfa_fcs_port_loop_init, bfa_fcs_port_loop_online, + bfa_fcs_port_loop_offline}, { +bfa_fcs_port_n2n_init, bfa_fcs_port_n2n_online, + bfa_fcs_port_n2n_offline},}; + +/** + * fcs_port_sm FCS logical port state machine + */ + +enum bfa_fcs_port_event { + BFA_FCS_PORT_SM_CREATE = 1, + BFA_FCS_PORT_SM_ONLINE = 2, + BFA_FCS_PORT_SM_OFFLINE = 3, + BFA_FCS_PORT_SM_DELETE = 4, + BFA_FCS_PORT_SM_DELRPORT = 5, +}; + +static void bfa_fcs_port_sm_uninit(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event); +static void bfa_fcs_port_sm_init(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event); +static void bfa_fcs_port_sm_online(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event); +static void bfa_fcs_port_sm_offline(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event); +static void bfa_fcs_port_sm_deleting(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event); + +static void +bfa_fcs_port_sm_uninit(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event) +{ + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case BFA_FCS_PORT_SM_CREATE: + bfa_sm_set_state(port, bfa_fcs_port_sm_init); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_sm_init(struct bfa_fcs_port_s *port, enum bfa_fcs_port_event event) +{ + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case BFA_FCS_PORT_SM_ONLINE: + bfa_sm_set_state(port, bfa_fcs_port_sm_online); + bfa_fcs_port_online_actions(port); + break; + + case BFA_FCS_PORT_SM_DELETE: + bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); + bfa_fcs_port_deleted(port); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_sm_online(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe, *qen; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case BFA_FCS_PORT_SM_OFFLINE: + bfa_sm_set_state(port, bfa_fcs_port_sm_offline); + bfa_fcs_port_offline_actions(port); + break; + + case BFA_FCS_PORT_SM_DELETE: + + __port_action[port->fabric->fab_type].offline(port); + + if (port->num_rports == 0) { + bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); + bfa_fcs_port_deleted(port); + } else { + bfa_sm_set_state(port, bfa_fcs_port_sm_deleting); + list_for_each_safe(qe, qen, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + bfa_fcs_rport_delete(rport); + } + } + break; + + case BFA_FCS_PORT_SM_DELRPORT: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_sm_offline(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe, *qen; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case BFA_FCS_PORT_SM_ONLINE: + bfa_sm_set_state(port, bfa_fcs_port_sm_online); + bfa_fcs_port_online_actions(port); + break; + + case BFA_FCS_PORT_SM_DELETE: + if (port->num_rports == 0) { + bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); + bfa_fcs_port_deleted(port); + } else { + bfa_sm_set_state(port, bfa_fcs_port_sm_deleting); + list_for_each_safe(qe, qen, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + bfa_fcs_rport_delete(rport); + } + } + break; + + case BFA_FCS_PORT_SM_DELRPORT: + case BFA_FCS_PORT_SM_OFFLINE: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_sm_deleting(struct bfa_fcs_port_s *port, + enum bfa_fcs_port_event event) +{ + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case BFA_FCS_PORT_SM_DELRPORT: + if (port->num_rports == 0) { + bfa_sm_set_state(port, bfa_fcs_port_sm_uninit); + bfa_fcs_port_deleted(port); + } + break; + + default: + bfa_assert(0); + } +} + + + +/** + * fcs_port_pvt + */ + +/** + * Send AEN notification + */ +static void +bfa_fcs_port_aen_post(struct bfa_fcs_port_s *port, + enum bfa_lport_aen_event event) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = port->fcs->logm; + enum bfa_port_role role = port->port_cfg.roles; + wwn_t lpwwn = bfa_fcs_port_get_pwwn(port); + char lpwwn_ptr[BFA_STRING_32]; + char *role_str[BFA_PORT_ROLE_FCP_MAX / 2 + 1] = + { "Initiator", "Target", "IPFC" }; + + wwn2str(lpwwn_ptr, lpwwn); + + bfa_assert(role <= BFA_PORT_ROLE_FCP_MAX); + + switch (event) { + case BFA_LPORT_AEN_ONLINE: + bfa_log(logmod, BFA_AEN_LPORT_ONLINE, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_OFFLINE: + bfa_log(logmod, BFA_AEN_LPORT_OFFLINE, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_NEW: + bfa_log(logmod, BFA_AEN_LPORT_NEW, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_DELETE: + bfa_log(logmod, BFA_AEN_LPORT_DELETE, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_DISCONNECT: + bfa_log(logmod, BFA_AEN_LPORT_DISCONNECT, lpwwn_ptr, + role_str[role / 2]); + break; + default: + break; + } + + aen_data.lport.vf_id = port->fabric->vf_id; + aen_data.lport.roles = role; + aen_data.lport.ppwwn = + bfa_fcs_port_get_pwwn(bfa_fcs_get_base_port(port->fcs)); + aen_data.lport.lpwwn = lpwwn; +} + +/* + * Send a LS reject + */ +static void +bfa_fcs_port_send_ls_rjt(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, + u8 reason_code, u8 reason_code_expl) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + struct bfa_rport_s *bfa_rport = NULL; + int len; + + bfa_trc(port->fcs, rx_fchs->s_id); + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_ls_rjt_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, + reason_code, reason_code_expl); + + bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL, + FC_MAX_PDUSZ, 0); +} + +/** + * Process incoming plogi from a remote port. + */ +static void +bfa_fcs_port_plogi(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, + struct fc_logi_s *plogi) +{ + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, rx_fchs->d_id); + bfa_trc(port->fcs, rx_fchs->s_id); + + /* + * If min cfg mode is enabled, drop any incoming PLOGIs + */ + if (__fcs_min_cfg(port->fcs)) { + bfa_trc(port->fcs, rx_fchs->s_id); + return; + } + + if (fc_plogi_parse(rx_fchs) != FC_PARSE_OK) { + bfa_trc(port->fcs, rx_fchs->s_id); + /* + * send a LS reject + */ + bfa_fcs_port_send_ls_rjt(port, rx_fchs, + FC_LS_RJT_RSN_PROTOCOL_ERROR, + FC_LS_RJT_EXP_SPARMS_ERR_OPTIONS); + return; + } + + /** +* Direct Attach P2P mode : verify address assigned by the r-port. + */ + if ((!bfa_fcs_fabric_is_switched(port->fabric)) + && + (memcmp + ((void *)&bfa_fcs_port_get_pwwn(port), (void *)&plogi->port_name, + sizeof(wwn_t)) < 0)) { + if (BFA_FCS_PID_IS_WKA(rx_fchs->d_id)) { + /* + * Address assigned to us cannot be a WKA + */ + bfa_fcs_port_send_ls_rjt(port, rx_fchs, + FC_LS_RJT_RSN_PROTOCOL_ERROR, + FC_LS_RJT_EXP_INVALID_NPORT_ID); + return; + } + port->pid = rx_fchs->d_id; + } + + /** + * First, check if we know the device by pwwn. + */ + rport = bfa_fcs_port_get_rport_by_pwwn(port, plogi->port_name); + if (rport) { + /** + * Direct Attach P2P mode: handle address assigned by the rport. + */ + if ((!bfa_fcs_fabric_is_switched(port->fabric)) + && + (memcmp + ((void *)&bfa_fcs_port_get_pwwn(port), + (void *)&plogi->port_name, sizeof(wwn_t)) < 0)) { + port->pid = rx_fchs->d_id; + rport->pid = rx_fchs->s_id; + } + bfa_fcs_rport_plogi(rport, rx_fchs, plogi); + return; + } + + /** + * Next, lookup rport by PID. + */ + rport = bfa_fcs_port_get_rport_by_pid(port, rx_fchs->s_id); + if (!rport) { + /** + * Inbound PLOGI from a new device. + */ + bfa_fcs_rport_plogi_create(port, rx_fchs, plogi); + return; + } + + /** + * Rport is known only by PID. + */ + if (rport->pwwn) { + /** + * This is a different device with the same pid. Old device + * disappeared. Send implicit LOGO to old device. + */ + bfa_assert(rport->pwwn != plogi->port_name); + bfa_fcs_rport_logo_imp(rport); + + /** + * Inbound PLOGI from a new device (with old PID). + */ + bfa_fcs_rport_plogi_create(port, rx_fchs, plogi); + return; + } + + /** + * PLOGI crossing each other. + */ + bfa_assert(rport->pwwn == WWN_NULL); + bfa_fcs_rport_plogi(rport, rx_fchs, plogi); +} + +/* + * Process incoming ECHO. + * Since it does not require a login, it is processed here. + */ +static void +bfa_fcs_port_echo(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, + struct fc_echo_s *echo, u16 rx_len) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + struct bfa_rport_s *bfa_rport = NULL; + int len, pyld_len; + + bfa_trc(port->fcs, rx_fchs->s_id); + bfa_trc(port->fcs, rx_fchs->d_id); + bfa_trc(port->fcs, rx_len); + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_ls_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id); + + /* + * Copy the payload (if any) from the echo frame + */ + pyld_len = rx_len - sizeof(struct fchs_s); + bfa_trc(port->fcs, pyld_len); + + if (pyld_len > len) + memcpy(((u8 *) bfa_fcxp_get_reqbuf(fcxp)) + + sizeof(struct fc_echo_s), (echo + 1), + (pyld_len - sizeof(struct fc_echo_s))); + + bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, pyld_len, &fchs, NULL, NULL, + FC_MAX_PDUSZ, 0); +} + +/* + * Process incoming RNID. + * Since it does not require a login, it is processed here. + */ +static void +bfa_fcs_port_rnid(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, + struct fc_rnid_cmd_s *rnid, u16 rx_len) +{ + struct fc_rnid_common_id_data_s common_id_data; + struct fc_rnid_general_topology_data_s gen_topo_data; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + struct bfa_rport_s *bfa_rport = NULL; + u16 len; + u32 data_format; + + bfa_trc(port->fcs, rx_fchs->s_id); + bfa_trc(port->fcs, rx_fchs->d_id); + bfa_trc(port->fcs, rx_len); + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + /* + * Check Node Indentification Data Format + * We only support General Topology Discovery Format. + * For any other requested Data Formats, we return Common Node Id Data + * only, as per FC-LS. + */ + bfa_trc(port->fcs, rnid->node_id_data_format); + if (rnid->node_id_data_format == RNID_NODEID_DATA_FORMAT_DISCOVERY) { + data_format = RNID_NODEID_DATA_FORMAT_DISCOVERY; + /* + * Get General topology data for this port + */ + bfa_fs_port_get_gen_topo_data(port, &gen_topo_data); + } else { + data_format = RNID_NODEID_DATA_FORMAT_COMMON; + } + + /* + * Copy the Node Id Info + */ + common_id_data.port_name = bfa_fcs_port_get_pwwn(port); + common_id_data.node_name = bfa_fcs_port_get_nwwn(port); + + len = fc_rnid_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, + data_format, &common_id_data, &gen_topo_data); + + bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL, + FC_MAX_PDUSZ, 0); + + return; +} + +/* + * Fill out General Topolpgy Discovery Data for RNID ELS. + */ +static void +bfa_fs_port_get_gen_topo_data(struct bfa_fcs_port_s *port, + struct fc_rnid_general_topology_data_s *gen_topo_data) +{ + + bfa_os_memset(gen_topo_data, 0, + sizeof(struct fc_rnid_general_topology_data_s)); + + gen_topo_data->asso_type = bfa_os_htonl(RNID_ASSOCIATED_TYPE_HOST); + gen_topo_data->phy_port_num = 0; /* @todo */ + gen_topo_data->num_attached_nodes = bfa_os_htonl(1); +} + +static void +bfa_fcs_port_online_actions(struct bfa_fcs_port_s *port) +{ + bfa_trc(port->fcs, port->fabric->oper_type); + + __port_action[port->fabric->fab_type].init(port); + __port_action[port->fabric->fab_type].online(port); + + bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_ONLINE); + bfa_fcb_port_online(port->fcs->bfad, port->port_cfg.roles, + port->fabric->vf_drv, (port->vport == NULL) ? + NULL : port->vport->vport_drv); +} + +static void +bfa_fcs_port_offline_actions(struct bfa_fcs_port_s *port) +{ + struct list_head *qe, *qen; + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, port->fabric->oper_type); + + __port_action[port->fabric->fab_type].offline(port); + + if (bfa_fcs_fabric_is_online(port->fabric) == BFA_TRUE) { + bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_DISCONNECT); + } else { + bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_OFFLINE); + } + bfa_fcb_port_offline(port->fcs->bfad, port->port_cfg.roles, + port->fabric->vf_drv, + (port->vport == NULL) ? NULL : port->vport->vport_drv); + + list_for_each_safe(qe, qen, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + bfa_fcs_rport_offline(rport); + } +} + +static void +bfa_fcs_port_unknown_init(struct bfa_fcs_port_s *port) +{ + bfa_assert(0); +} + +static void +bfa_fcs_port_unknown_online(struct bfa_fcs_port_s *port) +{ + bfa_assert(0); +} + +static void +bfa_fcs_port_unknown_offline(struct bfa_fcs_port_s *port) +{ + bfa_assert(0); +} + +static void +bfa_fcs_port_deleted(struct bfa_fcs_port_s *port) +{ + bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_DELETE); + + /* + * Base port will be deleted by the OS driver + */ + if (port->vport) { + bfa_fcb_port_delete(port->fcs->bfad, port->port_cfg.roles, + port->fabric->vf_drv, + port->vport ? port->vport->vport_drv : NULL); + bfa_fcs_vport_delete_comp(port->vport); + } else { + bfa_fcs_fabric_port_delete_comp(port->fabric); + } +} + + + +/** + * fcs_lport_api BFA FCS port API + */ +/** + * Module initialization + */ +void +bfa_fcs_port_modinit(struct bfa_fcs_s *fcs) +{ + +} + +/** + * Module cleanup + */ +void +bfa_fcs_port_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} + +/** + * Unsolicited frame receive handling. + */ +void +bfa_fcs_port_uf_recv(struct bfa_fcs_port_s *lport, struct fchs_s *fchs, + u16 len) +{ + u32 pid = fchs->s_id; + struct bfa_fcs_rport_s *rport = NULL; + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + bfa_stats(lport, uf_recvs); + + if (!bfa_fcs_port_is_online(lport)) { + bfa_stats(lport, uf_recv_drops); + return; + } + + /** + * First, handle ELSs that donot require a login. + */ + /* + * Handle PLOGI first + */ + if ((fchs->type == FC_TYPE_ELS) && + (els_cmd->els_code == FC_ELS_PLOGI)) { + bfa_fcs_port_plogi(lport, fchs, (struct fc_logi_s *) els_cmd); + return; + } + + /* + * Handle ECHO separately. + */ + if ((fchs->type == FC_TYPE_ELS) && (els_cmd->els_code == FC_ELS_ECHO)) { + bfa_fcs_port_echo(lport, fchs, + (struct fc_echo_s *) els_cmd, len); + return; + } + + /* + * Handle RNID separately. + */ + if ((fchs->type == FC_TYPE_ELS) && (els_cmd->els_code == FC_ELS_RNID)) { + bfa_fcs_port_rnid(lport, fchs, + (struct fc_rnid_cmd_s *) els_cmd, len); + return; + } + + /** + * look for a matching remote port ID + */ + rport = bfa_fcs_port_get_rport_by_pid(lport, pid); + if (rport) { + bfa_trc(rport->fcs, fchs->s_id); + bfa_trc(rport->fcs, fchs->d_id); + bfa_trc(rport->fcs, fchs->type); + + bfa_fcs_rport_uf_recv(rport, fchs, len); + return; + } + + /** + * Only handles ELS frames for now. + */ + if (fchs->type != FC_TYPE_ELS) { + bfa_trc(lport->fcs, fchs->type); + bfa_assert(0); + return; + } + + bfa_trc(lport->fcs, els_cmd->els_code); + if (els_cmd->els_code == FC_ELS_RSCN) { + bfa_fcs_port_scn_process_rscn(lport, fchs, len); + return; + } + + if (els_cmd->els_code == FC_ELS_LOGO) { + /** + * @todo Handle LOGO frames received. + */ + bfa_trc(lport->fcs, els_cmd->els_code); + return; + } + + if (els_cmd->els_code == FC_ELS_PRLI) { + /** + * @todo Handle PRLI frames received. + */ + bfa_trc(lport->fcs, els_cmd->els_code); + return; + } + + /** + * Unhandled ELS frames. Send a LS_RJT. + */ + bfa_fcs_port_send_ls_rjt(lport, fchs, FC_LS_RJT_RSN_CMD_NOT_SUPP, + FC_LS_RJT_EXP_NO_ADDL_INFO); + +} + +/** + * PID based Lookup for a R-Port in the Port R-Port Queue + */ +struct bfa_fcs_rport_s * +bfa_fcs_port_get_rport_by_pid(struct bfa_fcs_port_s *port, u32 pid) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe; + + list_for_each(qe, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + if (rport->pid == pid) + return rport; + } + + bfa_trc(port->fcs, pid); + return NULL; +} + +/** + * PWWN based Lookup for a R-Port in the Port R-Port Queue + */ +struct bfa_fcs_rport_s * +bfa_fcs_port_get_rport_by_pwwn(struct bfa_fcs_port_s *port, wwn_t pwwn) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe; + + list_for_each(qe, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + if (wwn_is_equal(rport->pwwn, pwwn)) + return rport; + } + + bfa_trc(port->fcs, pwwn); + return (NULL); +} + +/** + * NWWN based Lookup for a R-Port in the Port R-Port Queue + */ +struct bfa_fcs_rport_s * +bfa_fcs_port_get_rport_by_nwwn(struct bfa_fcs_port_s *port, wwn_t nwwn) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe; + + list_for_each(qe, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + if (wwn_is_equal(rport->nwwn, nwwn)) + return rport; + } + + bfa_trc(port->fcs, nwwn); + return (NULL); +} + +/** + * Called by rport module when new rports are discovered. + */ +void +bfa_fcs_port_add_rport(struct bfa_fcs_port_s *port, + struct bfa_fcs_rport_s *rport) +{ + list_add_tail(&rport->qe, &port->rport_q); + port->num_rports++; +} + +/** + * Called by rport module to when rports are deleted. + */ +void +bfa_fcs_port_del_rport(struct bfa_fcs_port_s *port, + struct bfa_fcs_rport_s *rport) +{ + bfa_assert(bfa_q_is_on_q(&port->rport_q, rport)); + list_del(&rport->qe); + port->num_rports--; + + bfa_sm_send_event(port, BFA_FCS_PORT_SM_DELRPORT); +} + +/** + * Called by fabric for base port when fabric login is complete. + * Called by vport for virtual ports when FDISC is complete. + */ +void +bfa_fcs_port_online(struct bfa_fcs_port_s *port) +{ + bfa_sm_send_event(port, BFA_FCS_PORT_SM_ONLINE); +} + +/** + * Called by fabric for base port when fabric goes offline. + * Called by vport for virtual ports when virtual port becomes offline. + */ +void +bfa_fcs_port_offline(struct bfa_fcs_port_s *port) +{ + bfa_sm_send_event(port, BFA_FCS_PORT_SM_OFFLINE); +} + +/** + * Called by fabric to delete base lport and associated resources. + * + * Called by vport to delete lport and associated resources. Should call + * bfa_fcs_vport_delete_comp() for vports on completion. + */ +void +bfa_fcs_port_delete(struct bfa_fcs_port_s *port) +{ + bfa_sm_send_event(port, BFA_FCS_PORT_SM_DELETE); +} + +/** + * Called by fabric in private loop topology to process LIP event. + */ +void +bfa_fcs_port_lip(struct bfa_fcs_port_s *port) +{ +} + +/** + * Return TRUE if port is online, else return FALSE + */ +bfa_boolean_t +bfa_fcs_port_is_online(struct bfa_fcs_port_s *port) +{ + return (bfa_sm_cmp_state(port, bfa_fcs_port_sm_online)); +} + +/** + * Logical port initialization of base or virtual port. + * Called by fabric for base port or by vport for virtual ports. + */ +void +bfa_fcs_lport_init(struct bfa_fcs_port_s *lport, struct bfa_fcs_s *fcs, + u16 vf_id, struct bfa_port_cfg_s *port_cfg, + struct bfa_fcs_vport_s *vport) +{ + lport->fcs = fcs; + lport->fabric = bfa_fcs_vf_lookup(fcs, vf_id); + bfa_os_assign(lport->port_cfg, *port_cfg); + lport->vport = vport; + lport->lp_tag = (vport) ? bfa_lps_get_tag(vport->lps) : + bfa_lps_get_tag(lport->fabric->lps); + + INIT_LIST_HEAD(&lport->rport_q); + lport->num_rports = 0; + + lport->bfad_port = + bfa_fcb_port_new(fcs->bfad, lport, lport->port_cfg.roles, + lport->fabric->vf_drv, + vport ? vport->vport_drv : NULL); + bfa_fcs_port_aen_post(lport, BFA_LPORT_AEN_NEW); + + bfa_sm_set_state(lport, bfa_fcs_port_sm_uninit); + bfa_sm_send_event(lport, BFA_FCS_PORT_SM_CREATE); +} + + + +/** + * fcs_lport_api + */ + +void +bfa_fcs_port_get_attr(struct bfa_fcs_port_s *port, + struct bfa_port_attr_s *port_attr) +{ + if (bfa_sm_cmp_state(port, bfa_fcs_port_sm_online)) + port_attr->pid = port->pid; + else + port_attr->pid = 0; + + port_attr->port_cfg = port->port_cfg; + + if (port->fabric) { + port_attr->port_type = bfa_fcs_fabric_port_type(port->fabric); + port_attr->loopback = bfa_fcs_fabric_is_loopback(port->fabric); + port_attr->fabric_name = bfa_fcs_port_get_fabric_name(port); + memcpy(port_attr->fabric_ip_addr, + bfa_fcs_port_get_fabric_ipaddr(port), + BFA_FCS_FABRIC_IPADDR_SZ); + + if (port->vport != NULL) + port_attr->port_type = BFA_PPORT_TYPE_VPORT; + + } else { + port_attr->port_type = BFA_PPORT_TYPE_UNKNOWN; + port_attr->state = BFA_PORT_UNINIT; + } + +} + + diff --git a/drivers/scsi/bfa/bfa_fcs_port.c b/drivers/scsi/bfa/bfa_fcs_port.c new file mode 100644 index 000000000000..9c4b24e62de1 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcs_port.c @@ -0,0 +1,68 @@ +/* + * 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. + */ + +/** + * bfa_fcs_pport.c BFA FCS PPORT ( physical port) + */ + +#include +#include +#include +#include "fcs_trcmod.h" +#include "fcs.h" +#include "fcs_fabric.h" +#include "fcs_port.h" + +BFA_TRC_FILE(FCS, PPORT); + +static void +bfa_fcs_pport_event_handler(void *cbarg, bfa_pport_event_t event) +{ + struct bfa_fcs_s *fcs = cbarg; + + bfa_trc(fcs, event); + + switch (event) { + case BFA_PPORT_LINKUP: + bfa_fcs_fabric_link_up(&fcs->fabric); + break; + + case BFA_PPORT_LINKDOWN: + bfa_fcs_fabric_link_down(&fcs->fabric); + break; + + case BFA_PPORT_TRUNK_LINKDOWN: + bfa_assert(0); + break; + + default: + bfa_assert(0); + } +} + +void +bfa_fcs_pport_modinit(struct bfa_fcs_s *fcs) +{ + bfa_pport_event_register(fcs->bfa, bfa_fcs_pport_event_handler, + fcs); +} + +void +bfa_fcs_pport_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} diff --git a/drivers/scsi/bfa/bfa_fcs_uf.c b/drivers/scsi/bfa/bfa_fcs_uf.c new file mode 100644 index 000000000000..ad01db6444b2 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcs_uf.c @@ -0,0 +1,105 @@ +/* + * 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. + */ + +/** + * bfa_fcs_uf.c BFA FCS UF ( Unsolicited Frames) + */ + +#include +#include +#include +#include "fcs.h" +#include "fcs_trcmod.h" +#include "fcs_fabric.h" +#include "fcs_uf.h" + +BFA_TRC_FILE(FCS, UF); + +/** + * BFA callback for unsolicited frame receive handler. + * + * @param[in] cbarg callback arg for receive handler + * @param[in] uf unsolicited frame descriptor + * + * @return None + */ +static void +bfa_fcs_uf_recv(void *cbarg, struct bfa_uf_s *uf) +{ + struct bfa_fcs_s *fcs = (struct bfa_fcs_s *) cbarg; + struct fchs_s *fchs = bfa_uf_get_frmbuf(uf); + u16 len = bfa_uf_get_frmlen(uf); + struct fc_vft_s *vft; + struct bfa_fcs_fabric_s *fabric; + + /** + * check for VFT header + */ + if (fchs->routing == FC_RTG_EXT_HDR && + fchs->cat_info == FC_CAT_VFT_HDR) { + bfa_stats(fcs, uf.tagged); + vft = bfa_uf_get_frmbuf(uf); + if (fcs->port_vfid == vft->vf_id) + fabric = &fcs->fabric; + else + fabric = bfa_fcs_vf_lookup(fcs, (u16) vft->vf_id); + + /** + * drop frame if vfid is unknown + */ + if (!fabric) { + bfa_assert(0); + bfa_stats(fcs, uf.vfid_unknown); + bfa_uf_free(uf); + return; + } + + /** + * skip vft header + */ + fchs = (struct fchs_s *) (vft + 1); + len -= sizeof(struct fc_vft_s); + + bfa_trc(fcs, vft->vf_id); + } else { + bfa_stats(fcs, uf.untagged); + fabric = &fcs->fabric; + } + + bfa_trc(fcs, ((u32 *) fchs)[0]); + bfa_trc(fcs, ((u32 *) fchs)[1]); + bfa_trc(fcs, ((u32 *) fchs)[2]); + bfa_trc(fcs, ((u32 *) fchs)[3]); + bfa_trc(fcs, ((u32 *) fchs)[4]); + bfa_trc(fcs, ((u32 *) fchs)[5]); + bfa_trc(fcs, len); + + bfa_fcs_fabric_uf_recv(fabric, fchs, len); + bfa_uf_free(uf); +} + +void +bfa_fcs_uf_modinit(struct bfa_fcs_s *fcs) +{ + bfa_uf_recv_register(fcs->bfa, bfa_fcs_uf_recv, fcs); +} + +void +bfa_fcs_uf_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} diff --git a/drivers/scsi/bfa/bfa_fcxp.c b/drivers/scsi/bfa/bfa_fcxp.c new file mode 100644 index 000000000000..4754a0e9006a --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcxp.c @@ -0,0 +1,782 @@ +/* + * 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. + */ + +#include +#include +#include + +BFA_TRC_FILE(HAL, FCXP); +BFA_MODULE(fcxp); + +/** + * forward declarations + */ +static void __bfa_fcxp_send_cbfn(void *cbarg, bfa_boolean_t complete); +static void hal_fcxp_rx_plog(struct bfa_s *bfa, struct bfa_fcxp_s *fcxp, + struct bfi_fcxp_send_rsp_s *fcxp_rsp); +static void hal_fcxp_tx_plog(struct bfa_s *bfa, u32 reqlen, + struct bfa_fcxp_s *fcxp, struct fchs_s *fchs); +static void bfa_fcxp_qresume(void *cbarg); +static void bfa_fcxp_queue(struct bfa_fcxp_s *fcxp, + struct bfi_fcxp_send_req_s *send_req); + +/** + * fcxp_pvt BFA FCXP private functions + */ + +static void +claim_fcxp_req_rsp_mem(struct bfa_fcxp_mod_s *mod, struct bfa_meminfo_s *mi) +{ + u8 *dm_kva = NULL; + u64 dm_pa; + u32 buf_pool_sz; + + dm_kva = bfa_meminfo_dma_virt(mi); + dm_pa = bfa_meminfo_dma_phys(mi); + + buf_pool_sz = mod->req_pld_sz * mod->num_fcxps; + + /* + * Initialize the fcxp req payload list + */ + mod->req_pld_list_kva = dm_kva; + mod->req_pld_list_pa = dm_pa; + dm_kva += buf_pool_sz; + dm_pa += buf_pool_sz; + bfa_os_memset(mod->req_pld_list_kva, 0, buf_pool_sz); + + /* + * Initialize the fcxp rsp payload list + */ + buf_pool_sz = mod->rsp_pld_sz * mod->num_fcxps; + mod->rsp_pld_list_kva = dm_kva; + mod->rsp_pld_list_pa = dm_pa; + dm_kva += buf_pool_sz; + dm_pa += buf_pool_sz; + bfa_os_memset(mod->rsp_pld_list_kva, 0, buf_pool_sz); + + bfa_meminfo_dma_virt(mi) = dm_kva; + bfa_meminfo_dma_phys(mi) = dm_pa; +} + +static void +claim_fcxps_mem(struct bfa_fcxp_mod_s *mod, struct bfa_meminfo_s *mi) +{ + u16 i; + struct bfa_fcxp_s *fcxp; + + fcxp = (struct bfa_fcxp_s *) bfa_meminfo_kva(mi); + bfa_os_memset(fcxp, 0, sizeof(struct bfa_fcxp_s) * mod->num_fcxps); + + INIT_LIST_HEAD(&mod->fcxp_free_q); + INIT_LIST_HEAD(&mod->fcxp_active_q); + + mod->fcxp_list = fcxp; + + for (i = 0; i < mod->num_fcxps; i++) { + fcxp->fcxp_mod = mod; + fcxp->fcxp_tag = i; + + list_add_tail(&fcxp->qe, &mod->fcxp_free_q); + bfa_reqq_winit(&fcxp->reqq_wqe, bfa_fcxp_qresume, fcxp); + fcxp->reqq_waiting = BFA_FALSE; + + fcxp = fcxp + 1; + } + + bfa_meminfo_kva(mi) = (void *)fcxp; +} + +static void +bfa_fcxp_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, + u32 *dm_len) +{ + u16 num_fcxp_reqs = cfg->fwcfg.num_fcxp_reqs; + + if (num_fcxp_reqs == 0) + return; + + /* + * Account for req/rsp payload + */ + *dm_len += BFA_FCXP_MAX_IBUF_SZ * num_fcxp_reqs; + if (cfg->drvcfg.min_cfg) + *dm_len += BFA_FCXP_MAX_IBUF_SZ * num_fcxp_reqs; + else + *dm_len += BFA_FCXP_MAX_LBUF_SZ * num_fcxp_reqs; + + /* + * Account for fcxp structs + */ + *ndm_len += sizeof(struct bfa_fcxp_s) * num_fcxp_reqs; +} + +static void +bfa_fcxp_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + + bfa_os_memset(mod, 0, sizeof(struct bfa_fcxp_mod_s)); + mod->bfa = bfa; + mod->num_fcxps = cfg->fwcfg.num_fcxp_reqs; + + /** + * Initialize FCXP request and response payload sizes. + */ + mod->req_pld_sz = mod->rsp_pld_sz = BFA_FCXP_MAX_IBUF_SZ; + if (!cfg->drvcfg.min_cfg) + mod->rsp_pld_sz = BFA_FCXP_MAX_LBUF_SZ; + + INIT_LIST_HEAD(&mod->wait_q); + + claim_fcxp_req_rsp_mem(mod, meminfo); + claim_fcxps_mem(mod, meminfo); +} + +static void +bfa_fcxp_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_fcxp_detach(struct bfa_s *bfa) +{ +} + +static void +bfa_fcxp_start(struct bfa_s *bfa) +{ +} + +static void +bfa_fcxp_stop(struct bfa_s *bfa) +{ +} + +static void +bfa_fcxp_iocdisable(struct bfa_s *bfa) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + struct bfa_fcxp_s *fcxp; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &mod->fcxp_active_q) { + fcxp = (struct bfa_fcxp_s *) qe; + if (fcxp->caller == NULL) { + fcxp->send_cbfn(fcxp->caller, fcxp, fcxp->send_cbarg, + BFA_STATUS_IOC_FAILURE, 0, 0, NULL); + bfa_fcxp_free(fcxp); + } else { + fcxp->rsp_status = BFA_STATUS_IOC_FAILURE; + bfa_cb_queue(bfa, &fcxp->hcb_qe, + __bfa_fcxp_send_cbfn, fcxp); + } + } +} + +static struct bfa_fcxp_s * +bfa_fcxp_get(struct bfa_fcxp_mod_s *fm) +{ + struct bfa_fcxp_s *fcxp; + + bfa_q_deq(&fm->fcxp_free_q, &fcxp); + + if (fcxp) + list_add_tail(&fcxp->qe, &fm->fcxp_active_q); + + return (fcxp); +} + +static void +bfa_fcxp_put(struct bfa_fcxp_s *fcxp) +{ + struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod; + struct bfa_fcxp_wqe_s *wqe; + + bfa_q_deq(&mod->wait_q, &wqe); + if (wqe) { + bfa_trc(mod->bfa, fcxp->fcxp_tag); + wqe->alloc_cbfn(wqe->alloc_cbarg, fcxp); + return; + } + + bfa_assert(bfa_q_is_on_q(&mod->fcxp_active_q, fcxp)); + list_del(&fcxp->qe); + list_add_tail(&fcxp->qe, &mod->fcxp_free_q); +} + +static void +bfa_fcxp_null_comp(void *bfad_fcxp, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + /**discarded fcxp completion */ +} + +static void +__bfa_fcxp_send_cbfn(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_fcxp_s *fcxp = cbarg; + + if (complete) { + fcxp->send_cbfn(fcxp->caller, fcxp, fcxp->send_cbarg, + fcxp->rsp_status, fcxp->rsp_len, + fcxp->residue_len, &fcxp->rsp_fchs); + } else { + bfa_fcxp_free(fcxp); + } +} + +static void +hal_fcxp_send_comp(struct bfa_s *bfa, struct bfi_fcxp_send_rsp_s *fcxp_rsp) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + struct bfa_fcxp_s *fcxp; + u16 fcxp_tag = bfa_os_ntohs(fcxp_rsp->fcxp_tag); + + bfa_trc(bfa, fcxp_tag); + + fcxp_rsp->rsp_len = bfa_os_ntohl(fcxp_rsp->rsp_len); + + /** + * @todo f/w should not set residue to non-0 when everything + * is received. + */ + if (fcxp_rsp->req_status == BFA_STATUS_OK) + fcxp_rsp->residue_len = 0; + else + fcxp_rsp->residue_len = bfa_os_ntohl(fcxp_rsp->residue_len); + + fcxp = BFA_FCXP_FROM_TAG(mod, fcxp_tag); + + bfa_assert(fcxp->send_cbfn != NULL); + + hal_fcxp_rx_plog(mod->bfa, fcxp, fcxp_rsp); + + if (fcxp->send_cbfn != NULL) { + if (fcxp->caller == NULL) { + bfa_trc(mod->bfa, fcxp->fcxp_tag); + + fcxp->send_cbfn(fcxp->caller, fcxp, fcxp->send_cbarg, + fcxp_rsp->req_status, fcxp_rsp->rsp_len, + fcxp_rsp->residue_len, &fcxp_rsp->fchs); + /* + * fcxp automatically freed on return from the callback + */ + bfa_fcxp_free(fcxp); + } else { + bfa_trc(mod->bfa, fcxp->fcxp_tag); + fcxp->rsp_status = fcxp_rsp->req_status; + fcxp->rsp_len = fcxp_rsp->rsp_len; + fcxp->residue_len = fcxp_rsp->residue_len; + fcxp->rsp_fchs = fcxp_rsp->fchs; + + bfa_cb_queue(bfa, &fcxp->hcb_qe, + __bfa_fcxp_send_cbfn, fcxp); + } + } else { + bfa_trc(bfa, fcxp_tag); + } +} + +static void +hal_fcxp_set_local_sges(struct bfi_sge_s *sge, u32 reqlen, u64 req_pa) +{ + union bfi_addr_u sga_zero = { {0} }; + + sge->sg_len = reqlen; + sge->flags = BFI_SGE_DATA_LAST; + bfa_dma_addr_set(sge[0].sga, req_pa); + bfa_sge_to_be(sge); + sge++; + + sge->sga = sga_zero; + sge->sg_len = reqlen; + sge->flags = BFI_SGE_PGDLEN; + bfa_sge_to_be(sge); +} + +static void +hal_fcxp_tx_plog(struct bfa_s *bfa, u32 reqlen, struct bfa_fcxp_s *fcxp, + struct fchs_s *fchs) +{ + /* + * TODO: TX ox_id + */ + if (reqlen > 0) { + if (fcxp->use_ireqbuf) { + u32 pld_w0 = + *((u32 *) BFA_FCXP_REQ_PLD(fcxp)); + + bfa_plog_fchdr_and_pl(bfa->plog, BFA_PL_MID_HAL_FCXP, + BFA_PL_EID_TX, + reqlen + sizeof(struct fchs_s), fchs, pld_w0); + } else { + bfa_plog_fchdr(bfa->plog, BFA_PL_MID_HAL_FCXP, + BFA_PL_EID_TX, reqlen + sizeof(struct fchs_s), + fchs); + } + } else { + bfa_plog_fchdr(bfa->plog, BFA_PL_MID_HAL_FCXP, BFA_PL_EID_TX, + reqlen + sizeof(struct fchs_s), fchs); + } +} + +static void +hal_fcxp_rx_plog(struct bfa_s *bfa, struct bfa_fcxp_s *fcxp, + struct bfi_fcxp_send_rsp_s *fcxp_rsp) +{ + if (fcxp_rsp->rsp_len > 0) { + if (fcxp->use_irspbuf) { + u32 pld_w0 = + *((u32 *) BFA_FCXP_RSP_PLD(fcxp)); + + bfa_plog_fchdr_and_pl(bfa->plog, BFA_PL_MID_HAL_FCXP, + BFA_PL_EID_RX, + (u16) fcxp_rsp->rsp_len, + &fcxp_rsp->fchs, pld_w0); + } else { + bfa_plog_fchdr(bfa->plog, BFA_PL_MID_HAL_FCXP, + BFA_PL_EID_RX, + (u16) fcxp_rsp->rsp_len, + &fcxp_rsp->fchs); + } + } else { + bfa_plog_fchdr(bfa->plog, BFA_PL_MID_HAL_FCXP, BFA_PL_EID_RX, + (u16) fcxp_rsp->rsp_len, &fcxp_rsp->fchs); + } +} + +/** + * Handler to resume sending fcxp when space in available in cpe queue. + */ +static void +bfa_fcxp_qresume(void *cbarg) +{ + struct bfa_fcxp_s *fcxp = cbarg; + struct bfa_s *bfa = fcxp->fcxp_mod->bfa; + struct bfi_fcxp_send_req_s *send_req; + + fcxp->reqq_waiting = BFA_FALSE; + send_req = bfa_reqq_next(bfa, BFA_REQQ_FCXP); + bfa_fcxp_queue(fcxp, send_req); +} + +/** + * Queue fcxp send request to foimrware. + */ +static void +bfa_fcxp_queue(struct bfa_fcxp_s *fcxp, struct bfi_fcxp_send_req_s *send_req) +{ + struct bfa_s *bfa = fcxp->fcxp_mod->bfa; + struct bfa_fcxp_req_info_s *reqi = &fcxp->req_info; + struct bfa_fcxp_rsp_info_s *rspi = &fcxp->rsp_info; + struct bfa_rport_s *rport = reqi->bfa_rport; + + bfi_h2i_set(send_req->mh, BFI_MC_FCXP, BFI_FCXP_H2I_SEND_REQ, + bfa_lpuid(bfa)); + + send_req->fcxp_tag = bfa_os_htons(fcxp->fcxp_tag); + if (rport) { + send_req->rport_fw_hndl = rport->fw_handle; + send_req->max_frmsz = bfa_os_htons(rport->rport_info.max_frmsz); + if (send_req->max_frmsz == 0) + send_req->max_frmsz = bfa_os_htons(FC_MAX_PDUSZ); + } else { + send_req->rport_fw_hndl = 0; + send_req->max_frmsz = bfa_os_htons(FC_MAX_PDUSZ); + } + + send_req->vf_id = bfa_os_htons(reqi->vf_id); + send_req->lp_tag = reqi->lp_tag; + send_req->class = reqi->class; + send_req->rsp_timeout = rspi->rsp_timeout; + send_req->cts = reqi->cts; + send_req->fchs = reqi->fchs; + + send_req->req_len = bfa_os_htonl(reqi->req_tot_len); + send_req->rsp_maxlen = bfa_os_htonl(rspi->rsp_maxlen); + + /* + * setup req sgles + */ + if (fcxp->use_ireqbuf == 1) { + hal_fcxp_set_local_sges(send_req->req_sge, reqi->req_tot_len, + BFA_FCXP_REQ_PLD_PA(fcxp)); + } else { + if (fcxp->nreq_sgles > 0) { + bfa_assert(fcxp->nreq_sgles == 1); + hal_fcxp_set_local_sges(send_req->req_sge, + reqi->req_tot_len, + fcxp->req_sga_cbfn(fcxp->caller, + 0)); + } else { + bfa_assert(reqi->req_tot_len == 0); + hal_fcxp_set_local_sges(send_req->rsp_sge, 0, 0); + } + } + + /* + * setup rsp sgles + */ + if (fcxp->use_irspbuf == 1) { + bfa_assert(rspi->rsp_maxlen <= BFA_FCXP_MAX_LBUF_SZ); + + hal_fcxp_set_local_sges(send_req->rsp_sge, rspi->rsp_maxlen, + BFA_FCXP_RSP_PLD_PA(fcxp)); + + } else { + if (fcxp->nrsp_sgles > 0) { + bfa_assert(fcxp->nrsp_sgles == 1); + hal_fcxp_set_local_sges(send_req->rsp_sge, + rspi->rsp_maxlen, + fcxp->rsp_sga_cbfn(fcxp->caller, + 0)); + } else { + bfa_assert(rspi->rsp_maxlen == 0); + hal_fcxp_set_local_sges(send_req->rsp_sge, 0, 0); + } + } + + hal_fcxp_tx_plog(bfa, reqi->req_tot_len, fcxp, &reqi->fchs); + + bfa_reqq_produce(bfa, BFA_REQQ_FCXP); + + bfa_trc(bfa, bfa_reqq_pi(bfa, BFA_REQQ_FCXP)); + bfa_trc(bfa, bfa_reqq_ci(bfa, BFA_REQQ_FCXP)); +} + + +/** + * hal_fcxp_api BFA FCXP API + */ + +/** + * Allocate an FCXP instance to send a response or to send a request + * that has a response. Request/response buffers are allocated by caller. + * + * @param[in] bfa BFA bfa instance + * @param[in] nreq_sgles Number of SG elements required for request + * buffer. 0, if fcxp internal buffers are used. + * Use bfa_fcxp_get_reqbuf() to get the + * internal req buffer. + * @param[in] req_sgles SG elements describing request buffer. Will be + * copied in by BFA and hence can be freed on + * return from this function. + * @param[in] get_req_sga function ptr to be called to get a request SG + * Address (given the sge index). + * @param[in] get_req_sglen function ptr to be called to get a request SG + * len (given the sge index). + * @param[in] get_rsp_sga function ptr to be called to get a response SG + * Address (given the sge index). + * @param[in] get_rsp_sglen function ptr to be called to get a response SG + * len (given the sge index). + * + * @return FCXP instance. NULL on failure. + */ +struct bfa_fcxp_s * +bfa_fcxp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles, + int nrsp_sgles, bfa_fcxp_get_sgaddr_t req_sga_cbfn, + bfa_fcxp_get_sglen_t req_sglen_cbfn, + bfa_fcxp_get_sgaddr_t rsp_sga_cbfn, + bfa_fcxp_get_sglen_t rsp_sglen_cbfn) +{ + struct bfa_fcxp_s *fcxp = NULL; + u32 nreq_sgpg, nrsp_sgpg; + + bfa_assert(bfa != NULL); + + fcxp = bfa_fcxp_get(BFA_FCXP_MOD(bfa)); + if (fcxp == NULL) + return (NULL); + + bfa_trc(bfa, fcxp->fcxp_tag); + + fcxp->caller = caller; + + if (nreq_sgles == 0) { + fcxp->use_ireqbuf = 1; + } else { + bfa_assert(req_sga_cbfn != NULL); + bfa_assert(req_sglen_cbfn != NULL); + + fcxp->use_ireqbuf = 0; + fcxp->req_sga_cbfn = req_sga_cbfn; + fcxp->req_sglen_cbfn = req_sglen_cbfn; + + fcxp->nreq_sgles = nreq_sgles; + + /* + * alloc required sgpgs + */ + if (nreq_sgles > BFI_SGE_INLINE) { + nreq_sgpg = BFA_SGPG_NPAGE(nreq_sgles); + + if (bfa_sgpg_malloc + (bfa, &fcxp->req_sgpg_q, nreq_sgpg) + != BFA_STATUS_OK) { + /* bfa_sgpg_wait(bfa, &fcxp->req_sgpg_wqe, + nreq_sgpg); */ + /* + * TODO + */ + } + } + } + + if (nrsp_sgles == 0) { + fcxp->use_irspbuf = 1; + } else { + bfa_assert(rsp_sga_cbfn != NULL); + bfa_assert(rsp_sglen_cbfn != NULL); + + fcxp->use_irspbuf = 0; + fcxp->rsp_sga_cbfn = rsp_sga_cbfn; + fcxp->rsp_sglen_cbfn = rsp_sglen_cbfn; + + fcxp->nrsp_sgles = nrsp_sgles; + /* + * alloc required sgpgs + */ + if (nrsp_sgles > BFI_SGE_INLINE) { + nrsp_sgpg = BFA_SGPG_NPAGE(nreq_sgles); + + if (bfa_sgpg_malloc + (bfa, &fcxp->rsp_sgpg_q, nrsp_sgpg) + != BFA_STATUS_OK) { + /* bfa_sgpg_wait(bfa, &fcxp->rsp_sgpg_wqe, + nrsp_sgpg); */ + /* + * TODO + */ + } + } + } + + return (fcxp); +} + +/** + * Get the internal request buffer pointer + * + * @param[in] fcxp BFA fcxp pointer + * + * @return pointer to the internal request buffer + */ +void * +bfa_fcxp_get_reqbuf(struct bfa_fcxp_s *fcxp) +{ + struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod; + void *reqbuf; + + bfa_assert(fcxp->use_ireqbuf == 1); + reqbuf = ((u8 *)mod->req_pld_list_kva) + + fcxp->fcxp_tag * mod->req_pld_sz; + return reqbuf; +} + +u32 +bfa_fcxp_get_reqbufsz(struct bfa_fcxp_s *fcxp) +{ + struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod; + + return mod->req_pld_sz; +} + +/** + * Get the internal response buffer pointer + * + * @param[in] fcxp BFA fcxp pointer + * + * @return pointer to the internal request buffer + */ +void * +bfa_fcxp_get_rspbuf(struct bfa_fcxp_s *fcxp) +{ + struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod; + void *rspbuf; + + bfa_assert(fcxp->use_irspbuf == 1); + + rspbuf = ((u8 *)mod->rsp_pld_list_kva) + + fcxp->fcxp_tag * mod->rsp_pld_sz; + return rspbuf; +} + +/** + * Free the BFA FCXP + * + * @param[in] fcxp BFA fcxp pointer + * + * @return void + */ +void +bfa_fcxp_free(struct bfa_fcxp_s *fcxp) +{ + struct bfa_fcxp_mod_s *mod = fcxp->fcxp_mod; + + bfa_assert(fcxp != NULL); + bfa_trc(mod->bfa, fcxp->fcxp_tag); + bfa_fcxp_put(fcxp); +} + +/** + * Send a FCXP request + * + * @param[in] fcxp BFA fcxp pointer + * @param[in] rport BFA rport pointer. Could be left NULL for WKA rports + * @param[in] vf_id virtual Fabric ID + * @param[in] lp_tag lport tag + * @param[in] cts use Continous sequence + * @param[in] cos fc Class of Service + * @param[in] reqlen request length, does not include FCHS length + * @param[in] fchs fc Header Pointer. The header content will be copied + * in by BFA. + * + * @param[in] cbfn call back function to be called on receiving + * the response + * @param[in] cbarg arg for cbfn + * @param[in] rsp_timeout + * response timeout + * + * @return bfa_status_t + */ +void +bfa_fcxp_send(struct bfa_fcxp_s *fcxp, struct bfa_rport_s *rport, + u16 vf_id, u8 lp_tag, bfa_boolean_t cts, enum fc_cos cos, + u32 reqlen, struct fchs_s *fchs, bfa_cb_fcxp_send_t cbfn, + void *cbarg, u32 rsp_maxlen, u8 rsp_timeout) +{ + struct bfa_s *bfa = fcxp->fcxp_mod->bfa; + struct bfa_fcxp_req_info_s *reqi = &fcxp->req_info; + struct bfa_fcxp_rsp_info_s *rspi = &fcxp->rsp_info; + struct bfi_fcxp_send_req_s *send_req; + + bfa_trc(bfa, fcxp->fcxp_tag); + + /** + * setup request/response info + */ + reqi->bfa_rport = rport; + reqi->vf_id = vf_id; + reqi->lp_tag = lp_tag; + reqi->class = cos; + rspi->rsp_timeout = rsp_timeout; + reqi->cts = cts; + reqi->fchs = *fchs; + reqi->req_tot_len = reqlen; + rspi->rsp_maxlen = rsp_maxlen; + fcxp->send_cbfn = cbfn ? cbfn : bfa_fcxp_null_comp; + fcxp->send_cbarg = cbarg; + + /** + * If no room in CPE queue, wait for + */ + send_req = bfa_reqq_next(bfa, BFA_REQQ_FCXP); + if (!send_req) { + bfa_trc(bfa, fcxp->fcxp_tag); + fcxp->reqq_waiting = BFA_TRUE; + bfa_reqq_wait(bfa, BFA_REQQ_FCXP, &fcxp->reqq_wqe); + return; + } + + bfa_fcxp_queue(fcxp, send_req); +} + +/** + * Abort a BFA FCXP + * + * @param[in] fcxp BFA fcxp pointer + * + * @return void + */ +bfa_status_t +bfa_fcxp_abort(struct bfa_fcxp_s *fcxp) +{ + bfa_assert(0); + return (BFA_STATUS_OK); +} + +void +bfa_fcxp_alloc_wait(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe, + bfa_fcxp_alloc_cbfn_t alloc_cbfn, void *alloc_cbarg) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + + bfa_assert(list_empty(&mod->fcxp_free_q)); + + wqe->alloc_cbfn = alloc_cbfn; + wqe->alloc_cbarg = alloc_cbarg; + list_add_tail(&wqe->qe, &mod->wait_q); +} + +void +bfa_fcxp_walloc_cancel(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + + bfa_assert(bfa_q_is_on_q(&mod->wait_q, wqe)); + list_del(&wqe->qe); +} + +void +bfa_fcxp_discard(struct bfa_fcxp_s *fcxp) +{ + /** + * If waiting for room in request queue, cancel reqq wait + * and free fcxp. + */ + if (fcxp->reqq_waiting) { + fcxp->reqq_waiting = BFA_FALSE; + bfa_reqq_wcancel(&fcxp->reqq_wqe); + bfa_fcxp_free(fcxp); + return; + } + + fcxp->send_cbfn = bfa_fcxp_null_comp; +} + + + +/** + * hal_fcxp_public BFA FCXP public functions + */ + +void +bfa_fcxp_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) +{ + switch (msg->mhdr.msg_id) { + case BFI_FCXP_I2H_SEND_RSP: + hal_fcxp_send_comp(bfa, (struct bfi_fcxp_send_rsp_s *) msg); + break; + + default: + bfa_trc(bfa, msg->mhdr.msg_id); + bfa_assert(0); + } +} + +u32 +bfa_fcxp_get_maxrsp(struct bfa_s *bfa) +{ + struct bfa_fcxp_mod_s *mod = BFA_FCXP_MOD(bfa); + + return mod->rsp_pld_sz; +} + + diff --git a/drivers/scsi/bfa/bfa_fcxp_priv.h b/drivers/scsi/bfa/bfa_fcxp_priv.h new file mode 100644 index 000000000000..4cda49397da0 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fcxp_priv.h @@ -0,0 +1,138 @@ +/* + * 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. + */ + +#ifndef __BFA_FCXP_PRIV_H__ +#define __BFA_FCXP_PRIV_H__ + +#include +#include +#include +#include + +#define BFA_FCXP_MIN (1) +#define BFA_FCXP_MAX_IBUF_SZ (2 * 1024 + 256) +#define BFA_FCXP_MAX_LBUF_SZ (4 * 1024 + 256) + +struct bfa_fcxp_mod_s { + struct bfa_s *bfa; /* backpointer to BFA */ + struct bfa_fcxp_s *fcxp_list; /* array of FCXPs */ + u16 num_fcxps; /* max num FCXP requests */ + struct list_head fcxp_free_q; /* free FCXPs */ + struct list_head fcxp_active_q; /* active FCXPs */ + void *req_pld_list_kva; /* list of FCXP req pld */ + u64 req_pld_list_pa; /* list of FCXP req pld */ + void *rsp_pld_list_kva; /* list of FCXP resp pld */ + u64 rsp_pld_list_pa; /* list of FCXP resp pld */ + struct list_head wait_q; /* wait queue for free fcxp */ + u32 req_pld_sz; + u32 rsp_pld_sz; +}; + +#define BFA_FCXP_MOD(__bfa) (&(__bfa)->modules.fcxp_mod) +#define BFA_FCXP_FROM_TAG(__mod, __tag) (&(__mod)->fcxp_list[__tag]) + +typedef void (*fcxp_send_cb_t) (struct bfa_s *ioc, struct bfa_fcxp_s *fcxp, + void *cb_arg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs); + +/** + * Information needed for a FCXP request + */ +struct bfa_fcxp_req_info_s { + struct bfa_rport_s *bfa_rport; /* Pointer to the bfa rport that was + *returned from bfa_rport_create(). + *This could be left NULL for WKA or for + *FCXP interactions before the rport + *nexus is established + */ + struct fchs_s fchs; /* request FC header structure */ + u8 cts; /* continous sequence */ + u8 class; /* FC class for the request/response */ + u16 max_frmsz; /* max send frame size */ + u16 vf_id; /* vsan tag if applicable */ + u8 lp_tag; /* lport tag */ + u32 req_tot_len; /* request payload total length */ +}; + +struct bfa_fcxp_rsp_info_s { + struct fchs_s rsp_fchs; /* Response frame's FC header will + * be *sent back in this field */ + u8 rsp_timeout; /* timeout in seconds, 0-no response + */ + u8 rsvd2[3]; + u32 rsp_maxlen; /* max response length expected */ +}; + +struct bfa_fcxp_s { + struct list_head qe; /* fcxp queue element */ + bfa_sm_t sm; /* state machine */ + void *caller; /* driver or fcs */ + struct bfa_fcxp_mod_s *fcxp_mod; + /* back pointer to fcxp mod */ + u16 fcxp_tag; /* internal tag */ + struct bfa_fcxp_req_info_s req_info; + /* request info */ + struct bfa_fcxp_rsp_info_s rsp_info; + /* response info */ + u8 use_ireqbuf; /* use internal req buf */ + u8 use_irspbuf; /* use internal rsp buf */ + u32 nreq_sgles; /* num request SGLEs */ + u32 nrsp_sgles; /* num response SGLEs */ + struct list_head req_sgpg_q; /* SG pages for request buf */ + struct list_head req_sgpg_wqe; /* wait queue for req SG page */ + struct list_head rsp_sgpg_q; /* SG pages for response buf */ + struct list_head rsp_sgpg_wqe; /* wait queue for rsp SG page */ + + bfa_fcxp_get_sgaddr_t req_sga_cbfn; + /* SG elem addr user function */ + bfa_fcxp_get_sglen_t req_sglen_cbfn; + /* SG elem len user function */ + bfa_fcxp_get_sgaddr_t rsp_sga_cbfn; + /* SG elem addr user function */ + bfa_fcxp_get_sglen_t rsp_sglen_cbfn; + /* SG elem len user function */ + bfa_cb_fcxp_send_t send_cbfn; /* send completion callback */ + void *send_cbarg; /* callback arg */ + struct bfa_sge_s req_sge[BFA_FCXP_MAX_SGES]; + /* req SG elems */ + struct bfa_sge_s rsp_sge[BFA_FCXP_MAX_SGES]; + /* rsp SG elems */ + u8 rsp_status; /* comp: rsp status */ + u32 rsp_len; /* comp: actual response len */ + u32 residue_len; /* comp: residual rsp length */ + struct fchs_s rsp_fchs; /* comp: response fchs */ + struct bfa_cb_qe_s hcb_qe; /* comp: callback qelem */ + struct bfa_reqq_wait_s reqq_wqe; + bfa_boolean_t reqq_waiting; +}; + +#define BFA_FCXP_REQ_PLD(_fcxp) (bfa_fcxp_get_reqbuf(_fcxp)) + +#define BFA_FCXP_RSP_FCHS(_fcxp) (&((_fcxp)->rsp_info.fchs)) +#define BFA_FCXP_RSP_PLD(_fcxp) (bfa_fcxp_get_rspbuf(_fcxp)) + +#define BFA_FCXP_REQ_PLD_PA(_fcxp) \ + ((_fcxp)->fcxp_mod->req_pld_list_pa + \ + ((_fcxp)->fcxp_mod->req_pld_sz * (_fcxp)->fcxp_tag)) + +#define BFA_FCXP_RSP_PLD_PA(_fcxp) \ + ((_fcxp)->fcxp_mod->rsp_pld_list_pa + \ + ((_fcxp)->fcxp_mod->rsp_pld_sz * (_fcxp)->fcxp_tag)) + +void bfa_fcxp_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +#endif /* __BFA_FCXP_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_fwimg_priv.h b/drivers/scsi/bfa/bfa_fwimg_priv.h new file mode 100644 index 000000000000..1ec1355924d9 --- /dev/null +++ b/drivers/scsi/bfa/bfa_fwimg_priv.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#ifndef __BFA_FWIMG_PRIV_H__ +#define __BFA_FWIMG_PRIV_H__ + +#define BFI_FLASH_CHUNK_SZ 256 /* Flash chunk size */ +#define BFI_FLASH_CHUNK_SZ_WORDS (BFI_FLASH_CHUNK_SZ/sizeof(u32)) + +extern u32 *bfi_image_ct_get_chunk(u32 off); +extern u32 bfi_image_ct_size; +extern u32 *bfi_image_cb_get_chunk(u32 off); +extern u32 bfi_image_cb_size; +extern u32 *bfi_image_cb; +extern u32 *bfi_image_ct; + +#endif /* __BFA_FWIMG_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_hw_cb.c b/drivers/scsi/bfa/bfa_hw_cb.c new file mode 100644 index 000000000000..ede1438619e2 --- /dev/null +++ b/drivers/scsi/bfa/bfa_hw_cb.c @@ -0,0 +1,142 @@ +/* + * 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. + */ + +#include +#include + +void +bfa_hwcb_reginit(struct bfa_s *bfa) +{ + struct bfa_iocfc_regs_s *bfa_regs = &bfa->iocfc.bfa_regs; + bfa_os_addr_t kva = bfa_ioc_bar0(&bfa->ioc); + int i, q, fn = bfa_ioc_pcifn(&bfa->ioc); + + if (fn == 0) { + bfa_regs->intr_status = (kva + HOSTFN0_INT_STATUS); + bfa_regs->intr_mask = (kva + HOSTFN0_INT_MSK); + } else { + bfa_regs->intr_status = (kva + HOSTFN1_INT_STATUS); + bfa_regs->intr_mask = (kva + HOSTFN1_INT_MSK); + } + + for (i = 0; i < BFI_IOC_MAX_CQS; i++) { + /* + * CPE registers + */ + q = CPE_Q_NUM(fn, i); + bfa_regs->cpe_q_pi[i] = (kva + CPE_Q_PI(q)); + bfa_regs->cpe_q_ci[i] = (kva + CPE_Q_CI(q)); + bfa_regs->cpe_q_depth[i] = (kva + CPE_Q_DEPTH(q)); + + /* + * RME registers + */ + q = CPE_Q_NUM(fn, i); + bfa_regs->rme_q_pi[i] = (kva + RME_Q_PI(q)); + bfa_regs->rme_q_ci[i] = (kva + RME_Q_CI(q)); + bfa_regs->rme_q_depth[i] = (kva + RME_Q_DEPTH(q)); + } +} + +void +bfa_hwcb_rspq_ack(struct bfa_s *bfa, int rspq) +{ +} + +static void +bfa_hwcb_rspq_ack_msix(struct bfa_s *bfa, int rspq) +{ + bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, + __HFN_INT_RME_Q0 << RME_Q_NUM(bfa_ioc_pcifn(&bfa->ioc), rspq)); +} + +void +bfa_hwcb_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap, + u32 *num_vecs, u32 *max_vec_bit) +{ +#define __HFN_NUMINTS 13 + if (bfa_ioc_pcifn(&bfa->ioc) == 0) { + *msix_vecs_bmap = (__HFN_INT_CPE_Q0 | __HFN_INT_CPE_Q1 | + __HFN_INT_CPE_Q2 | __HFN_INT_CPE_Q3 | + __HFN_INT_RME_Q0 | __HFN_INT_RME_Q1 | + __HFN_INT_RME_Q2 | __HFN_INT_RME_Q3 | + __HFN_INT_MBOX_LPU0); + *max_vec_bit = __HFN_INT_MBOX_LPU0; + } else { + *msix_vecs_bmap = (__HFN_INT_CPE_Q4 | __HFN_INT_CPE_Q5 | + __HFN_INT_CPE_Q6 | __HFN_INT_CPE_Q7 | + __HFN_INT_RME_Q4 | __HFN_INT_RME_Q5 | + __HFN_INT_RME_Q6 | __HFN_INT_RME_Q7 | + __HFN_INT_MBOX_LPU1); + *max_vec_bit = __HFN_INT_MBOX_LPU1; + } + + *msix_vecs_bmap |= (__HFN_INT_ERR_EMC | __HFN_INT_ERR_LPU0 | + __HFN_INT_ERR_LPU1 | __HFN_INT_ERR_PSS); + *num_vecs = __HFN_NUMINTS; +} + +/** + * No special setup required for crossbow -- vector assignments are implicit. + */ +void +bfa_hwcb_msix_init(struct bfa_s *bfa, int nvecs) +{ + int i; + + bfa_assert((nvecs == 1) || (nvecs == __HFN_NUMINTS)); + + bfa->msix.nvecs = nvecs; + if (nvecs == 1) { + for (i = 0; i < BFA_MSIX_CB_MAX; i++) + bfa->msix.handler[i] = bfa_msix_all; + return; + } + + for (i = BFA_MSIX_CPE_Q0; i <= BFA_MSIX_CPE_Q7; i++) + bfa->msix.handler[i] = bfa_msix_reqq; + + for (i = BFA_MSIX_RME_Q0; i <= BFA_MSIX_RME_Q7; i++) + bfa->msix.handler[i] = bfa_msix_rspq; + + for (; i < BFA_MSIX_CB_MAX; i++) + bfa->msix.handler[i] = bfa_msix_lpu_err; +} + +/** + * Crossbow -- dummy, interrupts are masked + */ +void +bfa_hwcb_msix_install(struct bfa_s *bfa) +{ +} + +void +bfa_hwcb_msix_uninstall(struct bfa_s *bfa) +{ +} + +/** + * No special enable/disable -- vector assignments are implicit. + */ +void +bfa_hwcb_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix) +{ + bfa->iocfc.hwif.hw_rspq_ack = bfa_hwcb_rspq_ack_msix; +} + + diff --git a/drivers/scsi/bfa/bfa_hw_ct.c b/drivers/scsi/bfa/bfa_hw_ct.c new file mode 100644 index 000000000000..51ae5740e6e9 --- /dev/null +++ b/drivers/scsi/bfa/bfa_hw_ct.c @@ -0,0 +1,162 @@ +/* + * 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. + */ + +#include +#include +#include + +BFA_TRC_FILE(HAL, IOCFC_CT); + +static u32 __ct_msix_err_vec_reg[] = { + HOST_MSIX_ERR_INDEX_FN0, + HOST_MSIX_ERR_INDEX_FN1, + HOST_MSIX_ERR_INDEX_FN2, + HOST_MSIX_ERR_INDEX_FN3, +}; + +static void +bfa_hwct_msix_lpu_err_set(struct bfa_s *bfa, bfa_boolean_t msix, int vec) +{ + int fn = bfa_ioc_pcifn(&bfa->ioc); + bfa_os_addr_t kva = bfa_ioc_bar0(&bfa->ioc); + + if (msix) + bfa_reg_write(kva + __ct_msix_err_vec_reg[fn], vec); + else + bfa_reg_write(kva + __ct_msix_err_vec_reg[fn], 0); +} + +/** + * Dummy interrupt handler for handling spurious interrupt during chip-reinit. + */ +static void +bfa_hwct_msix_dummy(struct bfa_s *bfa, int vec) +{ +} + +void +bfa_hwct_reginit(struct bfa_s *bfa) +{ + struct bfa_iocfc_regs_s *bfa_regs = &bfa->iocfc.bfa_regs; + bfa_os_addr_t kva = bfa_ioc_bar0(&bfa->ioc); + int i, q, fn = bfa_ioc_pcifn(&bfa->ioc); + + if (fn == 0) { + bfa_regs->intr_status = (kva + HOSTFN0_INT_STATUS); + bfa_regs->intr_mask = (kva + HOSTFN0_INT_MSK); + } else { + bfa_regs->intr_status = (kva + HOSTFN1_INT_STATUS); + bfa_regs->intr_mask = (kva + HOSTFN1_INT_MSK); + } + + for (i = 0; i < BFI_IOC_MAX_CQS; i++) { + /* + * CPE registers + */ + q = CPE_Q_NUM(fn, i); + bfa_regs->cpe_q_pi[i] = (kva + CPE_PI_PTR_Q(q << 5)); + bfa_regs->cpe_q_ci[i] = (kva + CPE_CI_PTR_Q(q << 5)); + bfa_regs->cpe_q_depth[i] = (kva + CPE_DEPTH_Q(q << 5)); + bfa_regs->cpe_q_ctrl[i] = (kva + CPE_QCTRL_Q(q << 5)); + + /* + * RME registers + */ + q = CPE_Q_NUM(fn, i); + bfa_regs->rme_q_pi[i] = (kva + RME_PI_PTR_Q(q << 5)); + bfa_regs->rme_q_ci[i] = (kva + RME_CI_PTR_Q(q << 5)); + bfa_regs->rme_q_depth[i] = (kva + RME_DEPTH_Q(q << 5)); + bfa_regs->rme_q_ctrl[i] = (kva + RME_QCTRL_Q(q << 5)); + } +} + +void +bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq) +{ + u32 r32; + + r32 = bfa_reg_read(bfa->iocfc.bfa_regs.rme_q_ctrl[rspq]); + bfa_reg_write(bfa->iocfc.bfa_regs.rme_q_ctrl[rspq], r32); +} + +void +bfa_hwct_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap, + u32 *num_vecs, u32 *max_vec_bit) +{ + *msix_vecs_bmap = (1 << BFA_MSIX_CT_MAX) - 1; + *max_vec_bit = (1 << (BFA_MSIX_CT_MAX - 1)); + *num_vecs = BFA_MSIX_CT_MAX; +} + +/** + * Setup MSI-X vector for catapult + */ +void +bfa_hwct_msix_init(struct bfa_s *bfa, int nvecs) +{ + bfa_assert((nvecs == 1) || (nvecs == BFA_MSIX_CT_MAX)); + bfa_trc(bfa, nvecs); + + bfa->msix.nvecs = nvecs; + bfa_hwct_msix_uninstall(bfa); +} + +void +bfa_hwct_msix_install(struct bfa_s *bfa) +{ + int i; + + if (bfa->msix.nvecs == 0) + return; + + if (bfa->msix.nvecs == 1) { + for (i = 0; i < BFA_MSIX_CT_MAX; i++) + bfa->msix.handler[i] = bfa_msix_all; + return; + } + + for (i = BFA_MSIX_CPE_Q0; i <= BFA_MSIX_CPE_Q3; i++) + bfa->msix.handler[i] = bfa_msix_reqq; + + for (; i <= BFA_MSIX_RME_Q3; i++) + bfa->msix.handler[i] = bfa_msix_rspq; + + bfa_assert(i == BFA_MSIX_LPU_ERR); + bfa->msix.handler[BFA_MSIX_LPU_ERR] = bfa_msix_lpu_err; +} + +void +bfa_hwct_msix_uninstall(struct bfa_s *bfa) +{ + int i; + + for (i = 0; i < BFA_MSIX_CT_MAX; i++) + bfa->msix.handler[i] = bfa_hwct_msix_dummy; +} + +/** + * Enable MSI-X vectors + */ +void +bfa_hwct_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix) +{ + bfa_trc(bfa, 0); + bfa_hwct_msix_lpu_err_set(bfa, msix, BFA_MSIX_LPU_ERR); + bfa_ioc_isr_mode_set(&bfa->ioc, msix); +} + + diff --git a/drivers/scsi/bfa/bfa_intr.c b/drivers/scsi/bfa/bfa_intr.c new file mode 100644 index 000000000000..0ca125712a04 --- /dev/null +++ b/drivers/scsi/bfa/bfa_intr.c @@ -0,0 +1,218 @@ +/* + * 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. + */ +#include +#include +#include +#include +#include + +BFA_TRC_FILE(HAL, INTR); + +static void +bfa_msix_errint(struct bfa_s *bfa, u32 intr) +{ + bfa_ioc_error_isr(&bfa->ioc); +} + +static void +bfa_msix_lpu(struct bfa_s *bfa) +{ + bfa_ioc_mbox_isr(&bfa->ioc); +} + +void +bfa_msix_all(struct bfa_s *bfa, int vec) +{ + bfa_intx(bfa); +} + +/** + * hal_intr_api + */ +bfa_boolean_t +bfa_intx(struct bfa_s *bfa) +{ + u32 intr, qintr; + int queue; + + intr = bfa_reg_read(bfa->iocfc.bfa_regs.intr_status); + if (!intr) + return BFA_FALSE; + + /** + * RME completion queue interrupt + */ + qintr = intr & __HFN_INT_RME_MASK; + bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, qintr); + + for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue ++) { + if (intr & (__HFN_INT_RME_Q0 << queue)) + bfa_msix_rspq(bfa, queue & (BFI_IOC_MAX_CQS - 1)); + } + intr &= ~qintr; + if (!intr) + return BFA_TRUE; + + /** + * CPE completion queue interrupt + */ + qintr = intr & __HFN_INT_CPE_MASK; + bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, qintr); + + for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue++) { + if (intr & (__HFN_INT_CPE_Q0 << queue)) + bfa_msix_reqq(bfa, queue & (BFI_IOC_MAX_CQS - 1)); + } + intr &= ~qintr; + if (!intr) + return BFA_TRUE; + + bfa_msix_lpu_err(bfa, intr); + + return BFA_TRUE; +} + +void +bfa_isr_enable(struct bfa_s *bfa) +{ + u32 intr_unmask; + int pci_func = bfa_ioc_pcifn(&bfa->ioc); + + bfa_trc(bfa, pci_func); + + bfa_msix_install(bfa); + intr_unmask = (__HFN_INT_ERR_EMC | __HFN_INT_ERR_LPU0 | + __HFN_INT_ERR_LPU1 | __HFN_INT_ERR_PSS); + + if (pci_func == 0) + intr_unmask |= (__HFN_INT_CPE_Q0 | __HFN_INT_CPE_Q1 | + __HFN_INT_CPE_Q2 | __HFN_INT_CPE_Q3 | + __HFN_INT_RME_Q0 | __HFN_INT_RME_Q1 | + __HFN_INT_RME_Q2 | __HFN_INT_RME_Q3 | + __HFN_INT_MBOX_LPU0); + else + intr_unmask |= (__HFN_INT_CPE_Q4 | __HFN_INT_CPE_Q5 | + __HFN_INT_CPE_Q6 | __HFN_INT_CPE_Q7 | + __HFN_INT_RME_Q4 | __HFN_INT_RME_Q5 | + __HFN_INT_RME_Q6 | __HFN_INT_RME_Q7 | + __HFN_INT_MBOX_LPU1); + + bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, intr_unmask); + bfa_reg_write(bfa->iocfc.bfa_regs.intr_mask, ~intr_unmask); + bfa_isr_mode_set(bfa, bfa->msix.nvecs != 0); +} + +void +bfa_isr_disable(struct bfa_s *bfa) +{ + bfa_isr_mode_set(bfa, BFA_FALSE); + bfa_reg_write(bfa->iocfc.bfa_regs.intr_mask, -1L); + bfa_msix_uninstall(bfa); +} + +void +bfa_msix_reqq(struct bfa_s *bfa, int qid) +{ + struct list_head *waitq, *qe, *qen; + struct bfa_reqq_wait_s *wqe; + + qid &= (BFI_IOC_MAX_CQS - 1); + + waitq = bfa_reqq(bfa, qid); + list_for_each_safe(qe, qen, waitq) { + /** + * Callback only as long as there is room in request queue + */ + if (bfa_reqq_full(bfa, qid)) + break; + + list_del(qe); + wqe = (struct bfa_reqq_wait_s *) qe; + wqe->qresume(wqe->cbarg); + } +} + +void +bfa_isr_unhandled(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + bfa_trc(bfa, m->mhdr.msg_class); + bfa_trc(bfa, m->mhdr.msg_id); + bfa_trc(bfa, m->mhdr.mtag.i2htok); + bfa_assert(0); + bfa_trc_stop(bfa->trcmod); +} + +void +bfa_msix_rspq(struct bfa_s *bfa, int rsp_qid) +{ + struct bfi_msg_s *m; + u32 pi, ci; + + bfa_trc_fp(bfa, rsp_qid); + + rsp_qid &= (BFI_IOC_MAX_CQS - 1); + + bfa->iocfc.hwif.hw_rspq_ack(bfa, rsp_qid); + + ci = bfa_rspq_ci(bfa, rsp_qid); + pi = bfa_rspq_pi(bfa, rsp_qid); + + bfa_trc_fp(bfa, ci); + bfa_trc_fp(bfa, pi); + + if (bfa->rme_process) { + while (ci != pi) { + m = bfa_rspq_elem(bfa, rsp_qid, ci); + bfa_assert_fp(m->mhdr.msg_class < BFI_MC_MAX); + + bfa_isrs[m->mhdr.msg_class] (bfa, m); + + CQ_INCR(ci, bfa->iocfc.cfg.drvcfg.num_rspq_elems); + } + } + + /** + * update CI + */ + bfa_rspq_ci(bfa, rsp_qid) = pi; + bfa_reg_write(bfa->iocfc.bfa_regs.rme_q_ci[rsp_qid], pi); + bfa_os_mmiowb(); +} + +void +bfa_msix_lpu_err(struct bfa_s *bfa, int vec) +{ + u32 intr; + + intr = bfa_reg_read(bfa->iocfc.bfa_regs.intr_status); + + if (intr & (__HFN_INT_MBOX_LPU0 | __HFN_INT_MBOX_LPU1)) + bfa_msix_lpu(bfa); + + if (intr & (__HFN_INT_ERR_EMC | + __HFN_INT_ERR_LPU0 | __HFN_INT_ERR_LPU1 | + __HFN_INT_ERR_PSS)) + bfa_msix_errint(bfa, intr); +} + +void +bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func) +{ + bfa_isrs[mc] = isr_func; +} + + diff --git a/drivers/scsi/bfa/bfa_intr_priv.h b/drivers/scsi/bfa/bfa_intr_priv.h new file mode 100644 index 000000000000..8ce6e6b105c8 --- /dev/null +++ b/drivers/scsi/bfa/bfa_intr_priv.h @@ -0,0 +1,115 @@ +/* + * 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. + */ + +#ifndef __BFA_INTR_PRIV_H__ +#define __BFA_INTR_PRIV_H__ + +/** + * Message handler + */ +typedef void (*bfa_isr_func_t) (struct bfa_s *bfa, struct bfi_msg_s *m); +void bfa_isr_unhandled(struct bfa_s *bfa, struct bfi_msg_s *m); +void bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func); + + +#define bfa_reqq_pi(__bfa, __reqq) (__bfa)->iocfc.req_cq_pi[__reqq] +#define bfa_reqq_ci(__bfa, __reqq) \ + *(u32 *)((__bfa)->iocfc.req_cq_shadow_ci[__reqq].kva) + +#define bfa_reqq_full(__bfa, __reqq) \ + (((bfa_reqq_pi(__bfa, __reqq) + 1) & \ + ((__bfa)->iocfc.cfg.drvcfg.num_reqq_elems - 1)) == \ + bfa_reqq_ci(__bfa, __reqq)) + +#define bfa_reqq_next(__bfa, __reqq) \ + (bfa_reqq_full(__bfa, __reqq) ? NULL : \ + ((void *)((struct bfi_msg_s *)((__bfa)->iocfc.req_cq_ba[__reqq].kva) \ + + bfa_reqq_pi((__bfa), (__reqq))))) + +#define bfa_reqq_produce(__bfa, __reqq) do { \ + (__bfa)->iocfc.req_cq_pi[__reqq]++; \ + (__bfa)->iocfc.req_cq_pi[__reqq] &= \ + ((__bfa)->iocfc.cfg.drvcfg.num_reqq_elems - 1); \ + bfa_reg_write((__bfa)->iocfc.bfa_regs.cpe_q_pi[__reqq], \ + (__bfa)->iocfc.req_cq_pi[__reqq]); \ + bfa_os_mmiowb(); \ +} while (0) + +#define bfa_rspq_pi(__bfa, __rspq) \ + *(u32 *)((__bfa)->iocfc.rsp_cq_shadow_pi[__rspq].kva) + +#define bfa_rspq_ci(__bfa, __rspq) (__bfa)->iocfc.rsp_cq_ci[__rspq] +#define bfa_rspq_elem(__bfa, __rspq, __ci) \ + &((struct bfi_msg_s *)((__bfa)->iocfc.rsp_cq_ba[__rspq].kva))[__ci] + +#define CQ_INCR(__index, __size) \ + (__index)++; (__index) &= ((__size) - 1) + +/** + * Queue element to wait for room in request queue. FIFO order is + * maintained when fullfilling requests. + */ +struct bfa_reqq_wait_s { + struct list_head qe; + void (*qresume) (void *cbarg); + void *cbarg; +}; + +/** + * Circular queue usage assignments + */ +enum { + BFA_REQQ_IOC = 0, /* all low-priority IOC msgs */ + BFA_REQQ_FCXP = 0, /* all FCXP messages */ + BFA_REQQ_LPS = 0, /* all lport service msgs */ + BFA_REQQ_PORT = 0, /* all port messages */ + BFA_REQQ_FLASH = 0, /* for flash module */ + BFA_REQQ_DIAG = 0, /* for diag module */ + BFA_REQQ_RPORT = 0, /* all port messages */ + BFA_REQQ_SBOOT = 0, /* all san boot messages */ + BFA_REQQ_QOS_LO = 1, /* all low priority IO */ + BFA_REQQ_QOS_MD = 2, /* all medium priority IO */ + BFA_REQQ_QOS_HI = 3, /* all high priority IO */ +}; + +static inline void +bfa_reqq_winit(struct bfa_reqq_wait_s *wqe, void (*qresume) (void *cbarg), + void *cbarg) +{ + wqe->qresume = qresume; + wqe->cbarg = cbarg; +} + +#define bfa_reqq(__bfa, __reqq) &(__bfa)->reqq_waitq[__reqq] + +/** + * static inline void + * bfa_reqq_wait(struct bfa_s *bfa, int reqq, struct bfa_reqq_wait_s *wqe) + */ +#define bfa_reqq_wait(__bfa, __reqq, __wqe) do { \ + \ + struct list_head *waitq = bfa_reqq(__bfa, __reqq); \ + \ + bfa_assert(((__reqq) < BFI_IOC_MAX_CQS)); \ + bfa_assert((__wqe)->qresume && (__wqe)->cbarg); \ + \ + list_add_tail(&(__wqe)->qe, waitq); \ +} while (0) + +#define bfa_reqq_wcancel(__wqe) list_del(&(__wqe)->qe) + +#endif /* __BFA_INTR_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c new file mode 100644 index 000000000000..149348934ce3 --- /dev/null +++ b/drivers/scsi/bfa/bfa_ioc.c @@ -0,0 +1,2382 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +BFA_TRC_FILE(HAL, IOC); + +/** + * IOC local definitions + */ +#define BFA_IOC_TOV 2000 /* msecs */ +#define BFA_IOC_HB_TOV 1000 /* msecs */ +#define BFA_IOC_HB_FAIL_MAX 4 +#define BFA_IOC_HWINIT_MAX 2 +#define BFA_IOC_FWIMG_MINSZ (16 * 1024) +#define BFA_IOC_TOV_RECOVER (BFA_IOC_HB_FAIL_MAX * BFA_IOC_HB_TOV \ + + BFA_IOC_TOV) + +#define bfa_ioc_timer_start(__ioc) \ + bfa_timer_begin((__ioc)->timer_mod, &(__ioc)->ioc_timer, \ + bfa_ioc_timeout, (__ioc), BFA_IOC_TOV) +#define bfa_ioc_timer_stop(__ioc) bfa_timer_stop(&(__ioc)->ioc_timer) + +#define BFA_DBG_FWTRC_ENTS (BFI_IOC_TRC_ENTS) +#define BFA_DBG_FWTRC_LEN \ + (BFA_DBG_FWTRC_ENTS * sizeof(struct bfa_trc_s) + \ + (sizeof(struct bfa_trc_mod_s) - \ + BFA_TRC_MAX * sizeof(struct bfa_trc_s))) +#define BFA_DBG_FWTRC_OFF(_fn) (BFI_IOC_TRC_OFF + BFA_DBG_FWTRC_LEN * (_fn)) +#define bfa_ioc_stats(_ioc, _stats) (_ioc)->stats._stats ++ + +#define BFA_FLASH_CHUNK_NO(off) (off / BFI_FLASH_CHUNK_SZ_WORDS) +#define BFA_FLASH_OFFSET_IN_CHUNK(off) (off % BFI_FLASH_CHUNK_SZ_WORDS) +#define BFA_FLASH_CHUNK_ADDR(chunkno) (chunkno * BFI_FLASH_CHUNK_SZ_WORDS) +bfa_boolean_t bfa_auto_recover = BFA_FALSE; + +/* + * forward declarations + */ +static void bfa_ioc_aen_post(struct bfa_ioc_s *bfa, + enum bfa_ioc_aen_event event); +static void bfa_ioc_hw_sem_get(struct bfa_ioc_s *ioc); +static void bfa_ioc_hw_sem_release(struct bfa_ioc_s *ioc); +static void bfa_ioc_hw_sem_get_cancel(struct bfa_ioc_s *ioc); +static void bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force); +static void bfa_ioc_timeout(void *ioc); +static void bfa_ioc_send_enable(struct bfa_ioc_s *ioc); +static void bfa_ioc_send_disable(struct bfa_ioc_s *ioc); +static void bfa_ioc_send_getattr(struct bfa_ioc_s *ioc); +static void bfa_ioc_hb_monitor(struct bfa_ioc_s *ioc); +static void bfa_ioc_hb_stop(struct bfa_ioc_s *ioc); +static void bfa_ioc_reset(struct bfa_ioc_s *ioc, bfa_boolean_t force); +static void bfa_ioc_mbox_poll(struct bfa_ioc_s *ioc); +static void bfa_ioc_mbox_hbfail(struct bfa_ioc_s *ioc); +static void bfa_ioc_recover(struct bfa_ioc_s *ioc); +static bfa_boolean_t bfa_ioc_firmware_lock(struct bfa_ioc_s *ioc); +static void bfa_ioc_firmware_unlock(struct bfa_ioc_s *ioc); +static void bfa_ioc_disable_comp(struct bfa_ioc_s *ioc); +static void bfa_ioc_lpu_stop(struct bfa_ioc_s *ioc); + +/** + * bfa_ioc_sm + */ + +/** + * IOC state machine events + */ +enum ioc_event { + IOC_E_ENABLE = 1, /* IOC enable request */ + IOC_E_DISABLE = 2, /* IOC disable request */ + IOC_E_TIMEOUT = 3, /* f/w response timeout */ + IOC_E_FWREADY = 4, /* f/w initialization done */ + IOC_E_FWRSP_GETATTR = 5, /* IOC get attribute response */ + IOC_E_FWRSP_ENABLE = 6, /* enable f/w response */ + IOC_E_FWRSP_DISABLE = 7, /* disable f/w response */ + IOC_E_HBFAIL = 8, /* heartbeat failure */ + IOC_E_HWERROR = 9, /* hardware error interrupt */ + IOC_E_SEMLOCKED = 10, /* h/w semaphore is locked */ + IOC_E_DETACH = 11, /* driver detach cleanup */ +}; + +bfa_fsm_state_decl(bfa_ioc, reset, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, fwcheck, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, mismatch, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, semwait, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, hwinit, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, enabling, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, getattr, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, op, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, initfail, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, hbfail, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, disabling, struct bfa_ioc_s, enum ioc_event); +bfa_fsm_state_decl(bfa_ioc, disabled, struct bfa_ioc_s, enum ioc_event); + +static struct bfa_sm_table_s ioc_sm_table[] = { + {BFA_SM(bfa_ioc_sm_reset), BFA_IOC_RESET}, + {BFA_SM(bfa_ioc_sm_fwcheck), BFA_IOC_FWMISMATCH}, + {BFA_SM(bfa_ioc_sm_mismatch), BFA_IOC_FWMISMATCH}, + {BFA_SM(bfa_ioc_sm_semwait), BFA_IOC_SEMWAIT}, + {BFA_SM(bfa_ioc_sm_hwinit), BFA_IOC_HWINIT}, + {BFA_SM(bfa_ioc_sm_enabling), BFA_IOC_HWINIT}, + {BFA_SM(bfa_ioc_sm_getattr), BFA_IOC_GETATTR}, + {BFA_SM(bfa_ioc_sm_op), BFA_IOC_OPERATIONAL}, + {BFA_SM(bfa_ioc_sm_initfail), BFA_IOC_INITFAIL}, + {BFA_SM(bfa_ioc_sm_hbfail), BFA_IOC_HBFAIL}, + {BFA_SM(bfa_ioc_sm_disabling), BFA_IOC_DISABLING}, + {BFA_SM(bfa_ioc_sm_disabled), BFA_IOC_DISABLED}, +}; + +/** + * Reset entry actions -- initialize state machine + */ +static void +bfa_ioc_sm_reset_entry(struct bfa_ioc_s *ioc) +{ + ioc->retry_count = 0; + ioc->auto_recover = bfa_auto_recover; +} + +/** + * Beginning state. IOC is in reset state. + */ +static void +bfa_ioc_sm_reset(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_ENABLE: + bfa_fsm_set_state(ioc, bfa_ioc_sm_fwcheck); + break; + + case IOC_E_DISABLE: + bfa_ioc_disable_comp(ioc); + break; + + case IOC_E_DETACH: + break; + + default: + bfa_sm_fault(ioc, event); + } +} + +/** + * Semaphore should be acquired for version check. + */ +static void +bfa_ioc_sm_fwcheck_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_hw_sem_get(ioc); +} + +/** + * Awaiting h/w semaphore to continue with version check. + */ +static void +bfa_ioc_sm_fwcheck(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_SEMLOCKED: + if (bfa_ioc_firmware_lock(ioc)) { + ioc->retry_count = 0; + bfa_fsm_set_state(ioc, bfa_ioc_sm_hwinit); + } else { + bfa_ioc_hw_sem_release(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_mismatch); + } + break; + + case IOC_E_DISABLE: + bfa_ioc_disable_comp(ioc); + /* + * fall through + */ + + case IOC_E_DETACH: + bfa_ioc_hw_sem_get_cancel(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_reset); + break; + + case IOC_E_FWREADY: + break; + + default: + bfa_sm_fault(ioc, event); + } +} + +/** + * Notify enable completion callback and generate mismatch AEN. + */ +static void +bfa_ioc_sm_mismatch_entry(struct bfa_ioc_s *ioc) +{ + /** + * Provide enable completion callback and AEN notification only once. + */ + if (ioc->retry_count == 0) { + ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE); + bfa_ioc_aen_post(ioc, BFA_IOC_AEN_FWMISMATCH); + } + ioc->retry_count++; + bfa_ioc_timer_start(ioc); +} + +/** + * Awaiting firmware version match. + */ +static void +bfa_ioc_sm_mismatch(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_TIMEOUT: + bfa_fsm_set_state(ioc, bfa_ioc_sm_fwcheck); + break; + + case IOC_E_DISABLE: + bfa_ioc_disable_comp(ioc); + /* + * fall through + */ + + case IOC_E_DETACH: + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_reset); + break; + + case IOC_E_FWREADY: + break; + + default: + bfa_sm_fault(ioc, event); + } +} + +/** + * Request for semaphore. + */ +static void +bfa_ioc_sm_semwait_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_hw_sem_get(ioc); +} + +/** + * Awaiting semaphore for h/w initialzation. + */ +static void +bfa_ioc_sm_semwait(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_SEMLOCKED: + ioc->retry_count = 0; + bfa_fsm_set_state(ioc, bfa_ioc_sm_hwinit); + break; + + case IOC_E_DISABLE: + bfa_ioc_hw_sem_get_cancel(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_hwinit_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_timer_start(ioc); + bfa_ioc_reset(ioc, BFA_FALSE); +} + +/** + * Hardware is being initialized. Interrupts are enabled. + * Holding hardware semaphore lock. + */ +static void +bfa_ioc_sm_hwinit(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_FWREADY: + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_enabling); + break; + + case IOC_E_HWERROR: + bfa_ioc_timer_stop(ioc); + /* + * fall through + */ + + case IOC_E_TIMEOUT: + ioc->retry_count++; + if (ioc->retry_count < BFA_IOC_HWINIT_MAX) { + bfa_ioc_timer_start(ioc); + bfa_ioc_reset(ioc, BFA_TRUE); + break; + } + + bfa_ioc_hw_sem_release(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_initfail); + break; + + case IOC_E_DISABLE: + bfa_ioc_hw_sem_release(ioc); + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_enabling_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_timer_start(ioc); + bfa_ioc_send_enable(ioc); +} + +/** + * Host IOC function is being enabled, awaiting response from firmware. + * Semaphore is acquired. + */ +static void +bfa_ioc_sm_enabling(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_FWRSP_ENABLE: + bfa_ioc_timer_stop(ioc); + bfa_ioc_hw_sem_release(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_getattr); + break; + + case IOC_E_HWERROR: + bfa_ioc_timer_stop(ioc); + /* + * fall through + */ + + case IOC_E_TIMEOUT: + ioc->retry_count++; + if (ioc->retry_count < BFA_IOC_HWINIT_MAX) { + bfa_reg_write(ioc->ioc_regs.ioc_fwstate, + BFI_IOC_UNINIT); + bfa_fsm_set_state(ioc, bfa_ioc_sm_hwinit); + break; + } + + bfa_ioc_hw_sem_release(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_initfail); + break; + + case IOC_E_DISABLE: + bfa_ioc_timer_stop(ioc); + bfa_ioc_hw_sem_release(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + case IOC_E_FWREADY: + bfa_ioc_send_enable(ioc); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_getattr_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_timer_start(ioc); + bfa_ioc_send_getattr(ioc); +} + +/** + * IOC configuration in progress. Timer is active. + */ +static void +bfa_ioc_sm_getattr(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_FWRSP_GETATTR: + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_op); + break; + + case IOC_E_HWERROR: + bfa_ioc_timer_stop(ioc); + /* + * fall through + */ + + case IOC_E_TIMEOUT: + bfa_fsm_set_state(ioc, bfa_ioc_sm_initfail); + break; + + case IOC_E_DISABLE: + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_op_entry(struct bfa_ioc_s *ioc) +{ + ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_OK); + bfa_ioc_hb_monitor(ioc); + bfa_ioc_aen_post(ioc, BFA_IOC_AEN_ENABLE); +} + +static void +bfa_ioc_sm_op(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_ENABLE: + break; + + case IOC_E_DISABLE: + bfa_ioc_hb_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabling); + break; + + case IOC_E_HWERROR: + case IOC_E_FWREADY: + /** + * Hard error or IOC recovery by other function. + * Treat it same as heartbeat failure. + */ + bfa_ioc_hb_stop(ioc); + /* + * !!! fall through !!! + */ + + case IOC_E_HBFAIL: + bfa_fsm_set_state(ioc, bfa_ioc_sm_hbfail); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_disabling_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_aen_post(ioc, BFA_IOC_AEN_DISABLE); + bfa_ioc_timer_start(ioc); + bfa_ioc_send_disable(ioc); +} + +/** + * IOC is being disabled + */ +static void +bfa_ioc_sm_disabling(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_HWERROR: + case IOC_E_FWRSP_DISABLE: + bfa_ioc_timer_stop(ioc); + /* + * !!! fall through !!! + */ + + case IOC_E_TIMEOUT: + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + +/** + * IOC disable completion entry. + */ +static void +bfa_ioc_sm_disabled_entry(struct bfa_ioc_s *ioc) +{ + bfa_ioc_disable_comp(ioc); +} + +static void +bfa_ioc_sm_disabled(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_ENABLE: + bfa_fsm_set_state(ioc, bfa_ioc_sm_semwait); + break; + + case IOC_E_DISABLE: + ioc->cbfn->disable_cbfn(ioc->bfa); + break; + + case IOC_E_FWREADY: + break; + + case IOC_E_DETACH: + bfa_ioc_firmware_unlock(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_reset); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_initfail_entry(struct bfa_ioc_s *ioc) +{ + ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE); + bfa_ioc_timer_start(ioc); +} + +/** + * Hardware initialization failed. + */ +static void +bfa_ioc_sm_initfail(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + case IOC_E_DISABLE: + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + case IOC_E_DETACH: + bfa_ioc_timer_stop(ioc); + bfa_ioc_firmware_unlock(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_reset); + break; + + case IOC_E_TIMEOUT: + bfa_fsm_set_state(ioc, bfa_ioc_sm_semwait); + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + +static void +bfa_ioc_sm_hbfail_entry(struct bfa_ioc_s *ioc) +{ + struct list_head *qe; + struct bfa_ioc_hbfail_notify_s *notify; + + /** + * Mark IOC as failed in hardware and stop firmware. + */ + bfa_ioc_lpu_stop(ioc); + bfa_reg_write(ioc->ioc_regs.ioc_fwstate, BFI_IOC_HBFAIL); + + if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT) { + bfa_reg_write(ioc->ioc_regs.ll_halt, __FW_INIT_HALT_P); + /* + * Wait for halt to take effect + */ + bfa_reg_read(ioc->ioc_regs.ll_halt); + } + + /** + * Notify driver and common modules registered for notification. + */ + ioc->cbfn->hbfail_cbfn(ioc->bfa); + list_for_each(qe, &ioc->hb_notify_q) { + notify = (struct bfa_ioc_hbfail_notify_s *)qe; + notify->cbfn(notify->cbarg); + } + + /** + * Flush any queued up mailbox requests. + */ + bfa_ioc_mbox_hbfail(ioc); + bfa_ioc_aen_post(ioc, BFA_IOC_AEN_HBFAIL); + + /** + * Trigger auto-recovery after a delay. + */ + if (ioc->auto_recover) { + bfa_timer_begin(ioc->timer_mod, &ioc->ioc_timer, + bfa_ioc_timeout, ioc, BFA_IOC_TOV_RECOVER); + } +} + +/** + * IOC heartbeat failure. + */ +static void +bfa_ioc_sm_hbfail(struct bfa_ioc_s *ioc, enum ioc_event event) +{ + bfa_trc(ioc, event); + + switch (event) { + + case IOC_E_ENABLE: + ioc->cbfn->enable_cbfn(ioc->bfa, BFA_STATUS_IOC_FAILURE); + break; + + case IOC_E_DISABLE: + if (ioc->auto_recover) + bfa_ioc_timer_stop(ioc); + bfa_fsm_set_state(ioc, bfa_ioc_sm_disabled); + break; + + case IOC_E_TIMEOUT: + bfa_fsm_set_state(ioc, bfa_ioc_sm_semwait); + break; + + case IOC_E_FWREADY: + /** + * Recovery is already initiated by other function. + */ + break; + + default: + bfa_sm_fault(ioc, event); + } +} + + + +/** + * bfa_ioc_pvt BFA IOC private functions + */ + +static void +bfa_ioc_disable_comp(struct bfa_ioc_s *ioc) +{ + struct list_head *qe; + struct bfa_ioc_hbfail_notify_s *notify; + + ioc->cbfn->disable_cbfn(ioc->bfa); + + /** + * Notify common modules registered for notification. + */ + list_for_each(qe, &ioc->hb_notify_q) { + notify = (struct bfa_ioc_hbfail_notify_s *)qe; + notify->cbfn(notify->cbarg); + } +} + +static void +bfa_ioc_sem_timeout(void *ioc_arg) +{ + struct bfa_ioc_s *ioc = (struct bfa_ioc_s *)ioc_arg; + + bfa_ioc_hw_sem_get(ioc); +} + +static void +bfa_ioc_usage_sem_get(struct bfa_ioc_s *ioc) +{ + u32 r32; + int cnt = 0; +#define BFA_SEM_SPINCNT 1000 + + do { + r32 = bfa_reg_read(ioc->ioc_regs.ioc_usage_sem_reg); + cnt++; + if (cnt > BFA_SEM_SPINCNT) + break; + } while (r32 != 0); + bfa_assert(cnt < BFA_SEM_SPINCNT); +} + +static void +bfa_ioc_usage_sem_release(struct bfa_ioc_s *ioc) +{ + bfa_reg_write(ioc->ioc_regs.ioc_usage_sem_reg, 1); +} + +static void +bfa_ioc_hw_sem_get(struct bfa_ioc_s *ioc) +{ + u32 r32; + + /** + * First read to the semaphore register will return 0, subsequent reads + * will return 1. Semaphore is released by writing 0 to the register + */ + r32 = bfa_reg_read(ioc->ioc_regs.ioc_sem_reg); + if (r32 == 0) { + bfa_fsm_send_event(ioc, IOC_E_SEMLOCKED); + return; + } + + bfa_timer_begin(ioc->timer_mod, &ioc->sem_timer, bfa_ioc_sem_timeout, + ioc, BFA_IOC_TOV); +} + +static void +bfa_ioc_hw_sem_release(struct bfa_ioc_s *ioc) +{ + bfa_reg_write(ioc->ioc_regs.ioc_sem_reg, 1); +} + +static void +bfa_ioc_hw_sem_get_cancel(struct bfa_ioc_s *ioc) +{ + bfa_timer_stop(&ioc->sem_timer); +} + +/** + * Initialize LPU local memory (aka secondary memory / SRAM) + */ +static void +bfa_ioc_lmem_init(struct bfa_ioc_s *ioc) +{ + u32 pss_ctl; + int i; +#define PSS_LMEM_INIT_TIME 10000 + + pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg); + pss_ctl &= ~__PSS_LMEM_RESET; + pss_ctl |= __PSS_LMEM_INIT_EN; + pss_ctl |= __PSS_I2C_CLK_DIV(3UL); /* i2c workaround 12.5khz clock */ + bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl); + + /** + * wait for memory initialization to be complete + */ + i = 0; + do { + pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg); + i++; + } while (!(pss_ctl & __PSS_LMEM_INIT_DONE) && (i < PSS_LMEM_INIT_TIME)); + + /** + * If memory initialization is not successful, IOC timeout will catch + * such failures. + */ + bfa_assert(pss_ctl & __PSS_LMEM_INIT_DONE); + bfa_trc(ioc, pss_ctl); + + pss_ctl &= ~(__PSS_LMEM_INIT_DONE | __PSS_LMEM_INIT_EN); + bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl); +} + +static void +bfa_ioc_lpu_start(struct bfa_ioc_s *ioc) +{ + u32 pss_ctl; + + /** + * Take processor out of reset. + */ + pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg); + pss_ctl &= ~__PSS_LPU0_RESET; + + bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl); +} + +static void +bfa_ioc_lpu_stop(struct bfa_ioc_s *ioc) +{ + u32 pss_ctl; + + /** + * Put processors in reset. + */ + pss_ctl = bfa_reg_read(ioc->ioc_regs.pss_ctl_reg); + pss_ctl |= (__PSS_LPU0_RESET | __PSS_LPU1_RESET); + + bfa_reg_write(ioc->ioc_regs.pss_ctl_reg, pss_ctl); +} + +/** + * Get driver and firmware versions. + */ +static void +bfa_ioc_fwver_get(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr) +{ + u32 pgnum, pgoff; + u32 loff = 0; + int i; + u32 *fwsig = (u32 *) fwhdr; + + pgnum = bfa_ioc_smem_pgnum(ioc, loff); + pgoff = bfa_ioc_smem_pgoff(ioc, loff); + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum); + + for (i = 0; i < (sizeof(struct bfi_ioc_image_hdr_s) / sizeof(u32)); + i++) { + fwsig[i] = bfa_mem_read(ioc->ioc_regs.smem_page_start, loff); + loff += sizeof(u32); + } +} + +static u32 * +bfa_ioc_fwimg_get_chunk(struct bfa_ioc_s *ioc, u32 off) +{ + if (ioc->ctdev) + return bfi_image_ct_get_chunk(off); + return bfi_image_cb_get_chunk(off); +} + +static u32 +bfa_ioc_fwimg_get_size(struct bfa_ioc_s *ioc) +{ +return (ioc->ctdev) ? bfi_image_ct_size : bfi_image_cb_size; +} + +/** + * Returns TRUE if same. + */ +static bfa_boolean_t +bfa_ioc_fwver_cmp(struct bfa_ioc_s *ioc, struct bfi_ioc_image_hdr_s *fwhdr) +{ + struct bfi_ioc_image_hdr_s *drv_fwhdr; + int i; + + drv_fwhdr = + (struct bfi_ioc_image_hdr_s *)bfa_ioc_fwimg_get_chunk(ioc, 0); + + for (i = 0; i < BFI_IOC_MD5SUM_SZ; i++) { + if (fwhdr->md5sum[i] != drv_fwhdr->md5sum[i]) { + bfa_trc(ioc, i); + bfa_trc(ioc, fwhdr->md5sum[i]); + bfa_trc(ioc, drv_fwhdr->md5sum[i]); + return BFA_FALSE; + } + } + + bfa_trc(ioc, fwhdr->md5sum[0]); + return BFA_TRUE; +} + +/** + * Return true if current running version is valid. Firmware signature and + * execution context (driver/bios) must match. + */ +static bfa_boolean_t +bfa_ioc_fwver_valid(struct bfa_ioc_s *ioc) +{ + struct bfi_ioc_image_hdr_s fwhdr, *drv_fwhdr; + + /** + * If bios/efi boot (flash based) -- return true + */ + if (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ) + return BFA_TRUE; + + bfa_ioc_fwver_get(ioc, &fwhdr); + drv_fwhdr = + (struct bfi_ioc_image_hdr_s *)bfa_ioc_fwimg_get_chunk(ioc, 0); + + if (fwhdr.signature != drv_fwhdr->signature) { + bfa_trc(ioc, fwhdr.signature); + bfa_trc(ioc, drv_fwhdr->signature); + return BFA_FALSE; + } + + if (fwhdr.exec != drv_fwhdr->exec) { + bfa_trc(ioc, fwhdr.exec); + bfa_trc(ioc, drv_fwhdr->exec); + return BFA_FALSE; + } + + return bfa_ioc_fwver_cmp(ioc, &fwhdr); +} + +/** + * Return true if firmware of current driver matches the running firmware. + */ +static bfa_boolean_t +bfa_ioc_firmware_lock(struct bfa_ioc_s *ioc) +{ + enum bfi_ioc_state ioc_fwstate; + u32 usecnt; + struct bfi_ioc_image_hdr_s fwhdr; + + /** + * Firmware match check is relevant only for CNA. + */ + if (!ioc->cna) + return BFA_TRUE; + + /** + * If bios boot (flash based) -- do not increment usage count + */ + if (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ) + return BFA_TRUE; + + bfa_ioc_usage_sem_get(ioc); + usecnt = bfa_reg_read(ioc->ioc_regs.ioc_usage_reg); + + /** + * If usage count is 0, always return TRUE. + */ + if (usecnt == 0) { + bfa_reg_write(ioc->ioc_regs.ioc_usage_reg, 1); + bfa_ioc_usage_sem_release(ioc); + bfa_trc(ioc, usecnt); + return BFA_TRUE; + } + + ioc_fwstate = bfa_reg_read(ioc->ioc_regs.ioc_fwstate); + bfa_trc(ioc, ioc_fwstate); + + /** + * Use count cannot be non-zero and chip in uninitialized state. + */ + bfa_assert(ioc_fwstate != BFI_IOC_UNINIT); + + /** + * Check if another driver with a different firmware is active + */ + bfa_ioc_fwver_get(ioc, &fwhdr); + if (!bfa_ioc_fwver_cmp(ioc, &fwhdr)) { + bfa_ioc_usage_sem_release(ioc); + bfa_trc(ioc, usecnt); + return BFA_FALSE; + } + + /** + * Same firmware version. Increment the reference count. + */ + usecnt++; + bfa_reg_write(ioc->ioc_regs.ioc_usage_reg, usecnt); + bfa_ioc_usage_sem_release(ioc); + bfa_trc(ioc, usecnt); + return BFA_TRUE; +} + +static void +bfa_ioc_firmware_unlock(struct bfa_ioc_s *ioc) +{ + u32 usecnt; + + /** + * Firmware lock is relevant only for CNA. + * If bios boot (flash based) -- do not decrement usage count + */ + if (!ioc->cna || (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ)) + return; + + /** + * decrement usage count + */ + bfa_ioc_usage_sem_get(ioc); + usecnt = bfa_reg_read(ioc->ioc_regs.ioc_usage_reg); + bfa_assert(usecnt > 0); + + usecnt--; + bfa_reg_write(ioc->ioc_regs.ioc_usage_reg, usecnt); + bfa_trc(ioc, usecnt); + + bfa_ioc_usage_sem_release(ioc); +} + +/** + * Conditionally flush any pending message from firmware at start. + */ +static void +bfa_ioc_msgflush(struct bfa_ioc_s *ioc) +{ + u32 r32; + + r32 = bfa_reg_read(ioc->ioc_regs.lpu_mbox_cmd); + if (r32) + bfa_reg_write(ioc->ioc_regs.lpu_mbox_cmd, 1); +} + + +static void +bfa_ioc_hwinit(struct bfa_ioc_s *ioc, bfa_boolean_t force) +{ + enum bfi_ioc_state ioc_fwstate; + bfa_boolean_t fwvalid; + + ioc_fwstate = bfa_reg_read(ioc->ioc_regs.ioc_fwstate); + + if (force) + ioc_fwstate = BFI_IOC_UNINIT; + + bfa_trc(ioc, ioc_fwstate); + + /** + * check if firmware is valid + */ + fwvalid = (ioc_fwstate == BFI_IOC_UNINIT) ? + BFA_FALSE : bfa_ioc_fwver_valid(ioc); + + if (!fwvalid) { + bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, ioc->pcidev.device_id); + return; + } + + /** + * If hardware initialization is in progress (initialized by other IOC), + * just wait for an initialization completion interrupt. + */ + if (ioc_fwstate == BFI_IOC_INITING) { + bfa_trc(ioc, ioc_fwstate); + ioc->cbfn->reset_cbfn(ioc->bfa); + return; + } + + /** + * If IOC function is disabled and firmware version is same, + * just re-enable IOC. + */ + if (ioc_fwstate == BFI_IOC_DISABLED || ioc_fwstate == BFI_IOC_OP) { + bfa_trc(ioc, ioc_fwstate); + + /** + * When using MSI-X any pending firmware ready event should + * be flushed. Otherwise MSI-X interrupts are not delivered. + */ + bfa_ioc_msgflush(ioc); + ioc->cbfn->reset_cbfn(ioc->bfa); + bfa_fsm_send_event(ioc, IOC_E_FWREADY); + return; + } + + /** + * Initialize the h/w for any other states. + */ + bfa_ioc_boot(ioc, BFI_BOOT_TYPE_NORMAL, ioc->pcidev.device_id); +} + +static void +bfa_ioc_timeout(void *ioc_arg) +{ + struct bfa_ioc_s *ioc = (struct bfa_ioc_s *)ioc_arg; + + bfa_trc(ioc, 0); + bfa_fsm_send_event(ioc, IOC_E_TIMEOUT); +} + +void +bfa_ioc_mbox_send(struct bfa_ioc_s *ioc, void *ioc_msg, int len) +{ + u32 *msgp = (u32 *) ioc_msg; + u32 i; + + bfa_trc(ioc, msgp[0]); + bfa_trc(ioc, len); + + bfa_assert(len <= BFI_IOC_MSGLEN_MAX); + + /* + * first write msg to mailbox registers + */ + for (i = 0; i < len / sizeof(u32); i++) + bfa_reg_write(ioc->ioc_regs.hfn_mbox + i * sizeof(u32), + bfa_os_wtole(msgp[i])); + + for (; i < BFI_IOC_MSGLEN_MAX / sizeof(u32); i++) + bfa_reg_write(ioc->ioc_regs.hfn_mbox + i * sizeof(u32), 0); + + /* + * write 1 to mailbox CMD to trigger LPU event + */ + bfa_reg_write(ioc->ioc_regs.hfn_mbox_cmd, 1); + (void)bfa_reg_read(ioc->ioc_regs.hfn_mbox_cmd); +} + +static void +bfa_ioc_send_enable(struct bfa_ioc_s *ioc) +{ + struct bfi_ioc_ctrl_req_s enable_req; + + bfi_h2i_set(enable_req.mh, BFI_MC_IOC, BFI_IOC_H2I_ENABLE_REQ, + bfa_ioc_portid(ioc)); + enable_req.ioc_class = ioc->ioc_mc; + bfa_ioc_mbox_send(ioc, &enable_req, sizeof(struct bfi_ioc_ctrl_req_s)); +} + +static void +bfa_ioc_send_disable(struct bfa_ioc_s *ioc) +{ + struct bfi_ioc_ctrl_req_s disable_req; + + bfi_h2i_set(disable_req.mh, BFI_MC_IOC, BFI_IOC_H2I_DISABLE_REQ, + bfa_ioc_portid(ioc)); + bfa_ioc_mbox_send(ioc, &disable_req, sizeof(struct bfi_ioc_ctrl_req_s)); +} + +static void +bfa_ioc_send_getattr(struct bfa_ioc_s *ioc) +{ + struct bfi_ioc_getattr_req_s attr_req; + + bfi_h2i_set(attr_req.mh, BFI_MC_IOC, BFI_IOC_H2I_GETATTR_REQ, + bfa_ioc_portid(ioc)); + bfa_dma_be_addr_set(attr_req.attr_addr, ioc->attr_dma.pa); + bfa_ioc_mbox_send(ioc, &attr_req, sizeof(attr_req)); +} + +static void +bfa_ioc_hb_check(void *cbarg) +{ + struct bfa_ioc_s *ioc = cbarg; + u32 hb_count; + + hb_count = bfa_reg_read(ioc->ioc_regs.heartbeat); + if (ioc->hb_count == hb_count) { + ioc->hb_fail++; + } else { + ioc->hb_count = hb_count; + ioc->hb_fail = 0; + } + + if (ioc->hb_fail >= BFA_IOC_HB_FAIL_MAX) { + bfa_log(ioc->logm, BFA_LOG_HAL_HEARTBEAT_FAILURE, hb_count); + ioc->hb_fail = 0; + bfa_ioc_recover(ioc); + return; + } + + bfa_ioc_mbox_poll(ioc); + bfa_timer_begin(ioc->timer_mod, &ioc->ioc_timer, bfa_ioc_hb_check, ioc, + BFA_IOC_HB_TOV); +} + +static void +bfa_ioc_hb_monitor(struct bfa_ioc_s *ioc) +{ + ioc->hb_fail = 0; + ioc->hb_count = bfa_reg_read(ioc->ioc_regs.heartbeat); + bfa_timer_begin(ioc->timer_mod, &ioc->ioc_timer, bfa_ioc_hb_check, ioc, + BFA_IOC_HB_TOV); +} + +static void +bfa_ioc_hb_stop(struct bfa_ioc_s *ioc) +{ + bfa_timer_stop(&ioc->ioc_timer); +} + +/** + * Host to LPU mailbox message addresses + */ +static struct { + u32 hfn_mbox, lpu_mbox, hfn_pgn; +} iocreg_fnreg[] = { + { + HOSTFN0_LPU_MBOX0_0, LPU_HOSTFN0_MBOX0_0, HOST_PAGE_NUM_FN0}, { + HOSTFN1_LPU_MBOX0_8, LPU_HOSTFN1_MBOX0_8, HOST_PAGE_NUM_FN1}, { + HOSTFN2_LPU_MBOX0_0, LPU_HOSTFN2_MBOX0_0, HOST_PAGE_NUM_FN2}, { + HOSTFN3_LPU_MBOX0_8, LPU_HOSTFN3_MBOX0_8, HOST_PAGE_NUM_FN3} +}; + +/** + * Host <-> LPU mailbox command/status registers - port 0 + */ +static struct { + u32 hfn, lpu; +} iocreg_mbcmd_p0[] = { + { + HOSTFN0_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN0_MBOX0_CMD_STAT}, { + HOSTFN1_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN1_MBOX0_CMD_STAT}, { + HOSTFN2_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN2_MBOX0_CMD_STAT}, { + HOSTFN3_LPU0_MBOX0_CMD_STAT, LPU0_HOSTFN3_MBOX0_CMD_STAT} +}; + +/** + * Host <-> LPU mailbox command/status registers - port 1 + */ +static struct { + u32 hfn, lpu; +} iocreg_mbcmd_p1[] = { + { + HOSTFN0_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN0_MBOX0_CMD_STAT}, { + HOSTFN1_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN1_MBOX0_CMD_STAT}, { + HOSTFN2_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN2_MBOX0_CMD_STAT}, { + HOSTFN3_LPU1_MBOX0_CMD_STAT, LPU1_HOSTFN3_MBOX0_CMD_STAT} +}; + +/** + * Shared IRQ handling in INTX mode + */ +static struct { + u32 isr, msk; +} iocreg_shirq_next[] = { + { + HOSTFN1_INT_STATUS, HOSTFN1_INT_MSK}, { + HOSTFN2_INT_STATUS, HOSTFN2_INT_MSK}, { + HOSTFN3_INT_STATUS, HOSTFN3_INT_MSK}, { +HOSTFN0_INT_STATUS, HOSTFN0_INT_MSK},}; + +static void +bfa_ioc_reg_init(struct bfa_ioc_s *ioc) +{ + bfa_os_addr_t rb; + int pcifn = bfa_ioc_pcifn(ioc); + + rb = bfa_ioc_bar0(ioc); + + ioc->ioc_regs.hfn_mbox = rb + iocreg_fnreg[pcifn].hfn_mbox; + ioc->ioc_regs.lpu_mbox = rb + iocreg_fnreg[pcifn].lpu_mbox; + ioc->ioc_regs.host_page_num_fn = rb + iocreg_fnreg[pcifn].hfn_pgn; + + if (ioc->port_id == 0) { + ioc->ioc_regs.heartbeat = rb + BFA_IOC0_HBEAT_REG; + ioc->ioc_regs.ioc_fwstate = rb + BFA_IOC0_STATE_REG; + ioc->ioc_regs.hfn_mbox_cmd = rb + iocreg_mbcmd_p0[pcifn].hfn; + ioc->ioc_regs.lpu_mbox_cmd = rb + iocreg_mbcmd_p0[pcifn].lpu; + ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P0; + } else { + ioc->ioc_regs.heartbeat = (rb + BFA_IOC1_HBEAT_REG); + ioc->ioc_regs.ioc_fwstate = (rb + BFA_IOC1_STATE_REG); + ioc->ioc_regs.hfn_mbox_cmd = rb + iocreg_mbcmd_p1[pcifn].hfn; + ioc->ioc_regs.lpu_mbox_cmd = rb + iocreg_mbcmd_p1[pcifn].lpu; + ioc->ioc_regs.ll_halt = rb + FW_INIT_HALT_P1; + } + + /** + * Shared IRQ handling in INTX mode + */ + ioc->ioc_regs.shirq_isr_next = rb + iocreg_shirq_next[pcifn].isr; + ioc->ioc_regs.shirq_msk_next = rb + iocreg_shirq_next[pcifn].msk; + + /* + * PSS control registers + */ + ioc->ioc_regs.pss_ctl_reg = (rb + PSS_CTL_REG); + ioc->ioc_regs.app_pll_fast_ctl_reg = (rb + APP_PLL_425_CTL_REG); + ioc->ioc_regs.app_pll_slow_ctl_reg = (rb + APP_PLL_312_CTL_REG); + + /* + * IOC semaphore registers and serialization + */ + ioc->ioc_regs.ioc_sem_reg = (rb + HOST_SEM0_REG); + ioc->ioc_regs.ioc_usage_sem_reg = (rb + HOST_SEM1_REG); + ioc->ioc_regs.ioc_usage_reg = (rb + BFA_FW_USE_COUNT); + + /** + * sram memory access + */ + ioc->ioc_regs.smem_page_start = (rb + PSS_SMEM_PAGE_START); + ioc->ioc_regs.smem_pg0 = BFI_IOC_SMEM_PG0_CB; + if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT) + ioc->ioc_regs.smem_pg0 = BFI_IOC_SMEM_PG0_CT; +} + +/** + * Initiate a full firmware download. + */ +static void +bfa_ioc_download_fw(struct bfa_ioc_s *ioc, u32 boot_type, + u32 boot_param) +{ + u32 *fwimg; + u32 pgnum, pgoff; + u32 loff = 0; + u32 chunkno = 0; + u32 i; + + /** + * Initialize LMEM first before code download + */ + bfa_ioc_lmem_init(ioc); + + /** + * Flash based firmware boot + */ + bfa_trc(ioc, bfa_ioc_fwimg_get_size(ioc)); + if (bfa_ioc_fwimg_get_size(ioc) < BFA_IOC_FWIMG_MINSZ) + boot_type = BFI_BOOT_TYPE_FLASH; + fwimg = bfa_ioc_fwimg_get_chunk(ioc, chunkno); + fwimg[BFI_BOOT_TYPE_OFF / sizeof(u32)] = bfa_os_swap32(boot_type); + fwimg[BFI_BOOT_PARAM_OFF / sizeof(u32)] = + bfa_os_swap32(boot_param); + + pgnum = bfa_ioc_smem_pgnum(ioc, loff); + pgoff = bfa_ioc_smem_pgoff(ioc, loff); + + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum); + + for (i = 0; i < bfa_ioc_fwimg_get_size(ioc); i++) { + + if (BFA_FLASH_CHUNK_NO(i) != chunkno) { + chunkno = BFA_FLASH_CHUNK_NO(i); + fwimg = bfa_ioc_fwimg_get_chunk(ioc, + BFA_FLASH_CHUNK_ADDR(chunkno)); + } + + /** + * write smem + */ + bfa_mem_write(ioc->ioc_regs.smem_page_start, loff, + fwimg[BFA_FLASH_OFFSET_IN_CHUNK(i)]); + + loff += sizeof(u32); + + /** + * handle page offset wrap around + */ + loff = PSS_SMEM_PGOFF(loff); + if (loff == 0) { + pgnum++; + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum); + } + } + + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, + bfa_ioc_smem_pgnum(ioc, 0)); +} + +static void +bfa_ioc_reset(struct bfa_ioc_s *ioc, bfa_boolean_t force) +{ + bfa_ioc_hwinit(ioc, force); +} + +/** + * Update BFA configuration from firmware configuration. + */ +static void +bfa_ioc_getattr_reply(struct bfa_ioc_s *ioc) +{ + struct bfi_ioc_attr_s *attr = ioc->attr; + + attr->adapter_prop = bfa_os_ntohl(attr->adapter_prop); + attr->maxfrsize = bfa_os_ntohs(attr->maxfrsize); + + bfa_fsm_send_event(ioc, IOC_E_FWRSP_GETATTR); +} + +/** + * Attach time initialization of mbox logic. + */ +static void +bfa_ioc_mbox_attach(struct bfa_ioc_s *ioc) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + int mc; + + INIT_LIST_HEAD(&mod->cmd_q); + for (mc = 0; mc < BFI_MC_MAX; mc++) { + mod->mbhdlr[mc].cbfn = NULL; + mod->mbhdlr[mc].cbarg = ioc->bfa; + } +} + +/** + * Mbox poll timer -- restarts any pending mailbox requests. + */ +static void +bfa_ioc_mbox_poll(struct bfa_ioc_s *ioc) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + struct bfa_mbox_cmd_s *cmd; + u32 stat; + + /** + * If no command pending, do nothing + */ + if (list_empty(&mod->cmd_q)) + return; + + /** + * If previous command is not yet fetched by firmware, do nothing + */ + stat = bfa_reg_read(ioc->ioc_regs.hfn_mbox_cmd); + if (stat) + return; + + /** + * Enqueue command to firmware. + */ + bfa_q_deq(&mod->cmd_q, &cmd); + bfa_ioc_mbox_send(ioc, cmd->msg, sizeof(cmd->msg)); +} + +/** + * Cleanup any pending requests. + */ +static void +bfa_ioc_mbox_hbfail(struct bfa_ioc_s *ioc) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + struct bfa_mbox_cmd_s *cmd; + + while (!list_empty(&mod->cmd_q)) + bfa_q_deq(&mod->cmd_q, &cmd); +} + +/** + * Initialize IOC to port mapping. + */ + +#define FNC_PERS_FN_SHIFT(__fn) ((__fn) * 8) +static void +bfa_ioc_map_port(struct bfa_ioc_s *ioc) +{ + bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva; + u32 r32; + + /** + * For crossbow, port id is same as pci function. + */ + if (ioc->pcidev.device_id != BFA_PCI_DEVICE_ID_CT) { + ioc->port_id = bfa_ioc_pcifn(ioc); + return; + } + + /** + * For catapult, base port id on personality register and IOC type + */ + r32 = bfa_reg_read(rb + FNC_PERS_REG); + r32 >>= FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc)); + ioc->port_id = (r32 & __F0_PORT_MAP_MK) >> __F0_PORT_MAP_SH; + + bfa_trc(ioc, bfa_ioc_pcifn(ioc)); + bfa_trc(ioc, ioc->port_id); +} + + + +/** + * bfa_ioc_public + */ + +/** +* Set interrupt mode for a function: INTX or MSIX + */ +void +bfa_ioc_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t msix) +{ + bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva; + u32 r32, mode; + + r32 = bfa_reg_read(rb + FNC_PERS_REG); + bfa_trc(ioc, r32); + + mode = (r32 >> FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc))) & + __F0_INTX_STATUS; + + /** + * If already in desired mode, do not change anything + */ + if (!msix && mode) + return; + + if (msix) + mode = __F0_INTX_STATUS_MSIX; + else + mode = __F0_INTX_STATUS_INTA; + + r32 &= ~(__F0_INTX_STATUS << FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc))); + r32 |= (mode << FNC_PERS_FN_SHIFT(bfa_ioc_pcifn(ioc))); + bfa_trc(ioc, r32); + + bfa_reg_write(rb + FNC_PERS_REG, r32); +} + +bfa_status_t +bfa_ioc_pll_init(struct bfa_ioc_s *ioc) +{ + bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva; + u32 pll_sclk, pll_fclk, r32; + + if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT) { + pll_sclk = + __APP_PLL_312_ENABLE | __APP_PLL_312_LRESETN | + __APP_PLL_312_RSEL200500 | __APP_PLL_312_P0_1(0U) | + __APP_PLL_312_JITLMT0_1(3U) | + __APP_PLL_312_CNTLMT0_1(1U); + pll_fclk = + __APP_PLL_425_ENABLE | __APP_PLL_425_LRESETN | + __APP_PLL_425_RSEL200500 | __APP_PLL_425_P0_1(0U) | + __APP_PLL_425_JITLMT0_1(3U) | + __APP_PLL_425_CNTLMT0_1(1U); + + /** + * For catapult, choose operational mode FC/FCoE + */ + if (ioc->fcmode) { + bfa_reg_write((rb + OP_MODE), 0); + bfa_reg_write((rb + ETH_MAC_SER_REG), + __APP_EMS_CMLCKSEL | __APP_EMS_REFCKBUFEN2 + | __APP_EMS_CHANNEL_SEL); + } else { + ioc->pllinit = BFA_TRUE; + bfa_reg_write((rb + OP_MODE), __GLOBAL_FCOE_MODE); + bfa_reg_write((rb + ETH_MAC_SER_REG), + __APP_EMS_REFCKBUFEN1); + } + } else { + pll_sclk = + __APP_PLL_312_ENABLE | __APP_PLL_312_LRESETN | + __APP_PLL_312_P0_1(3U) | __APP_PLL_312_JITLMT0_1(3U) | + __APP_PLL_312_CNTLMT0_1(3U); + pll_fclk = + __APP_PLL_425_ENABLE | __APP_PLL_425_LRESETN | + __APP_PLL_425_RSEL200500 | __APP_PLL_425_P0_1(3U) | + __APP_PLL_425_JITLMT0_1(3U) | + __APP_PLL_425_CNTLMT0_1(3U); + } + + bfa_reg_write((rb + BFA_IOC0_STATE_REG), BFI_IOC_UNINIT); + bfa_reg_write((rb + BFA_IOC1_STATE_REG), BFI_IOC_UNINIT); + + bfa_reg_write((rb + HOSTFN0_INT_MSK), 0xffffffffU); + bfa_reg_write((rb + HOSTFN1_INT_MSK), 0xffffffffU); + bfa_reg_write((rb + HOSTFN0_INT_STATUS), 0xffffffffU); + bfa_reg_write((rb + HOSTFN1_INT_STATUS), 0xffffffffU); + bfa_reg_write((rb + HOSTFN0_INT_MSK), 0xffffffffU); + bfa_reg_write((rb + HOSTFN1_INT_MSK), 0xffffffffU); + + bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg, + __APP_PLL_312_LOGIC_SOFT_RESET); + bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg, + __APP_PLL_312_BYPASS | __APP_PLL_312_LOGIC_SOFT_RESET); + bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg, + __APP_PLL_425_LOGIC_SOFT_RESET); + bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg, + __APP_PLL_425_BYPASS | __APP_PLL_425_LOGIC_SOFT_RESET); + bfa_os_udelay(2); + bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg, + __APP_PLL_312_LOGIC_SOFT_RESET); + bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg, + __APP_PLL_425_LOGIC_SOFT_RESET); + + bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg, + pll_sclk | __APP_PLL_312_LOGIC_SOFT_RESET); + bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg, + pll_fclk | __APP_PLL_425_LOGIC_SOFT_RESET); + + /** + * Wait for PLLs to lock. + */ + bfa_os_udelay(2000); + bfa_reg_write((rb + HOSTFN0_INT_STATUS), 0xffffffffU); + bfa_reg_write((rb + HOSTFN1_INT_STATUS), 0xffffffffU); + + bfa_reg_write(ioc->ioc_regs.app_pll_slow_ctl_reg, pll_sclk); + bfa_reg_write(ioc->ioc_regs.app_pll_fast_ctl_reg, pll_fclk); + + if (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT) { + bfa_reg_write((rb + MBIST_CTL_REG), __EDRAM_BISTR_START); + bfa_os_udelay(1000); + r32 = bfa_reg_read((rb + MBIST_STAT_REG)); + bfa_trc(ioc, r32); + } + + return BFA_STATUS_OK; +} + +/** + * Interface used by diag module to do firmware boot with memory test + * as the entry vector. + */ +void +bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type, u32 boot_param) +{ + bfa_os_addr_t rb; + + bfa_ioc_stats(ioc, ioc_boots); + + if (bfa_ioc_pll_init(ioc) != BFA_STATUS_OK) + return; + + /** + * Initialize IOC state of all functions on a chip reset. + */ + rb = ioc->pcidev.pci_bar_kva; + if (boot_param == BFI_BOOT_TYPE_MEMTEST) { + bfa_reg_write((rb + BFA_IOC0_STATE_REG), BFI_IOC_MEMTEST); + bfa_reg_write((rb + BFA_IOC1_STATE_REG), BFI_IOC_MEMTEST); + } else { + bfa_reg_write((rb + BFA_IOC0_STATE_REG), BFI_IOC_INITING); + bfa_reg_write((rb + BFA_IOC1_STATE_REG), BFI_IOC_INITING); + } + + bfa_ioc_download_fw(ioc, boot_type, boot_param); + + /** + * Enable interrupts just before starting LPU + */ + ioc->cbfn->reset_cbfn(ioc->bfa); + bfa_ioc_lpu_start(ioc); +} + +/** + * Enable/disable IOC failure auto recovery. + */ +void +bfa_ioc_auto_recover(bfa_boolean_t auto_recover) +{ + bfa_auto_recover = BFA_FALSE; +} + + +bfa_boolean_t +bfa_ioc_is_operational(struct bfa_ioc_s *ioc) +{ + return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_op); +} + +void +bfa_ioc_msgget(struct bfa_ioc_s *ioc, void *mbmsg) +{ + u32 *msgp = mbmsg; + u32 r32; + int i; + + /** + * read the MBOX msg + */ + for (i = 0; i < (sizeof(union bfi_ioc_i2h_msg_u) / sizeof(u32)); + i++) { + r32 = bfa_reg_read(ioc->ioc_regs.lpu_mbox + + i * sizeof(u32)); + msgp[i] = bfa_os_htonl(r32); + } + + /** + * turn off mailbox interrupt by clearing mailbox status + */ + bfa_reg_write(ioc->ioc_regs.lpu_mbox_cmd, 1); + bfa_reg_read(ioc->ioc_regs.lpu_mbox_cmd); +} + +void +bfa_ioc_isr(struct bfa_ioc_s *ioc, struct bfi_mbmsg_s *m) +{ + union bfi_ioc_i2h_msg_u *msg; + + msg = (union bfi_ioc_i2h_msg_u *)m; + + bfa_ioc_stats(ioc, ioc_isrs); + + switch (msg->mh.msg_id) { + case BFI_IOC_I2H_HBEAT: + break; + + case BFI_IOC_I2H_READY_EVENT: + bfa_fsm_send_event(ioc, IOC_E_FWREADY); + break; + + case BFI_IOC_I2H_ENABLE_REPLY: + bfa_fsm_send_event(ioc, IOC_E_FWRSP_ENABLE); + break; + + case BFI_IOC_I2H_DISABLE_REPLY: + bfa_fsm_send_event(ioc, IOC_E_FWRSP_DISABLE); + break; + + case BFI_IOC_I2H_GETATTR_REPLY: + bfa_ioc_getattr_reply(ioc); + break; + + default: + bfa_trc(ioc, msg->mh.msg_id); + bfa_assert(0); + } +} + +/** + * IOC attach time initialization and setup. + * + * @param[in] ioc memory for IOC + * @param[in] bfa driver instance structure + * @param[in] trcmod kernel trace module + * @param[in] aen kernel aen event module + * @param[in] logm kernel logging module + */ +void +bfa_ioc_attach(struct bfa_ioc_s *ioc, void *bfa, struct bfa_ioc_cbfn_s *cbfn, + struct bfa_timer_mod_s *timer_mod, struct bfa_trc_mod_s *trcmod, + struct bfa_aen_s *aen, struct bfa_log_mod_s *logm) +{ + ioc->bfa = bfa; + ioc->cbfn = cbfn; + ioc->timer_mod = timer_mod; + ioc->trcmod = trcmod; + ioc->aen = aen; + ioc->logm = logm; + ioc->fcmode = BFA_FALSE; + ioc->pllinit = BFA_FALSE; + ioc->dbg_fwsave_once = BFA_TRUE; + + bfa_ioc_mbox_attach(ioc); + INIT_LIST_HEAD(&ioc->hb_notify_q); + + bfa_fsm_set_state(ioc, bfa_ioc_sm_reset); +} + +/** + * Driver detach time IOC cleanup. + */ +void +bfa_ioc_detach(struct bfa_ioc_s *ioc) +{ + bfa_fsm_send_event(ioc, IOC_E_DETACH); +} + +/** + * Setup IOC PCI properties. + * + * @param[in] pcidev PCI device information for this IOC + */ +void +bfa_ioc_pci_init(struct bfa_ioc_s *ioc, struct bfa_pcidev_s *pcidev, + enum bfi_mclass mc) +{ + ioc->ioc_mc = mc; + ioc->pcidev = *pcidev; + ioc->ctdev = (ioc->pcidev.device_id == BFA_PCI_DEVICE_ID_CT); + ioc->cna = ioc->ctdev && !ioc->fcmode; + + bfa_ioc_map_port(ioc); + bfa_ioc_reg_init(ioc); +} + +/** + * Initialize IOC dma memory + * + * @param[in] dm_kva kernel virtual address of IOC dma memory + * @param[in] dm_pa physical address of IOC dma memory + */ +void +bfa_ioc_mem_claim(struct bfa_ioc_s *ioc, u8 *dm_kva, u64 dm_pa) +{ + /** + * dma memory for firmware attribute + */ + ioc->attr_dma.kva = dm_kva; + ioc->attr_dma.pa = dm_pa; + ioc->attr = (struct bfi_ioc_attr_s *)dm_kva; +} + +/** + * Return size of dma memory required. + */ +u32 +bfa_ioc_meminfo(void) +{ + return BFA_ROUNDUP(sizeof(struct bfi_ioc_attr_s), BFA_DMA_ALIGN_SZ); +} + +void +bfa_ioc_enable(struct bfa_ioc_s *ioc) +{ + bfa_ioc_stats(ioc, ioc_enables); + ioc->dbg_fwsave_once = BFA_TRUE; + + bfa_fsm_send_event(ioc, IOC_E_ENABLE); +} + +void +bfa_ioc_disable(struct bfa_ioc_s *ioc) +{ + bfa_ioc_stats(ioc, ioc_disables); + bfa_fsm_send_event(ioc, IOC_E_DISABLE); +} + +/** + * Returns memory required for saving firmware trace in case of crash. + * Driver must call this interface to allocate memory required for + * automatic saving of firmware trace. Driver should call + * bfa_ioc_debug_memclaim() right after bfa_ioc_attach() to setup this + * trace memory. + */ +int +bfa_ioc_debug_trcsz(bfa_boolean_t auto_recover) +{ +return (auto_recover) ? BFA_DBG_FWTRC_LEN : 0; +} + +/** + * Initialize memory for saving firmware trace. Driver must initialize + * trace memory before call bfa_ioc_enable(). + */ +void +bfa_ioc_debug_memclaim(struct bfa_ioc_s *ioc, void *dbg_fwsave) +{ + bfa_assert(ioc->auto_recover); + ioc->dbg_fwsave = dbg_fwsave; + ioc->dbg_fwsave_len = bfa_ioc_debug_trcsz(ioc->auto_recover); +} + +u32 +bfa_ioc_smem_pgnum(struct bfa_ioc_s *ioc, u32 fmaddr) +{ + return PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, fmaddr); +} + +u32 +bfa_ioc_smem_pgoff(struct bfa_ioc_s *ioc, u32 fmaddr) +{ + return PSS_SMEM_PGOFF(fmaddr); +} + +/** + * Register mailbox message handler functions + * + * @param[in] ioc IOC instance + * @param[in] mcfuncs message class handler functions + */ +void +bfa_ioc_mbox_register(struct bfa_ioc_s *ioc, bfa_ioc_mbox_mcfunc_t *mcfuncs) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + int mc; + + for (mc = 0; mc < BFI_MC_MAX; mc++) + mod->mbhdlr[mc].cbfn = mcfuncs[mc]; +} + +/** + * Register mailbox message handler function, to be called by common modules + */ +void +bfa_ioc_mbox_regisr(struct bfa_ioc_s *ioc, enum bfi_mclass mc, + bfa_ioc_mbox_mcfunc_t cbfn, void *cbarg) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + + mod->mbhdlr[mc].cbfn = cbfn; + mod->mbhdlr[mc].cbarg = cbarg; +} + +/** + * Queue a mailbox command request to firmware. Waits if mailbox is busy. + * Responsibility of caller to serialize + * + * @param[in] ioc IOC instance + * @param[i] cmd Mailbox command + */ +void +bfa_ioc_mbox_queue(struct bfa_ioc_s *ioc, struct bfa_mbox_cmd_s *cmd) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + u32 stat; + + /** + * If a previous command is pending, queue new command + */ + if (!list_empty(&mod->cmd_q)) { + list_add_tail(&cmd->qe, &mod->cmd_q); + return; + } + + /** + * If mailbox is busy, queue command for poll timer + */ + stat = bfa_reg_read(ioc->ioc_regs.hfn_mbox_cmd); + if (stat) { + list_add_tail(&cmd->qe, &mod->cmd_q); + return; + } + + /** + * mailbox is free -- queue command to firmware + */ + bfa_ioc_mbox_send(ioc, cmd->msg, sizeof(cmd->msg)); +} + +/** + * Handle mailbox interrupts + */ +void +bfa_ioc_mbox_isr(struct bfa_ioc_s *ioc) +{ + struct bfa_ioc_mbox_mod_s *mod = &ioc->mbox_mod; + struct bfi_mbmsg_s m; + int mc; + + bfa_ioc_msgget(ioc, &m); + + /** + * Treat IOC message class as special. + */ + mc = m.mh.msg_class; + if (mc == BFI_MC_IOC) { + bfa_ioc_isr(ioc, &m); + return; + } + + if ((mc > BFI_MC_MAX) || (mod->mbhdlr[mc].cbfn == NULL)) + return; + + mod->mbhdlr[mc].cbfn(mod->mbhdlr[mc].cbarg, &m); +} + +void +bfa_ioc_error_isr(struct bfa_ioc_s *ioc) +{ + bfa_fsm_send_event(ioc, IOC_E_HWERROR); +} + +#ifndef BFA_BIOS_BUILD + +/** + * return true if IOC is disabled + */ +bfa_boolean_t +bfa_ioc_is_disabled(struct bfa_ioc_s *ioc) +{ + return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling) + || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled)); +} + +/** + * return true if IOC firmware is different. + */ +bfa_boolean_t +bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc) +{ + return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_reset) + || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_fwcheck) + || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_mismatch)); +} + +#define bfa_ioc_state_disabled(__sm) \ + (((__sm) == BFI_IOC_UNINIT) || \ + ((__sm) == BFI_IOC_INITING) || \ + ((__sm) == BFI_IOC_HWINIT) || \ + ((__sm) == BFI_IOC_DISABLED) || \ + ((__sm) == BFI_IOC_HBFAIL) || \ + ((__sm) == BFI_IOC_CFG_DISABLED)) + +/** + * Check if adapter is disabled -- both IOCs should be in a disabled + * state. + */ +bfa_boolean_t +bfa_ioc_adapter_is_disabled(struct bfa_ioc_s *ioc) +{ + u32 ioc_state; + bfa_os_addr_t rb = ioc->pcidev.pci_bar_kva; + + if (!bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled)) + return BFA_FALSE; + + ioc_state = bfa_reg_read(rb + BFA_IOC0_STATE_REG); + if (!bfa_ioc_state_disabled(ioc_state)) + return BFA_FALSE; + + ioc_state = bfa_reg_read(rb + BFA_IOC1_STATE_REG); + if (!bfa_ioc_state_disabled(ioc_state)) + return BFA_FALSE; + + return BFA_TRUE; +} + +/** + * Add to IOC heartbeat failure notification queue. To be used by common + * modules such as + */ +void +bfa_ioc_hbfail_register(struct bfa_ioc_s *ioc, + struct bfa_ioc_hbfail_notify_s *notify) +{ + list_add_tail(¬ify->qe, &ioc->hb_notify_q); +} + +#define BFA_MFG_NAME "Brocade" +void +bfa_ioc_get_adapter_attr(struct bfa_ioc_s *ioc, + struct bfa_adapter_attr_s *ad_attr) +{ + struct bfi_ioc_attr_s *ioc_attr; + char model[BFA_ADAPTER_MODEL_NAME_LEN]; + + ioc_attr = ioc->attr; + bfa_os_memcpy((void *)&ad_attr->serial_num, + (void *)ioc_attr->brcd_serialnum, + BFA_ADAPTER_SERIAL_NUM_LEN); + + bfa_os_memcpy(&ad_attr->fw_ver, ioc_attr->fw_version, BFA_VERSION_LEN); + bfa_os_memcpy(&ad_attr->optrom_ver, ioc_attr->optrom_version, + BFA_VERSION_LEN); + bfa_os_memcpy(&ad_attr->manufacturer, BFA_MFG_NAME, + BFA_ADAPTER_MFG_NAME_LEN); + bfa_os_memcpy(&ad_attr->vpd, &ioc_attr->vpd, + sizeof(struct bfa_mfg_vpd_s)); + + ad_attr->nports = BFI_ADAPTER_GETP(NPORTS, ioc_attr->adapter_prop); + ad_attr->max_speed = BFI_ADAPTER_GETP(SPEED, ioc_attr->adapter_prop); + + /** + * model name + */ + if (BFI_ADAPTER_GETP(SPEED, ioc_attr->adapter_prop) == 10) { + strcpy(model, "BR-10?0"); + model[5] = '0' + ad_attr->nports; + } else { + strcpy(model, "Brocade-??5"); + model[8] = + '0' + BFI_ADAPTER_GETP(SPEED, ioc_attr->adapter_prop); + model[9] = '0' + ad_attr->nports; + } + + if (BFI_ADAPTER_IS_SPECIAL(ioc_attr->adapter_prop)) + ad_attr->prototype = 1; + else + ad_attr->prototype = 0; + + bfa_os_memcpy(&ad_attr->model, model, BFA_ADAPTER_MODEL_NAME_LEN); + bfa_os_memcpy(&ad_attr->model_descr, &ad_attr->model, + BFA_ADAPTER_MODEL_NAME_LEN); + + ad_attr->pwwn = bfa_ioc_get_pwwn(ioc); + ad_attr->mac = bfa_ioc_get_mac(ioc); + + ad_attr->pcie_gen = ioc_attr->pcie_gen; + ad_attr->pcie_lanes = ioc_attr->pcie_lanes; + ad_attr->pcie_lanes_orig = ioc_attr->pcie_lanes_orig; + ad_attr->asic_rev = ioc_attr->asic_rev; + ad_attr->hw_ver[0] = 'R'; + ad_attr->hw_ver[1] = 'e'; + ad_attr->hw_ver[2] = 'v'; + ad_attr->hw_ver[3] = '-'; + ad_attr->hw_ver[4] = ioc_attr->asic_rev; + ad_attr->hw_ver[5] = '\0'; + + ad_attr->cna_capable = ioc->cna; +} + +void +bfa_ioc_get_attr(struct bfa_ioc_s *ioc, struct bfa_ioc_attr_s *ioc_attr) +{ + bfa_os_memset((void *)ioc_attr, 0, sizeof(struct bfa_ioc_attr_s)); + + ioc_attr->state = bfa_sm_to_state(ioc_sm_table, ioc->fsm); + ioc_attr->port_id = ioc->port_id; + + if (!ioc->ctdev) + ioc_attr->ioc_type = BFA_IOC_TYPE_FC; + else if (ioc->ioc_mc == BFI_MC_IOCFC) + ioc_attr->ioc_type = BFA_IOC_TYPE_FCoE; + else if (ioc->ioc_mc == BFI_MC_LL) + ioc_attr->ioc_type = BFA_IOC_TYPE_LL; + + bfa_ioc_get_adapter_attr(ioc, &ioc_attr->adapter_attr); + + ioc_attr->pci_attr.device_id = ioc->pcidev.device_id; + ioc_attr->pci_attr.pcifn = ioc->pcidev.pci_func; + ioc_attr->pci_attr.chip_rev[0] = 'R'; + ioc_attr->pci_attr.chip_rev[1] = 'e'; + ioc_attr->pci_attr.chip_rev[2] = 'v'; + ioc_attr->pci_attr.chip_rev[3] = '-'; + ioc_attr->pci_attr.chip_rev[4] = ioc_attr->adapter_attr.asic_rev; + ioc_attr->pci_attr.chip_rev[5] = '\0'; +} + +/** + * hal_wwn_public + */ +wwn_t +bfa_ioc_get_pwwn(struct bfa_ioc_s *ioc) +{ + union { + wwn_t wwn; + u8 byte[sizeof(wwn_t)]; + } + w; + + w.wwn = ioc->attr->mfg_wwn; + + if (bfa_ioc_portid(ioc) == 1) + w.byte[7]++; + + return w.wwn; +} + +wwn_t +bfa_ioc_get_nwwn(struct bfa_ioc_s *ioc) +{ + union { + wwn_t wwn; + u8 byte[sizeof(wwn_t)]; + } + w; + + w.wwn = ioc->attr->mfg_wwn; + + if (bfa_ioc_portid(ioc) == 1) + w.byte[7]++; + + w.byte[0] = 0x20; + + return w.wwn; +} + +wwn_t +bfa_ioc_get_wwn_naa5(struct bfa_ioc_s *ioc, u16 inst) +{ + union { + wwn_t wwn; + u8 byte[sizeof(wwn_t)]; + } + w , w5; + + bfa_trc(ioc, inst); + + w.wwn = ioc->attr->mfg_wwn; + w5.byte[0] = 0x50 | w.byte[2] >> 4; + w5.byte[1] = w.byte[2] << 4 | w.byte[3] >> 4; + w5.byte[2] = w.byte[3] << 4 | w.byte[4] >> 4; + w5.byte[3] = w.byte[4] << 4 | w.byte[5] >> 4; + w5.byte[4] = w.byte[5] << 4 | w.byte[6] >> 4; + w5.byte[5] = w.byte[6] << 4 | w.byte[7] >> 4; + w5.byte[6] = w.byte[7] << 4 | (inst & 0x0f00) >> 8; + w5.byte[7] = (inst & 0xff); + + return w5.wwn; +} + +u64 +bfa_ioc_get_adid(struct bfa_ioc_s *ioc) +{ + return ioc->attr->mfg_wwn; +} + +mac_t +bfa_ioc_get_mac(struct bfa_ioc_s *ioc) +{ + mac_t mac; + + mac = ioc->attr->mfg_mac; + mac.mac[MAC_ADDRLEN - 1] += bfa_ioc_pcifn(ioc); + + return mac; +} + +void +bfa_ioc_set_fcmode(struct bfa_ioc_s *ioc) +{ + ioc->fcmode = BFA_TRUE; + ioc->port_id = bfa_ioc_pcifn(ioc); +} + +bfa_boolean_t +bfa_ioc_get_fcmode(struct bfa_ioc_s *ioc) +{ + return ioc->fcmode || (ioc->pcidev.device_id != BFA_PCI_DEVICE_ID_CT); +} + +/** + * Return true if interrupt should be claimed. + */ +bfa_boolean_t +bfa_ioc_intx_claim(struct bfa_ioc_s *ioc) +{ + u32 isr, msk; + + /** + * Always claim if not catapult. + */ + if (!ioc->ctdev) + return BFA_TRUE; + + /** + * FALSE if next device is claiming interrupt. + * TRUE if next device is not interrupting or not present. + */ + msk = bfa_reg_read(ioc->ioc_regs.shirq_msk_next); + isr = bfa_reg_read(ioc->ioc_regs.shirq_isr_next); + return !(isr & ~msk); +} + +/** + * Send AEN notification + */ +static void +bfa_ioc_aen_post(struct bfa_ioc_s *ioc, enum bfa_ioc_aen_event event) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = ioc->logm; + s32 inst_num = 0; + struct bfa_ioc_attr_s ioc_attr; + + switch (event) { + case BFA_IOC_AEN_HBGOOD: + bfa_log(logmod, BFA_AEN_IOC_HBGOOD, inst_num); + break; + case BFA_IOC_AEN_HBFAIL: + bfa_log(logmod, BFA_AEN_IOC_HBFAIL, inst_num); + break; + case BFA_IOC_AEN_ENABLE: + bfa_log(logmod, BFA_AEN_IOC_ENABLE, inst_num); + break; + case BFA_IOC_AEN_DISABLE: + bfa_log(logmod, BFA_AEN_IOC_DISABLE, inst_num); + break; + case BFA_IOC_AEN_FWMISMATCH: + bfa_log(logmod, BFA_AEN_IOC_FWMISMATCH, inst_num); + break; + default: + break; + } + + memset(&aen_data.ioc.pwwn, 0, sizeof(aen_data.ioc.pwwn)); + memset(&aen_data.ioc.mac, 0, sizeof(aen_data.ioc.mac)); + bfa_ioc_get_attr(ioc, &ioc_attr); + switch (ioc_attr.ioc_type) { + case BFA_IOC_TYPE_FC: + aen_data.ioc.pwwn = bfa_ioc_get_pwwn(ioc); + break; + case BFA_IOC_TYPE_FCoE: + aen_data.ioc.pwwn = bfa_ioc_get_pwwn(ioc); + aen_data.ioc.mac = bfa_ioc_get_mac(ioc); + break; + case BFA_IOC_TYPE_LL: + aen_data.ioc.mac = bfa_ioc_get_mac(ioc); + break; + default: + bfa_assert(ioc_attr.ioc_type == BFA_IOC_TYPE_FC); + break; + } + aen_data.ioc.ioc_type = ioc_attr.ioc_type; +} + +/** + * Retrieve saved firmware trace from a prior IOC failure. + */ +bfa_status_t +bfa_ioc_debug_fwsave(struct bfa_ioc_s *ioc, void *trcdata, int *trclen) +{ + int tlen; + + if (ioc->dbg_fwsave_len == 0) + return BFA_STATUS_ENOFSAVE; + + tlen = *trclen; + if (tlen > ioc->dbg_fwsave_len) + tlen = ioc->dbg_fwsave_len; + + bfa_os_memcpy(trcdata, ioc->dbg_fwsave, tlen); + *trclen = tlen; + return BFA_STATUS_OK; +} + +/** + * Retrieve saved firmware trace from a prior IOC failure. + */ +bfa_status_t +bfa_ioc_debug_fwtrc(struct bfa_ioc_s *ioc, void *trcdata, int *trclen) +{ + u32 pgnum; + u32 loff = BFA_DBG_FWTRC_OFF(bfa_ioc_portid(ioc)); + int i, tlen; + u32 *tbuf = trcdata, r32; + + bfa_trc(ioc, *trclen); + + pgnum = bfa_ioc_smem_pgnum(ioc, loff); + loff = bfa_ioc_smem_pgoff(ioc, loff); + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum); + + tlen = *trclen; + if (tlen > BFA_DBG_FWTRC_LEN) + tlen = BFA_DBG_FWTRC_LEN; + tlen /= sizeof(u32); + + bfa_trc(ioc, tlen); + + for (i = 0; i < tlen; i++) { + r32 = bfa_mem_read(ioc->ioc_regs.smem_page_start, loff); + tbuf[i] = bfa_os_ntohl(r32); + loff += sizeof(u32); + + /** + * handle page offset wrap around + */ + loff = PSS_SMEM_PGOFF(loff); + if (loff == 0) { + pgnum++; + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, pgnum); + } + } + bfa_reg_write(ioc->ioc_regs.host_page_num_fn, + bfa_ioc_smem_pgnum(ioc, 0)); + bfa_trc(ioc, pgnum); + + *trclen = tlen * sizeof(u32); + return BFA_STATUS_OK; +} + +/** + * Save firmware trace if configured. + */ +static void +bfa_ioc_debug_save(struct bfa_ioc_s *ioc) +{ + int tlen; + + if (ioc->dbg_fwsave_len) { + tlen = ioc->dbg_fwsave_len; + bfa_ioc_debug_fwtrc(ioc, ioc->dbg_fwsave, &tlen); + } +} + +/** + * Firmware failure detected. Start recovery actions. + */ +static void +bfa_ioc_recover(struct bfa_ioc_s *ioc) +{ + if (ioc->dbg_fwsave_once) { + ioc->dbg_fwsave_once = BFA_FALSE; + bfa_ioc_debug_save(ioc); + } + + bfa_ioc_stats(ioc, ioc_hbfails); + bfa_fsm_send_event(ioc, IOC_E_HBFAIL); +} + +#else + +static void +bfa_ioc_aen_post(struct bfa_ioc_s *ioc, enum bfa_ioc_aen_event event) +{ +} + +static void +bfa_ioc_recover(struct bfa_ioc_s *ioc) +{ + bfa_assert(0); +} + +#endif + + diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h new file mode 100644 index 000000000000..58efd4b13143 --- /dev/null +++ b/drivers/scsi/bfa/bfa_ioc.h @@ -0,0 +1,259 @@ +/* + * 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. + */ + +#ifndef __BFA_IOC_H__ +#define __BFA_IOC_H__ + +#include +#include +#include +#include +#include + +/** + * PCI device information required by IOC + */ +struct bfa_pcidev_s { + int pci_slot; + u8 pci_func; + u16 device_id; + bfa_os_addr_t pci_bar_kva; +}; + +/** + * Structure used to remember the DMA-able memory block's KVA and Physical + * Address + */ +struct bfa_dma_s { + void *kva; /*! Kernel virtual address */ + u64 pa; /*! Physical address */ +}; + +#define BFA_DMA_ALIGN_SZ 256 +#define BFA_ROUNDUP(_l, _s) (((_l) + ((_s) - 1)) & ~((_s) - 1)) + + + +#define bfa_dma_addr_set(dma_addr, pa) \ + __bfa_dma_addr_set(&dma_addr, (u64)pa) + +static inline void +__bfa_dma_addr_set(union bfi_addr_u *dma_addr, u64 pa) +{ + dma_addr->a32.addr_lo = (u32) pa; + dma_addr->a32.addr_hi = (u32) (bfa_os_u32(pa)); +} + + +#define bfa_dma_be_addr_set(dma_addr, pa) \ + __bfa_dma_be_addr_set(&dma_addr, (u64)pa) +static inline void +__bfa_dma_be_addr_set(union bfi_addr_u *dma_addr, u64 pa) +{ + dma_addr->a32.addr_lo = (u32) bfa_os_htonl(pa); + dma_addr->a32.addr_hi = (u32) bfa_os_htonl(bfa_os_u32(pa)); +} + +struct bfa_ioc_regs_s { + bfa_os_addr_t hfn_mbox_cmd; + bfa_os_addr_t hfn_mbox; + bfa_os_addr_t lpu_mbox_cmd; + bfa_os_addr_t lpu_mbox; + bfa_os_addr_t pss_ctl_reg; + bfa_os_addr_t app_pll_fast_ctl_reg; + bfa_os_addr_t app_pll_slow_ctl_reg; + bfa_os_addr_t ioc_sem_reg; + bfa_os_addr_t ioc_usage_sem_reg; + bfa_os_addr_t ioc_usage_reg; + bfa_os_addr_t host_page_num_fn; + bfa_os_addr_t heartbeat; + bfa_os_addr_t ioc_fwstate; + bfa_os_addr_t ll_halt; + bfa_os_addr_t shirq_isr_next; + bfa_os_addr_t shirq_msk_next; + bfa_os_addr_t smem_page_start; + u32 smem_pg0; +}; + +#define bfa_reg_read(_raddr) bfa_os_reg_read(_raddr) +#define bfa_reg_write(_raddr, _val) bfa_os_reg_write(_raddr, _val) +#define bfa_mem_read(_raddr, _off) bfa_os_mem_read(_raddr, _off) +#define bfa_mem_write(_raddr, _off, _val) \ + bfa_os_mem_write(_raddr, _off, _val) +/** + * IOC Mailbox structures + */ +struct bfa_mbox_cmd_s { + struct list_head qe; + u32 msg[BFI_IOC_MSGSZ]; +}; + +/** + * IOC mailbox module + */ +typedef void (*bfa_ioc_mbox_mcfunc_t)(void *cbarg, struct bfi_mbmsg_s *m); +struct bfa_ioc_mbox_mod_s { + struct list_head cmd_q; /* pending mbox queue */ + int nmclass; /* number of handlers */ + struct { + bfa_ioc_mbox_mcfunc_t cbfn; /* message handlers */ + void *cbarg; + } mbhdlr[BFI_MC_MAX]; +}; + +/** + * IOC callback function interfaces + */ +typedef void (*bfa_ioc_enable_cbfn_t)(void *bfa, enum bfa_status status); +typedef void (*bfa_ioc_disable_cbfn_t)(void *bfa); +typedef void (*bfa_ioc_hbfail_cbfn_t)(void *bfa); +typedef void (*bfa_ioc_reset_cbfn_t)(void *bfa); +struct bfa_ioc_cbfn_s { + bfa_ioc_enable_cbfn_t enable_cbfn; + bfa_ioc_disable_cbfn_t disable_cbfn; + bfa_ioc_hbfail_cbfn_t hbfail_cbfn; + bfa_ioc_reset_cbfn_t reset_cbfn; +}; + +/** + * Heartbeat failure notification queue element. + */ +struct bfa_ioc_hbfail_notify_s { + struct list_head qe; + bfa_ioc_hbfail_cbfn_t cbfn; + void *cbarg; +}; + +/** + * Initialize a heartbeat failure notification structure + */ +#define bfa_ioc_hbfail_init(__notify, __cbfn, __cbarg) do { \ + (__notify)->cbfn = (__cbfn); \ + (__notify)->cbarg = (__cbarg); \ +} while (0) + +struct bfa_ioc_s { + bfa_fsm_t fsm; + struct bfa_s *bfa; + struct bfa_pcidev_s pcidev; + struct bfa_timer_mod_s *timer_mod; + struct bfa_timer_s ioc_timer; + struct bfa_timer_s sem_timer; + u32 hb_count; + u32 hb_fail; + u32 retry_count; + struct list_head hb_notify_q; + void *dbg_fwsave; + int dbg_fwsave_len; + bfa_boolean_t dbg_fwsave_once; + enum bfi_mclass ioc_mc; + struct bfa_ioc_regs_s ioc_regs; + struct bfa_trc_mod_s *trcmod; + struct bfa_aen_s *aen; + struct bfa_log_mod_s *logm; + struct bfa_ioc_drv_stats_s stats; + bfa_boolean_t auto_recover; + bfa_boolean_t fcmode; + bfa_boolean_t ctdev; + bfa_boolean_t cna; + bfa_boolean_t pllinit; + u8 port_id; + + struct bfa_dma_s attr_dma; + struct bfi_ioc_attr_s *attr; + struct bfa_ioc_cbfn_s *cbfn; + struct bfa_ioc_mbox_mod_s mbox_mod; +}; + +#define bfa_ioc_pcifn(__ioc) (__ioc)->pcidev.pci_func +#define bfa_ioc_devid(__ioc) (__ioc)->pcidev.device_id +#define bfa_ioc_bar0(__ioc) (__ioc)->pcidev.pci_bar_kva +#define bfa_ioc_portid(__ioc) ((__ioc)->port_id) +#define bfa_ioc_fetch_stats(__ioc, __stats) \ + ((__stats)->drv_stats) = (__ioc)->stats +#define bfa_ioc_clr_stats(__ioc) \ + bfa_os_memset(&(__ioc)->stats, 0, sizeof((__ioc)->stats)) +#define bfa_ioc_maxfrsize(__ioc) (__ioc)->attr->maxfrsize +#define bfa_ioc_rx_bbcredit(__ioc) (__ioc)->attr->rx_bbcredit +#define bfa_ioc_speed_sup(__ioc) \ + BFI_ADAPTER_GETP(SPEED, (__ioc)->attr->adapter_prop) + +/** + * IOC mailbox interface + */ +void bfa_ioc_mbox_queue(struct bfa_ioc_s *ioc, struct bfa_mbox_cmd_s *cmd); +void bfa_ioc_mbox_register(struct bfa_ioc_s *ioc, + bfa_ioc_mbox_mcfunc_t *mcfuncs); +void bfa_ioc_mbox_isr(struct bfa_ioc_s *ioc); +void bfa_ioc_mbox_send(struct bfa_ioc_s *ioc, void *ioc_msg, int len); +void bfa_ioc_msgget(struct bfa_ioc_s *ioc, void *mbmsg); +void bfa_ioc_mbox_regisr(struct bfa_ioc_s *ioc, enum bfi_mclass mc, + bfa_ioc_mbox_mcfunc_t cbfn, void *cbarg); + +/** + * IOC interfaces + */ +void bfa_ioc_attach(struct bfa_ioc_s *ioc, void *bfa, + struct bfa_ioc_cbfn_s *cbfn, struct bfa_timer_mod_s *timer_mod, + struct bfa_trc_mod_s *trcmod, + struct bfa_aen_s *aen, struct bfa_log_mod_s *logm); +void bfa_ioc_detach(struct bfa_ioc_s *ioc); +void bfa_ioc_pci_init(struct bfa_ioc_s *ioc, struct bfa_pcidev_s *pcidev, + enum bfi_mclass mc); +u32 bfa_ioc_meminfo(void); +void bfa_ioc_mem_claim(struct bfa_ioc_s *ioc, u8 *dm_kva, u64 dm_pa); +void bfa_ioc_enable(struct bfa_ioc_s *ioc); +void bfa_ioc_disable(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_intx_claim(struct bfa_ioc_s *ioc); + +void bfa_ioc_boot(struct bfa_ioc_s *ioc, u32 boot_type, u32 boot_param); +void bfa_ioc_isr(struct bfa_ioc_s *ioc, struct bfi_mbmsg_s *msg); +void bfa_ioc_error_isr(struct bfa_ioc_s *ioc); +void bfa_ioc_isr_mode_set(struct bfa_ioc_s *ioc, bfa_boolean_t intx); +bfa_status_t bfa_ioc_pll_init(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_is_operational(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_is_disabled(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_adapter_is_disabled(struct bfa_ioc_s *ioc); +void bfa_ioc_cfg_complete(struct bfa_ioc_s *ioc); +void bfa_ioc_get_attr(struct bfa_ioc_s *ioc, struct bfa_ioc_attr_s *ioc_attr); +void bfa_ioc_get_adapter_attr(struct bfa_ioc_s *ioc, + struct bfa_adapter_attr_s *ad_attr); +int bfa_ioc_debug_trcsz(bfa_boolean_t auto_recover); +void bfa_ioc_debug_memclaim(struct bfa_ioc_s *ioc, void *dbg_fwsave); +bfa_status_t bfa_ioc_debug_fwsave(struct bfa_ioc_s *ioc, void *trcdata, + int *trclen); +bfa_status_t bfa_ioc_debug_fwtrc(struct bfa_ioc_s *ioc, void *trcdata, + int *trclen); +u32 bfa_ioc_smem_pgnum(struct bfa_ioc_s *ioc, u32 fmaddr); +u32 bfa_ioc_smem_pgoff(struct bfa_ioc_s *ioc, u32 fmaddr); +void bfa_ioc_set_fcmode(struct bfa_ioc_s *ioc); +bfa_boolean_t bfa_ioc_get_fcmode(struct bfa_ioc_s *ioc); +void bfa_ioc_hbfail_register(struct bfa_ioc_s *ioc, + struct bfa_ioc_hbfail_notify_s *notify); + +/* + * bfa mfg wwn API functions + */ +wwn_t bfa_ioc_get_pwwn(struct bfa_ioc_s *ioc); +wwn_t bfa_ioc_get_nwwn(struct bfa_ioc_s *ioc); +wwn_t bfa_ioc_get_wwn_naa5(struct bfa_ioc_s *ioc, u16 inst); +mac_t bfa_ioc_get_mac(struct bfa_ioc_s *ioc); +u64 bfa_ioc_get_adid(struct bfa_ioc_s *ioc); + +#endif /* __BFA_IOC_H__ */ + diff --git a/drivers/scsi/bfa/bfa_iocfc.c b/drivers/scsi/bfa/bfa_iocfc.c new file mode 100644 index 000000000000..12350b022d63 --- /dev/null +++ b/drivers/scsi/bfa/bfa_iocfc.c @@ -0,0 +1,872 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "bfa_callback_priv.h" +#include "bfad_drv.h" + +BFA_TRC_FILE(HAL, IOCFC); + +/** + * IOC local definitions + */ +#define BFA_IOCFC_TOV 5000 /* msecs */ + +enum { + BFA_IOCFC_ACT_NONE = 0, + BFA_IOCFC_ACT_INIT = 1, + BFA_IOCFC_ACT_STOP = 2, + BFA_IOCFC_ACT_DISABLE = 3, +}; + +/* + * forward declarations + */ +static void bfa_iocfc_enable_cbfn(void *bfa_arg, enum bfa_status status); +static void bfa_iocfc_disable_cbfn(void *bfa_arg); +static void bfa_iocfc_hbfail_cbfn(void *bfa_arg); +static void bfa_iocfc_reset_cbfn(void *bfa_arg); +static void bfa_iocfc_stats_clear(void *bfa_arg); +static void bfa_iocfc_stats_swap(struct bfa_fw_stats_s *d, + struct bfa_fw_stats_s *s); +static void bfa_iocfc_stats_clr_cb(void *bfa_arg, bfa_boolean_t complete); +static void bfa_iocfc_stats_clr_timeout(void *bfa_arg); +static void bfa_iocfc_stats_cb(void *bfa_arg, bfa_boolean_t complete); +static void bfa_iocfc_stats_timeout(void *bfa_arg); + +static struct bfa_ioc_cbfn_s bfa_iocfc_cbfn; + +/** + * bfa_ioc_pvt BFA IOC private functions + */ + +static void +bfa_iocfc_cqs_sz(struct bfa_iocfc_cfg_s *cfg, u32 *dm_len) +{ + int i, per_reqq_sz, per_rspq_sz; + + per_reqq_sz = BFA_ROUNDUP((cfg->drvcfg.num_reqq_elems * BFI_LMSG_SZ), + BFA_DMA_ALIGN_SZ); + per_rspq_sz = BFA_ROUNDUP((cfg->drvcfg.num_rspq_elems * BFI_LMSG_SZ), + BFA_DMA_ALIGN_SZ); + + /* + * Calculate CQ size + */ + for (i = 0; i < cfg->fwcfg.num_cqs; i++) { + *dm_len = *dm_len + per_reqq_sz; + *dm_len = *dm_len + per_rspq_sz; + } + + /* + * Calculate Shadow CI/PI size + */ + for (i = 0; i < cfg->fwcfg.num_cqs; i++) + *dm_len += (2 * BFA_CACHELINE_SZ); +} + +static void +bfa_iocfc_fw_cfg_sz(struct bfa_iocfc_cfg_s *cfg, u32 *dm_len) +{ + *dm_len += + BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfg_s), BFA_CACHELINE_SZ); + *dm_len += + BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfgrsp_s), + BFA_CACHELINE_SZ); + *dm_len += BFA_ROUNDUP(sizeof(struct bfa_fw_stats_s), BFA_CACHELINE_SZ); +} + +/** + * Use the Mailbox interface to send BFI_IOCFC_H2I_CFG_REQ + */ +static void +bfa_iocfc_send_cfg(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_cfg_req_s cfg_req; + struct bfi_iocfc_cfg_s *cfg_info = iocfc->cfginfo; + struct bfa_iocfc_cfg_s *cfg = &iocfc->cfg; + int i; + + bfa_assert(cfg->fwcfg.num_cqs <= BFI_IOC_MAX_CQS); + bfa_trc(bfa, cfg->fwcfg.num_cqs); + + iocfc->cfgdone = BFA_FALSE; + bfa_iocfc_reset_queues(bfa); + + /** + * initialize IOC configuration info + */ + cfg_info->endian_sig = BFI_IOC_ENDIAN_SIG; + cfg_info->num_cqs = cfg->fwcfg.num_cqs; + + bfa_dma_be_addr_set(cfg_info->cfgrsp_addr, iocfc->cfgrsp_dma.pa); + bfa_dma_be_addr_set(cfg_info->stats_addr, iocfc->stats_pa); + + /** + * dma map REQ and RSP circular queues and shadow pointers + */ + for (i = 0; i < cfg->fwcfg.num_cqs; i++) { + bfa_dma_be_addr_set(cfg_info->req_cq_ba[i], + iocfc->req_cq_ba[i].pa); + bfa_dma_be_addr_set(cfg_info->req_shadow_ci[i], + iocfc->req_cq_shadow_ci[i].pa); + cfg_info->req_cq_elems[i] = + bfa_os_htons(cfg->drvcfg.num_reqq_elems); + + bfa_dma_be_addr_set(cfg_info->rsp_cq_ba[i], + iocfc->rsp_cq_ba[i].pa); + bfa_dma_be_addr_set(cfg_info->rsp_shadow_pi[i], + iocfc->rsp_cq_shadow_pi[i].pa); + cfg_info->rsp_cq_elems[i] = + bfa_os_htons(cfg->drvcfg.num_rspq_elems); + } + + /** + * dma map IOC configuration itself + */ + bfi_h2i_set(cfg_req.mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_CFG_REQ, + bfa_lpuid(bfa)); + bfa_dma_be_addr_set(cfg_req.ioc_cfg_dma_addr, iocfc->cfg_info.pa); + + bfa_ioc_mbox_send(&bfa->ioc, &cfg_req, + sizeof(struct bfi_iocfc_cfg_req_s)); +} + +static void +bfa_iocfc_init_mem(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_pcidev_s *pcidev) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + bfa->bfad = bfad; + iocfc->bfa = bfa; + iocfc->action = BFA_IOCFC_ACT_NONE; + + bfa_os_assign(iocfc->cfg, *cfg); + + /** + * Initialize chip specific handlers. + */ + if (bfa_ioc_devid(&bfa->ioc) == BFA_PCI_DEVICE_ID_CT) { + iocfc->hwif.hw_reginit = bfa_hwct_reginit; + iocfc->hwif.hw_rspq_ack = bfa_hwct_rspq_ack; + iocfc->hwif.hw_msix_init = bfa_hwct_msix_init; + iocfc->hwif.hw_msix_install = bfa_hwct_msix_install; + iocfc->hwif.hw_msix_uninstall = bfa_hwct_msix_uninstall; + iocfc->hwif.hw_isr_mode_set = bfa_hwct_isr_mode_set; + iocfc->hwif.hw_msix_getvecs = bfa_hwct_msix_getvecs; + } else { + iocfc->hwif.hw_reginit = bfa_hwcb_reginit; + iocfc->hwif.hw_rspq_ack = bfa_hwcb_rspq_ack; + iocfc->hwif.hw_msix_init = bfa_hwcb_msix_init; + iocfc->hwif.hw_msix_install = bfa_hwcb_msix_install; + iocfc->hwif.hw_msix_uninstall = bfa_hwcb_msix_uninstall; + iocfc->hwif.hw_isr_mode_set = bfa_hwcb_isr_mode_set; + iocfc->hwif.hw_msix_getvecs = bfa_hwcb_msix_getvecs; + } + + iocfc->hwif.hw_reginit(bfa); + bfa->msix.nvecs = 0; +} + +static void +bfa_iocfc_mem_claim(struct bfa_s *bfa, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo) +{ + u8 *dm_kva; + u64 dm_pa; + int i, per_reqq_sz, per_rspq_sz; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + int dbgsz; + + dm_kva = bfa_meminfo_dma_virt(meminfo); + dm_pa = bfa_meminfo_dma_phys(meminfo); + + /* + * First allocate dma memory for IOC. + */ + bfa_ioc_mem_claim(&bfa->ioc, dm_kva, dm_pa); + dm_kva += bfa_ioc_meminfo(); + dm_pa += bfa_ioc_meminfo(); + + /* + * Claim DMA-able memory for the request/response queues and for shadow + * ci/pi registers + */ + per_reqq_sz = BFA_ROUNDUP((cfg->drvcfg.num_reqq_elems * BFI_LMSG_SZ), + BFA_DMA_ALIGN_SZ); + per_rspq_sz = BFA_ROUNDUP((cfg->drvcfg.num_rspq_elems * BFI_LMSG_SZ), + BFA_DMA_ALIGN_SZ); + + for (i = 0; i < cfg->fwcfg.num_cqs; i++) { + iocfc->req_cq_ba[i].kva = dm_kva; + iocfc->req_cq_ba[i].pa = dm_pa; + bfa_os_memset(dm_kva, 0, per_reqq_sz); + dm_kva += per_reqq_sz; + dm_pa += per_reqq_sz; + + iocfc->rsp_cq_ba[i].kva = dm_kva; + iocfc->rsp_cq_ba[i].pa = dm_pa; + bfa_os_memset(dm_kva, 0, per_rspq_sz); + dm_kva += per_rspq_sz; + dm_pa += per_rspq_sz; + } + + for (i = 0; i < cfg->fwcfg.num_cqs; i++) { + iocfc->req_cq_shadow_ci[i].kva = dm_kva; + iocfc->req_cq_shadow_ci[i].pa = dm_pa; + dm_kva += BFA_CACHELINE_SZ; + dm_pa += BFA_CACHELINE_SZ; + + iocfc->rsp_cq_shadow_pi[i].kva = dm_kva; + iocfc->rsp_cq_shadow_pi[i].pa = dm_pa; + dm_kva += BFA_CACHELINE_SZ; + dm_pa += BFA_CACHELINE_SZ; + } + + /* + * Claim DMA-able memory for the config info page + */ + bfa->iocfc.cfg_info.kva = dm_kva; + bfa->iocfc.cfg_info.pa = dm_pa; + bfa->iocfc.cfginfo = (struct bfi_iocfc_cfg_s *) dm_kva; + dm_kva += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfg_s), BFA_CACHELINE_SZ); + dm_pa += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfg_s), BFA_CACHELINE_SZ); + + /* + * Claim DMA-able memory for the config response + */ + bfa->iocfc.cfgrsp_dma.kva = dm_kva; + bfa->iocfc.cfgrsp_dma.pa = dm_pa; + bfa->iocfc.cfgrsp = (struct bfi_iocfc_cfgrsp_s *) dm_kva; + + dm_kva += + BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfgrsp_s), + BFA_CACHELINE_SZ); + dm_pa += BFA_ROUNDUP(sizeof(struct bfi_iocfc_cfgrsp_s), + BFA_CACHELINE_SZ); + + /* + * Claim DMA-able memory for iocfc stats + */ + bfa->iocfc.stats_kva = dm_kva; + bfa->iocfc.stats_pa = dm_pa; + bfa->iocfc.fw_stats = (struct bfa_fw_stats_s *) dm_kva; + dm_kva += BFA_ROUNDUP(sizeof(struct bfa_fw_stats_s), BFA_CACHELINE_SZ); + dm_pa += BFA_ROUNDUP(sizeof(struct bfa_fw_stats_s), BFA_CACHELINE_SZ); + + bfa_meminfo_dma_virt(meminfo) = dm_kva; + bfa_meminfo_dma_phys(meminfo) = dm_pa; + + dbgsz = bfa_ioc_debug_trcsz(bfa_auto_recover); + if (dbgsz > 0) { + bfa_ioc_debug_memclaim(&bfa->ioc, bfa_meminfo_kva(meminfo)); + bfa_meminfo_kva(meminfo) += dbgsz; + } +} + +/** + * BFA submodules initialization completion notification. + */ +static void +bfa_iocfc_initdone_submod(struct bfa_s *bfa) +{ + int i; + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->initdone(bfa); +} + +/** + * Start BFA submodules. + */ +static void +bfa_iocfc_start_submod(struct bfa_s *bfa) +{ + int i; + + bfa->rme_process = BFA_TRUE; + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->start(bfa); +} + +/** + * Disable BFA submodules. + */ +static void +bfa_iocfc_disable_submod(struct bfa_s *bfa) +{ + int i; + + for (i = 0; hal_mods[i]; i++) + hal_mods[i]->iocdisable(bfa); +} + +static void +bfa_iocfc_init_cb(void *bfa_arg, bfa_boolean_t complete) +{ + struct bfa_s *bfa = bfa_arg; + + if (complete) { + if (bfa->iocfc.cfgdone) + bfa_cb_init(bfa->bfad, BFA_STATUS_OK); + else + bfa_cb_init(bfa->bfad, BFA_STATUS_FAILED); + } else + bfa->iocfc.action = BFA_IOCFC_ACT_NONE; +} + +static void +bfa_iocfc_stop_cb(void *bfa_arg, bfa_boolean_t compl) +{ + struct bfa_s *bfa = bfa_arg; + struct bfad_s *bfad = bfa->bfad; + + if (compl) + complete(&bfad->comp); + + else + bfa->iocfc.action = BFA_IOCFC_ACT_NONE; +} + +static void +bfa_iocfc_disable_cb(void *bfa_arg, bfa_boolean_t compl) +{ + struct bfa_s *bfa = bfa_arg; + struct bfad_s *bfad = bfa->bfad; + + if (compl) + complete(&bfad->disable_comp); +} + +/** + * Update BFA configuration from firmware configuration. + */ +static void +bfa_iocfc_cfgrsp(struct bfa_s *bfa) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_cfgrsp_s *cfgrsp = iocfc->cfgrsp; + struct bfa_iocfc_fwcfg_s *fwcfg = &cfgrsp->fwcfg; + struct bfi_iocfc_cfg_s *cfginfo = iocfc->cfginfo; + + fwcfg->num_cqs = fwcfg->num_cqs; + fwcfg->num_ioim_reqs = bfa_os_ntohs(fwcfg->num_ioim_reqs); + fwcfg->num_tskim_reqs = bfa_os_ntohs(fwcfg->num_tskim_reqs); + fwcfg->num_fcxp_reqs = bfa_os_ntohs(fwcfg->num_fcxp_reqs); + fwcfg->num_uf_bufs = bfa_os_ntohs(fwcfg->num_uf_bufs); + fwcfg->num_rports = bfa_os_ntohs(fwcfg->num_rports); + + cfginfo->intr_attr.coalesce = cfgrsp->intr_attr.coalesce; + cfginfo->intr_attr.delay = bfa_os_ntohs(cfgrsp->intr_attr.delay); + cfginfo->intr_attr.latency = bfa_os_ntohs(cfgrsp->intr_attr.latency); + + iocfc->cfgdone = BFA_TRUE; + + /** + * Configuration is complete - initialize/start submodules + */ + if (iocfc->action == BFA_IOCFC_ACT_INIT) + bfa_cb_queue(bfa, &iocfc->init_hcb_qe, bfa_iocfc_init_cb, bfa); + else + bfa_iocfc_start_submod(bfa); +} + +static void +bfa_iocfc_stats_clear(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_stats_req_s stats_req; + + bfa_timer_start(bfa, &iocfc->stats_timer, + bfa_iocfc_stats_clr_timeout, bfa, + BFA_IOCFC_TOV); + + bfi_h2i_set(stats_req.mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_CLEAR_STATS_REQ, + bfa_lpuid(bfa)); + bfa_ioc_mbox_send(&bfa->ioc, &stats_req, + sizeof(struct bfi_iocfc_stats_req_s)); +} + +static void +bfa_iocfc_stats_swap(struct bfa_fw_stats_s *d, struct bfa_fw_stats_s *s) +{ + u32 *dip = (u32 *) d; + u32 *sip = (u32 *) s; + int i; + + for (i = 0; i < (sizeof(struct bfa_fw_stats_s) / sizeof(u32)); i++) + dip[i] = bfa_os_ntohl(sip[i]); +} + +static void +bfa_iocfc_stats_clr_cb(void *bfa_arg, bfa_boolean_t complete) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + if (complete) { + bfa_ioc_clr_stats(&bfa->ioc); + iocfc->stats_cbfn(iocfc->stats_cbarg, iocfc->stats_status); + } else { + iocfc->stats_busy = BFA_FALSE; + iocfc->stats_status = BFA_STATUS_OK; + } +} + +static void +bfa_iocfc_stats_clr_timeout(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + bfa_trc(bfa, 0); + + iocfc->stats_status = BFA_STATUS_ETIMER; + bfa_cb_queue(bfa, &iocfc->stats_hcb_qe, bfa_iocfc_stats_clr_cb, bfa); +} + +static void +bfa_iocfc_stats_cb(void *bfa_arg, bfa_boolean_t complete) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + if (complete) { + if (iocfc->stats_status == BFA_STATUS_OK) { + bfa_os_memset(iocfc->stats_ret, 0, + sizeof(*iocfc->stats_ret)); + bfa_iocfc_stats_swap(&iocfc->stats_ret->fw_stats, + iocfc->fw_stats); + } + iocfc->stats_cbfn(iocfc->stats_cbarg, iocfc->stats_status); + } else { + iocfc->stats_busy = BFA_FALSE; + iocfc->stats_status = BFA_STATUS_OK; + } +} + +static void +bfa_iocfc_stats_timeout(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + bfa_trc(bfa, 0); + + iocfc->stats_status = BFA_STATUS_ETIMER; + bfa_cb_queue(bfa, &iocfc->stats_hcb_qe, bfa_iocfc_stats_cb, bfa); +} + +static void +bfa_iocfc_stats_query(struct bfa_s *bfa) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_stats_req_s stats_req; + + bfa_timer_start(bfa, &iocfc->stats_timer, + bfa_iocfc_stats_timeout, bfa, BFA_IOCFC_TOV); + + bfi_h2i_set(stats_req.mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_GET_STATS_REQ, + bfa_lpuid(bfa)); + bfa_ioc_mbox_send(&bfa->ioc, &stats_req, + sizeof(struct bfi_iocfc_stats_req_s)); +} + +void +bfa_iocfc_reset_queues(struct bfa_s *bfa) +{ + int q; + + for (q = 0; q < BFI_IOC_MAX_CQS; q++) { + bfa_reqq_ci(bfa, q) = 0; + bfa_reqq_pi(bfa, q) = 0; + bfa_rspq_ci(bfa, q) = 0; + bfa_rspq_pi(bfa, q) = 0; + } +} + +/** + * IOC enable request is complete + */ +static void +bfa_iocfc_enable_cbfn(void *bfa_arg, enum bfa_status status) +{ + struct bfa_s *bfa = bfa_arg; + + if (status != BFA_STATUS_OK) { + bfa_isr_disable(bfa); + if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT) + bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe, + bfa_iocfc_init_cb, bfa); + return; + } + + bfa_iocfc_initdone_submod(bfa); + bfa_iocfc_send_cfg(bfa); +} + +/** + * IOC disable request is complete + */ +static void +bfa_iocfc_disable_cbfn(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + + bfa_isr_disable(bfa); + bfa_iocfc_disable_submod(bfa); + + if (bfa->iocfc.action == BFA_IOCFC_ACT_STOP) + bfa_cb_queue(bfa, &bfa->iocfc.stop_hcb_qe, bfa_iocfc_stop_cb, + bfa); + else { + bfa_assert(bfa->iocfc.action == BFA_IOCFC_ACT_DISABLE); + bfa_cb_queue(bfa, &bfa->iocfc.dis_hcb_qe, bfa_iocfc_disable_cb, + bfa); + } +} + +/** + * Notify sub-modules of hardware failure. + */ +static void +bfa_iocfc_hbfail_cbfn(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + + bfa->rme_process = BFA_FALSE; + + bfa_isr_disable(bfa); + bfa_iocfc_disable_submod(bfa); + + if (bfa->iocfc.action == BFA_IOCFC_ACT_INIT) + bfa_cb_queue(bfa, &bfa->iocfc.init_hcb_qe, bfa_iocfc_init_cb, + bfa); +} + +/** + * Actions on chip-reset completion. + */ +static void +bfa_iocfc_reset_cbfn(void *bfa_arg) +{ + struct bfa_s *bfa = bfa_arg; + + bfa_iocfc_reset_queues(bfa); + bfa_isr_enable(bfa); +} + + + +/** + * bfa_ioc_public + */ + +/** + * Query IOC memory requirement information. + */ +void +bfa_iocfc_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len) +{ + /* dma memory for IOC */ + *dm_len += bfa_ioc_meminfo(); + + bfa_iocfc_fw_cfg_sz(cfg, dm_len); + bfa_iocfc_cqs_sz(cfg, dm_len); + *km_len += bfa_ioc_debug_trcsz(bfa_auto_recover); +} + +/** + * Query IOC memory requirement information. + */ +void +bfa_iocfc_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + int i; + + bfa_iocfc_cbfn.enable_cbfn = bfa_iocfc_enable_cbfn; + bfa_iocfc_cbfn.disable_cbfn = bfa_iocfc_disable_cbfn; + bfa_iocfc_cbfn.hbfail_cbfn = bfa_iocfc_hbfail_cbfn; + bfa_iocfc_cbfn.reset_cbfn = bfa_iocfc_reset_cbfn; + + bfa_ioc_attach(&bfa->ioc, bfa, &bfa_iocfc_cbfn, &bfa->timer_mod, + bfa->trcmod, bfa->aen, bfa->logm); + bfa_ioc_pci_init(&bfa->ioc, pcidev, BFI_MC_IOCFC); + bfa_ioc_mbox_register(&bfa->ioc, bfa_mbox_isrs); + + /** + * Choose FC (ssid: 0x1C) v/s FCoE (ssid: 0x14) mode. + */ + if (0) + bfa_ioc_set_fcmode(&bfa->ioc); + + bfa_iocfc_init_mem(bfa, bfad, cfg, pcidev); + bfa_iocfc_mem_claim(bfa, cfg, meminfo); + bfa_timer_init(&bfa->timer_mod); + + INIT_LIST_HEAD(&bfa->comp_q); + for (i = 0; i < BFI_IOC_MAX_CQS; i++) + INIT_LIST_HEAD(&bfa->reqq_waitq[i]); +} + +/** + * Query IOC memory requirement information. + */ +void +bfa_iocfc_detach(struct bfa_s *bfa) +{ + bfa_ioc_detach(&bfa->ioc); +} + +/** + * Query IOC memory requirement information. + */ +void +bfa_iocfc_init(struct bfa_s *bfa) +{ + bfa->iocfc.action = BFA_IOCFC_ACT_INIT; + bfa_ioc_enable(&bfa->ioc); + bfa_msix_install(bfa); +} + +/** + * IOC start called from bfa_start(). Called to start IOC operations + * at driver instantiation for this instance. + */ +void +bfa_iocfc_start(struct bfa_s *bfa) +{ + if (bfa->iocfc.cfgdone) + bfa_iocfc_start_submod(bfa); +} + +/** + * IOC stop called from bfa_stop(). Called only when driver is unloaded + * for this instance. + */ +void +bfa_iocfc_stop(struct bfa_s *bfa) +{ + bfa->iocfc.action = BFA_IOCFC_ACT_STOP; + + bfa->rme_process = BFA_FALSE; + bfa_ioc_disable(&bfa->ioc); +} + +void +bfa_iocfc_isr(void *bfaarg, struct bfi_mbmsg_s *m) +{ + struct bfa_s *bfa = bfaarg; + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + union bfi_iocfc_i2h_msg_u *msg; + + msg = (union bfi_iocfc_i2h_msg_u *) m; + bfa_trc(bfa, msg->mh.msg_id); + + switch (msg->mh.msg_id) { + case BFI_IOCFC_I2H_CFG_REPLY: + iocfc->cfg_reply = &msg->cfg_reply; + bfa_iocfc_cfgrsp(bfa); + break; + + case BFI_IOCFC_I2H_GET_STATS_RSP: + if (iocfc->stats_busy == BFA_FALSE + || iocfc->stats_status == BFA_STATUS_ETIMER) + break; + + bfa_timer_stop(&iocfc->stats_timer); + iocfc->stats_status = BFA_STATUS_OK; + bfa_cb_queue(bfa, &iocfc->stats_hcb_qe, bfa_iocfc_stats_cb, + bfa); + break; + case BFI_IOCFC_I2H_CLEAR_STATS_RSP: + /* + * check for timer pop before processing the rsp + */ + if (iocfc->stats_busy == BFA_FALSE + || iocfc->stats_status == BFA_STATUS_ETIMER) + break; + + bfa_timer_stop(&iocfc->stats_timer); + iocfc->stats_status = BFA_STATUS_OK; + bfa_cb_queue(bfa, &iocfc->stats_hcb_qe, + bfa_iocfc_stats_clr_cb, bfa); + break; + case BFI_IOCFC_I2H_UPDATEQ_RSP: + iocfc->updateq_cbfn(iocfc->updateq_cbarg, BFA_STATUS_OK); + break; + default: + bfa_assert(0); + } +} + +#ifndef BFA_BIOS_BUILD +void +bfa_adapter_get_attr(struct bfa_s *bfa, struct bfa_adapter_attr_s *ad_attr) +{ + bfa_ioc_get_adapter_attr(&bfa->ioc, ad_attr); +} + +u64 +bfa_adapter_get_id(struct bfa_s *bfa) +{ + return bfa_ioc_get_adid(&bfa->ioc); +} + +void +bfa_iocfc_get_attr(struct bfa_s *bfa, struct bfa_iocfc_attr_s *attr) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + attr->intr_attr = iocfc->cfginfo->intr_attr; + attr->config = iocfc->cfg; +} + +bfa_status_t +bfa_iocfc_israttr_set(struct bfa_s *bfa, struct bfa_iocfc_intr_attr_s *attr) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_set_intr_req_s *m; + + iocfc->cfginfo->intr_attr = *attr; + if (!bfa_iocfc_is_operational(bfa)) + return BFA_STATUS_OK; + + m = bfa_reqq_next(bfa, BFA_REQQ_IOC); + if (!m) + return BFA_STATUS_DEVBUSY; + + bfi_h2i_set(m->mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_SET_INTR_REQ, + bfa_lpuid(bfa)); + m->coalesce = attr->coalesce; + m->delay = bfa_os_htons(attr->delay); + m->latency = bfa_os_htons(attr->latency); + + bfa_trc(bfa, attr->delay); + bfa_trc(bfa, attr->latency); + + bfa_reqq_produce(bfa, BFA_REQQ_IOC); + return BFA_STATUS_OK; +} + +void +bfa_iocfc_set_snsbase(struct bfa_s *bfa, u64 snsbase_pa) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + iocfc->cfginfo->sense_buf_len = (BFI_IOIM_SNSLEN - 1); + bfa_dma_be_addr_set(iocfc->cfginfo->ioim_snsbase, snsbase_pa); +} + +bfa_status_t +bfa_iocfc_get_stats(struct bfa_s *bfa, struct bfa_iocfc_stats_s *stats, + bfa_cb_ioc_t cbfn, void *cbarg) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + if (iocfc->stats_busy) { + bfa_trc(bfa, iocfc->stats_busy); + return (BFA_STATUS_DEVBUSY); + } + + iocfc->stats_busy = BFA_TRUE; + iocfc->stats_ret = stats; + iocfc->stats_cbfn = cbfn; + iocfc->stats_cbarg = cbarg; + + bfa_iocfc_stats_query(bfa); + + return (BFA_STATUS_OK); +} + +bfa_status_t +bfa_iocfc_clear_stats(struct bfa_s *bfa, bfa_cb_ioc_t cbfn, void *cbarg) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + + if (iocfc->stats_busy) { + bfa_trc(bfa, iocfc->stats_busy); + return (BFA_STATUS_DEVBUSY); + } + + iocfc->stats_busy = BFA_TRUE; + iocfc->stats_cbfn = cbfn; + iocfc->stats_cbarg = cbarg; + + bfa_iocfc_stats_clear(bfa); + return (BFA_STATUS_OK); +} + +/** + * Enable IOC after it is disabled. + */ +void +bfa_iocfc_enable(struct bfa_s *bfa) +{ + bfa_plog_str(bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_MISC, 0, + "IOC Enable"); + bfa_ioc_enable(&bfa->ioc); +} + +void +bfa_iocfc_disable(struct bfa_s *bfa) +{ + bfa_plog_str(bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_MISC, 0, + "IOC Disable"); + bfa->iocfc.action = BFA_IOCFC_ACT_DISABLE; + + bfa->rme_process = BFA_FALSE; + bfa_ioc_disable(&bfa->ioc); +} + + +bfa_boolean_t +bfa_iocfc_is_operational(struct bfa_s *bfa) +{ + return bfa_ioc_is_operational(&bfa->ioc) && bfa->iocfc.cfgdone; +} + +/** + * Return boot target port wwns -- read from boot information in flash. + */ +void +bfa_iocfc_get_bootwwns(struct bfa_s *bfa, u8 *nwwns, wwn_t **wwns) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_cfgrsp_s *cfgrsp = iocfc->cfgrsp; + + *nwwns = cfgrsp->bootwwns.nwwns; + *wwns = cfgrsp->bootwwns.wwn; +} + +#endif + + diff --git a/drivers/scsi/bfa/bfa_iocfc.h b/drivers/scsi/bfa/bfa_iocfc.h new file mode 100644 index 000000000000..7ad177ed4cfc --- /dev/null +++ b/drivers/scsi/bfa/bfa_iocfc.h @@ -0,0 +1,168 @@ +/* + * 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. + */ + +#ifndef __BFA_IOCFC_H__ +#define __BFA_IOCFC_H__ + +#include +#include +#include +#include + +#define BFA_REQQ_NELEMS_MIN (4) +#define BFA_RSPQ_NELEMS_MIN (4) + +struct bfa_iocfc_regs_s { + bfa_os_addr_t intr_status; + bfa_os_addr_t intr_mask; + bfa_os_addr_t cpe_q_pi[BFI_IOC_MAX_CQS]; + bfa_os_addr_t cpe_q_ci[BFI_IOC_MAX_CQS]; + bfa_os_addr_t cpe_q_depth[BFI_IOC_MAX_CQS]; + bfa_os_addr_t cpe_q_ctrl[BFI_IOC_MAX_CQS]; + bfa_os_addr_t rme_q_ci[BFI_IOC_MAX_CQS]; + bfa_os_addr_t rme_q_pi[BFI_IOC_MAX_CQS]; + bfa_os_addr_t rme_q_depth[BFI_IOC_MAX_CQS]; + bfa_os_addr_t rme_q_ctrl[BFI_IOC_MAX_CQS]; +}; + +/** + * MSIX vector handlers + */ +#define BFA_MSIX_MAX_VECTORS 22 +typedef void (*bfa_msix_handler_t)(struct bfa_s *bfa, int vec); +struct bfa_msix_s { + int nvecs; + bfa_msix_handler_t handler[BFA_MSIX_MAX_VECTORS]; +}; + +/** + * Chip specific interfaces + */ +struct bfa_hwif_s { + void (*hw_reginit)(struct bfa_s *bfa); + void (*hw_rspq_ack)(struct bfa_s *bfa, int rspq); + void (*hw_msix_init)(struct bfa_s *bfa, int nvecs); + void (*hw_msix_install)(struct bfa_s *bfa); + void (*hw_msix_uninstall)(struct bfa_s *bfa); + void (*hw_isr_mode_set)(struct bfa_s *bfa, bfa_boolean_t msix); + void (*hw_msix_getvecs)(struct bfa_s *bfa, u32 *vecmap, + u32 *nvecs, u32 *maxvec); +}; +typedef void (*bfa_cb_iocfc_t) (void *cbarg, enum bfa_status status); + +struct bfa_iocfc_s { + struct bfa_s *bfa; + struct bfa_iocfc_cfg_s cfg; + int action; + + u32 req_cq_pi[BFI_IOC_MAX_CQS]; + u32 rsp_cq_ci[BFI_IOC_MAX_CQS]; + + struct bfa_cb_qe_s init_hcb_qe; + struct bfa_cb_qe_s stop_hcb_qe; + struct bfa_cb_qe_s dis_hcb_qe; + struct bfa_cb_qe_s stats_hcb_qe; + bfa_boolean_t cfgdone; + + struct bfa_dma_s cfg_info; + struct bfi_iocfc_cfg_s *cfginfo; + struct bfa_dma_s cfgrsp_dma; + struct bfi_iocfc_cfgrsp_s *cfgrsp; + struct bfi_iocfc_cfg_reply_s *cfg_reply; + + u8 *stats_kva; + u64 stats_pa; + struct bfa_fw_stats_s *fw_stats; + struct bfa_timer_s stats_timer; /* timer */ + struct bfa_iocfc_stats_s *stats_ret; /* driver stats location */ + bfa_status_t stats_status; /* stats/statsclr status */ + bfa_boolean_t stats_busy; /* outstanding stats */ + bfa_cb_ioc_t stats_cbfn; /* driver callback function */ + void *stats_cbarg; /* user callback arg */ + + struct bfa_dma_s req_cq_ba[BFI_IOC_MAX_CQS]; + struct bfa_dma_s req_cq_shadow_ci[BFI_IOC_MAX_CQS]; + struct bfa_dma_s rsp_cq_ba[BFI_IOC_MAX_CQS]; + struct bfa_dma_s rsp_cq_shadow_pi[BFI_IOC_MAX_CQS]; + struct bfa_iocfc_regs_s bfa_regs; /* BFA device registers */ + struct bfa_hwif_s hwif; + + bfa_cb_iocfc_t updateq_cbfn; /* bios callback function */ + void *updateq_cbarg; /* bios callback arg */ +}; + +#define bfa_lpuid(__bfa) bfa_ioc_portid(&(__bfa)->ioc) +#define bfa_msix_init(__bfa, __nvecs) \ + (__bfa)->iocfc.hwif.hw_msix_init(__bfa, __nvecs) +#define bfa_msix_install(__bfa) \ + (__bfa)->iocfc.hwif.hw_msix_install(__bfa) +#define bfa_msix_uninstall(__bfa) \ + (__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa) +#define bfa_isr_mode_set(__bfa, __msix) \ + (__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix) +#define bfa_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec) \ + (__bfa)->iocfc.hwif.hw_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec) + +/* + * FC specific IOC functions. + */ +void bfa_iocfc_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len); +void bfa_iocfc_attach(struct bfa_s *bfa, void *bfad, + struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo, + struct bfa_pcidev_s *pcidev); +void bfa_iocfc_detach(struct bfa_s *bfa); +void bfa_iocfc_init(struct bfa_s *bfa); +void bfa_iocfc_start(struct bfa_s *bfa); +void bfa_iocfc_stop(struct bfa_s *bfa); +void bfa_iocfc_isr(void *bfa, struct bfi_mbmsg_s *msg); +void bfa_iocfc_set_snsbase(struct bfa_s *bfa, u64 snsbase_pa); +bfa_boolean_t bfa_iocfc_is_operational(struct bfa_s *bfa); +void bfa_iocfc_reset_queues(struct bfa_s *bfa); +void bfa_iocfc_updateq(struct bfa_s *bfa, u32 reqq_ba, u32 rspq_ba, + u32 reqq_sci, u32 rspq_spi, + bfa_cb_iocfc_t cbfn, void *cbarg); + +void bfa_msix_all(struct bfa_s *bfa, int vec); +void bfa_msix_reqq(struct bfa_s *bfa, int vec); +void bfa_msix_rspq(struct bfa_s *bfa, int vec); +void bfa_msix_lpu_err(struct bfa_s *bfa, int vec); + +void bfa_hwcb_reginit(struct bfa_s *bfa); +void bfa_hwcb_rspq_ack(struct bfa_s *bfa, int rspq); +void bfa_hwcb_msix_init(struct bfa_s *bfa, int nvecs); +void bfa_hwcb_msix_install(struct bfa_s *bfa); +void bfa_hwcb_msix_uninstall(struct bfa_s *bfa); +void bfa_hwcb_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix); +void bfa_hwcb_msix_getvecs(struct bfa_s *bfa, u32 *vecmap, + u32 *nvecs, u32 *maxvec); +void bfa_hwct_reginit(struct bfa_s *bfa); +void bfa_hwct_rspq_ack(struct bfa_s *bfa, int rspq); +void bfa_hwct_msix_init(struct bfa_s *bfa, int nvecs); +void bfa_hwct_msix_install(struct bfa_s *bfa); +void bfa_hwct_msix_uninstall(struct bfa_s *bfa); +void bfa_hwct_isr_mode_set(struct bfa_s *bfa, bfa_boolean_t msix); +void bfa_hwct_msix_getvecs(struct bfa_s *bfa, u32 *vecmap, + u32 *nvecs, u32 *maxvec); + +void bfa_com_meminfo(bfa_boolean_t mincfg, u32 *dm_len); +void bfa_com_attach(struct bfa_s *bfa, struct bfa_meminfo_s *mi, + bfa_boolean_t mincfg); +void bfa_iocfc_get_bootwwns(struct bfa_s *bfa, u8 *nwwns, wwn_t **wwns); + +#endif /* __BFA_IOCFC_H__ */ + diff --git a/drivers/scsi/bfa/bfa_iocfc_q.c b/drivers/scsi/bfa/bfa_iocfc_q.c new file mode 100644 index 000000000000..500a17df40b2 --- /dev/null +++ b/drivers/scsi/bfa/bfa_iocfc_q.c @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#include +#include "bfa_intr_priv.h" + +BFA_TRC_FILE(HAL, IOCFC_Q); + +void +bfa_iocfc_updateq(struct bfa_s *bfa, u32 reqq_ba, u32 rspq_ba, + u32 reqq_sci, u32 rspq_spi, bfa_cb_iocfc_t cbfn, + void *cbarg) +{ + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_updateq_req_s updateq_req; + + iocfc->updateq_cbfn = cbfn; + iocfc->updateq_cbarg = cbarg; + + bfi_h2i_set(updateq_req.mh, BFI_MC_IOCFC, BFI_IOCFC_H2I_UPDATEQ_REQ, + bfa_lpuid(bfa)); + + updateq_req.reqq_ba = bfa_os_htonl(reqq_ba); + updateq_req.rspq_ba = bfa_os_htonl(rspq_ba); + updateq_req.reqq_sci = bfa_os_htonl(reqq_sci); + updateq_req.rspq_spi = bfa_os_htonl(rspq_spi); + + bfa_ioc_mbox_send(&bfa->ioc, &updateq_req, + sizeof(struct bfi_iocfc_updateq_req_s)); +} diff --git a/drivers/scsi/bfa/bfa_ioim.c b/drivers/scsi/bfa/bfa_ioim.c new file mode 100644 index 000000000000..7ae2552e1e14 --- /dev/null +++ b/drivers/scsi/bfa/bfa_ioim.c @@ -0,0 +1,1311 @@ +/* + * 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. + */ + +#include +#include +#include + +BFA_TRC_FILE(HAL, IOIM); + +/* + * forward declarations. + */ +static bfa_boolean_t bfa_ioim_send_ioreq(struct bfa_ioim_s *ioim); +static bfa_boolean_t bfa_ioim_sge_setup(struct bfa_ioim_s *ioim); +static void bfa_ioim_sgpg_setup(struct bfa_ioim_s *ioim); +static bfa_boolean_t bfa_ioim_send_abort(struct bfa_ioim_s *ioim); +static void bfa_ioim_notify_cleanup(struct bfa_ioim_s *ioim); +static void __bfa_cb_ioim_good_comp(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_ioim_comp(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_ioim_abort(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_ioim_failed(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_ioim_pathtov(void *cbarg, bfa_boolean_t complete); + +/** + * bfa_ioim_sm + */ + +/** + * IO state machine events + */ +enum bfa_ioim_event { + BFA_IOIM_SM_START = 1, /* io start request from host */ + BFA_IOIM_SM_COMP_GOOD = 2, /* io good comp, resource free */ + BFA_IOIM_SM_COMP = 3, /* io comp, resource is free */ + BFA_IOIM_SM_COMP_UTAG = 4, /* io comp, resource is free */ + BFA_IOIM_SM_DONE = 5, /* io comp, resource not free */ + BFA_IOIM_SM_FREE = 6, /* io resource is freed */ + BFA_IOIM_SM_ABORT = 7, /* abort request from scsi stack */ + BFA_IOIM_SM_ABORT_COMP = 8, /* abort from f/w */ + BFA_IOIM_SM_ABORT_DONE = 9, /* abort completion from f/w */ + BFA_IOIM_SM_QRESUME = 10, /* CQ space available to queue IO */ + BFA_IOIM_SM_SGALLOCED = 11, /* SG page allocation successful */ + BFA_IOIM_SM_SQRETRY = 12, /* sequence recovery retry */ + BFA_IOIM_SM_HCB = 13, /* bfa callback complete */ + BFA_IOIM_SM_CLEANUP = 14, /* IO cleanup from itnim */ + BFA_IOIM_SM_TMSTART = 15, /* IO cleanup from tskim */ + BFA_IOIM_SM_TMDONE = 16, /* IO cleanup from tskim */ + BFA_IOIM_SM_HWFAIL = 17, /* IOC h/w failure event */ + BFA_IOIM_SM_IOTOV = 18, /* ITN offline TOV */ +}; + +/* + * forward declaration of IO state machine + */ +static void bfa_ioim_sm_uninit(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_sgalloc(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_active(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_abort(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_cleanup(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_qfull(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_abort_qfull(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_cleanup_qfull(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_hcb(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_hcb_free(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); +static void bfa_ioim_sm_resfree(struct bfa_ioim_s *ioim, + enum bfa_ioim_event event); + +/** + * IO is not started (unallocated). + */ +static void +bfa_ioim_sm_uninit(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_trc_fp(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_START: + if (!bfa_itnim_is_online(ioim->itnim)) { + if (!bfa_itnim_hold_io(ioim->itnim)) { + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + list_del(&ioim->qe); + list_add_tail(&ioim->qe, + &ioim->fcpim->ioim_comp_q); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, + __bfa_cb_ioim_pathtov, ioim); + } else { + list_del(&ioim->qe); + list_add_tail(&ioim->qe, + &ioim->itnim->pending_q); + } + break; + } + + if (ioim->nsges > BFI_SGE_INLINE) { + if (!bfa_ioim_sge_setup(ioim)) { + bfa_sm_set_state(ioim, bfa_ioim_sm_sgalloc); + return; + } + } + + if (!bfa_ioim_send_ioreq(ioim)) { + bfa_sm_set_state(ioim, bfa_ioim_sm_qfull); + break; + } + + bfa_sm_set_state(ioim, bfa_ioim_sm_active); + break; + + case BFA_IOIM_SM_IOTOV: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, + __bfa_cb_ioim_pathtov, ioim); + break; + + case BFA_IOIM_SM_ABORT: + /** + * IO in pending queue can get abort requests. Complete abort + * requests immediately. + */ + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_assert(bfa_q_is_on_q(&ioim->itnim->pending_q, ioim)); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is waiting for SG pages. + */ +static void +bfa_ioim_sm_sgalloc(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_SGALLOCED: + if (!bfa_ioim_send_ioreq(ioim)) { + bfa_sm_set_state(ioim, bfa_ioim_sm_qfull); + break; + } + bfa_sm_set_state(ioim, bfa_ioim_sm_active); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_sgpg_wcancel(ioim->bfa, &ioim->iosp->sgpg_wqe); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_ABORT: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_sgpg_wcancel(ioim->bfa, &ioim->iosp->sgpg_wqe); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_sgpg_wcancel(ioim->bfa, &ioim->iosp->sgpg_wqe); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is active. + */ +static void +bfa_ioim_sm_active(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_trc_fp(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_COMP_GOOD: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, + __bfa_cb_ioim_good_comp, ioim); + break; + + case BFA_IOIM_SM_COMP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_comp, + ioim); + break; + + case BFA_IOIM_SM_DONE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb_free); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_comp, + ioim); + break; + + case BFA_IOIM_SM_ABORT: + ioim->iosp->abort_explicit = BFA_TRUE; + ioim->io_cbfn = __bfa_cb_ioim_abort; + + if (bfa_ioim_send_abort(ioim)) + bfa_sm_set_state(ioim, bfa_ioim_sm_abort); + else { + bfa_sm_set_state(ioim, bfa_ioim_sm_abort_qfull); + bfa_reqq_wait(ioim->bfa, ioim->itnim->reqq, + &ioim->iosp->reqq_wait); + } + break; + + case BFA_IOIM_SM_CLEANUP: + ioim->iosp->abort_explicit = BFA_FALSE; + ioim->io_cbfn = __bfa_cb_ioim_failed; + + if (bfa_ioim_send_abort(ioim)) + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup); + else { + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup_qfull); + bfa_reqq_wait(ioim->bfa, ioim->itnim->reqq, + &ioim->iosp->reqq_wait); + } + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is being aborted, waiting for completion from firmware. + */ +static void +bfa_ioim_sm_abort(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_COMP_GOOD: + case BFA_IOIM_SM_COMP: + case BFA_IOIM_SM_DONE: + case BFA_IOIM_SM_FREE: + break; + + case BFA_IOIM_SM_ABORT_DONE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb_free); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_ABORT_COMP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_COMP_UTAG: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_assert(ioim->iosp->abort_explicit == BFA_TRUE); + ioim->iosp->abort_explicit = BFA_FALSE; + + if (bfa_ioim_send_abort(ioim)) + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup); + else { + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup_qfull); + bfa_reqq_wait(ioim->bfa, ioim->itnim->reqq, + &ioim->iosp->reqq_wait); + } + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is being cleaned up (implicit abort), waiting for completion from + * firmware. + */ +static void +bfa_ioim_sm_cleanup(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_COMP_GOOD: + case BFA_IOIM_SM_COMP: + case BFA_IOIM_SM_DONE: + case BFA_IOIM_SM_FREE: + break; + + case BFA_IOIM_SM_ABORT: + /** + * IO is already being aborted implicitly + */ + ioim->io_cbfn = __bfa_cb_ioim_abort; + break; + + case BFA_IOIM_SM_ABORT_DONE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb_free); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_ABORT_COMP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_COMP_UTAG: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + case BFA_IOIM_SM_CLEANUP: + /** + * IO can be in cleanup state already due to TM command. 2nd cleanup + * request comes from ITN offline event. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is waiting for room in request CQ + */ +static void +bfa_ioim_sm_qfull(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_QRESUME: + bfa_sm_set_state(ioim, bfa_ioim_sm_active); + bfa_ioim_send_ioreq(ioim); + break; + + case BFA_IOIM_SM_ABORT: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Active IO is being aborted, waiting for room in request CQ. + */ +static void +bfa_ioim_sm_abort_qfull(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_QRESUME: + bfa_sm_set_state(ioim, bfa_ioim_sm_abort); + bfa_ioim_send_abort(ioim); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_assert(ioim->iosp->abort_explicit == BFA_TRUE); + ioim->iosp->abort_explicit = BFA_FALSE; + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup_qfull); + break; + + case BFA_IOIM_SM_COMP_GOOD: + case BFA_IOIM_SM_COMP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_DONE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb_free); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_abort, + ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Active IO is being cleaned up, waiting for room in request CQ. + */ +static void +bfa_ioim_sm_cleanup_qfull(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_QRESUME: + bfa_sm_set_state(ioim, bfa_ioim_sm_cleanup); + bfa_ioim_send_abort(ioim); + break; + + case BFA_IOIM_SM_ABORT: + /** + * IO is alraedy being cleaned up implicitly + */ + ioim->io_cbfn = __bfa_cb_ioim_abort; + break; + + case BFA_IOIM_SM_COMP_GOOD: + case BFA_IOIM_SM_COMP: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_DONE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb_free); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + bfa_reqq_wcancel(&ioim->iosp->reqq_wait); + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, __bfa_cb_ioim_failed, + ioim); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO bfa callback is pending. + */ +static void +bfa_ioim_sm_hcb(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_trc_fp(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_HCB: + bfa_sm_set_state(ioim, bfa_ioim_sm_uninit); + bfa_ioim_free(ioim); + bfa_cb_ioim_resfree(ioim->bfa->bfad); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + break; + + default: + bfa_assert(0); + } +} + +/** + * IO bfa callback is pending. IO resource cannot be freed. + */ +static void +bfa_ioim_sm_hcb_free(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_HCB: + bfa_sm_set_state(ioim, bfa_ioim_sm_resfree); + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &ioim->fcpim->ioim_resfree_q); + break; + + case BFA_IOIM_SM_FREE: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + bfa_sm_set_state(ioim, bfa_ioim_sm_hcb); + break; + + default: + bfa_assert(0); + } +} + +/** + * IO is completed, waiting resource free from firmware. + */ +static void +bfa_ioim_sm_resfree(struct bfa_ioim_s *ioim, enum bfa_ioim_event event) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, event); + + switch (event) { + case BFA_IOIM_SM_FREE: + bfa_sm_set_state(ioim, bfa_ioim_sm_uninit); + bfa_ioim_free(ioim); + bfa_cb_ioim_resfree(ioim->bfa->bfad); + break; + + case BFA_IOIM_SM_CLEANUP: + bfa_ioim_notify_cleanup(ioim); + break; + + case BFA_IOIM_SM_HWFAIL: + break; + + default: + bfa_assert(0); + } +} + + + +/** + * bfa_ioim_private + */ + +static void +__bfa_cb_ioim_good_comp(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_ioim_s *ioim = cbarg; + + if (!complete) { + bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); + return; + } + + bfa_cb_ioim_good_comp(ioim->bfa->bfad, ioim->dio); +} + +static void +__bfa_cb_ioim_comp(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_ioim_s *ioim = cbarg; + struct bfi_ioim_rsp_s *m; + u8 *snsinfo = NULL; + u8 sns_len = 0; + s32 residue = 0; + + if (!complete) { + bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); + return; + } + + m = (struct bfi_ioim_rsp_s *) &ioim->iosp->comp_rspmsg; + if (m->io_status == BFI_IOIM_STS_OK) { + /** + * setup sense information, if present + */ + if (m->scsi_status == SCSI_STATUS_CHECK_CONDITION + && m->sns_len) { + sns_len = m->sns_len; + snsinfo = ioim->iosp->snsinfo; + } + + /** + * setup residue value correctly for normal completions + */ + if (m->resid_flags == FCP_RESID_UNDER) + residue = bfa_os_ntohl(m->residue); + if (m->resid_flags == FCP_RESID_OVER) { + residue = bfa_os_ntohl(m->residue); + residue = -residue; + } + } + + bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, m->io_status, + m->scsi_status, sns_len, snsinfo, residue); +} + +static void +__bfa_cb_ioim_failed(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_ioim_s *ioim = cbarg; + + if (!complete) { + bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); + return; + } + + bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_ABORTED, + 0, 0, NULL, 0); +} + +static void +__bfa_cb_ioim_pathtov(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_ioim_s *ioim = cbarg; + + if (!complete) { + bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); + return; + } + + bfa_cb_ioim_done(ioim->bfa->bfad, ioim->dio, BFI_IOIM_STS_PATHTOV, + 0, 0, NULL, 0); +} + +static void +__bfa_cb_ioim_abort(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_ioim_s *ioim = cbarg; + + if (!complete) { + bfa_sm_send_event(ioim, BFA_IOIM_SM_HCB); + return; + } + + bfa_cb_ioim_abort(ioim->bfa->bfad, ioim->dio); +} + +static void +bfa_ioim_sgpg_alloced(void *cbarg) +{ + struct bfa_ioim_s *ioim = cbarg; + + ioim->nsgpgs = BFA_SGPG_NPAGE(ioim->nsges); + list_splice_tail_init(&ioim->iosp->sgpg_wqe.sgpg_q, &ioim->sgpg_q); + bfa_ioim_sgpg_setup(ioim); + bfa_sm_send_event(ioim, BFA_IOIM_SM_SGALLOCED); +} + +/** + * Send I/O request to firmware. + */ +static bfa_boolean_t +bfa_ioim_send_ioreq(struct bfa_ioim_s *ioim) +{ + struct bfa_itnim_s *itnim = ioim->itnim; + struct bfi_ioim_req_s *m; + static struct fcp_cmnd_s cmnd_z0 = { 0 }; + struct bfi_sge_s *sge; + u32 pgdlen = 0; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(ioim->bfa, itnim->reqq); + if (!m) { + bfa_reqq_wait(ioim->bfa, ioim->itnim->reqq, + &ioim->iosp->reqq_wait); + return BFA_FALSE; + } + + /** + * build i/o request message next + */ + m->io_tag = bfa_os_htons(ioim->iotag); + m->rport_hdl = ioim->itnim->rport->fw_handle; + m->io_timeout = bfa_cb_ioim_get_timeout(ioim->dio); + + /** + * build inline IO SG element here + */ + sge = &m->sges[0]; + if (ioim->nsges) { + sge->sga = bfa_cb_ioim_get_sgaddr(ioim->dio, 0); + pgdlen = bfa_cb_ioim_get_sglen(ioim->dio, 0); + sge->sg_len = pgdlen; + sge->flags = (ioim->nsges > BFI_SGE_INLINE) ? + BFI_SGE_DATA_CPL : BFI_SGE_DATA_LAST; + bfa_sge_to_be(sge); + sge++; + } + + if (ioim->nsges > BFI_SGE_INLINE) { + sge->sga = ioim->sgpg->sgpg_pa; + } else { + sge->sga.a32.addr_lo = 0; + sge->sga.a32.addr_hi = 0; + } + sge->sg_len = pgdlen; + sge->flags = BFI_SGE_PGDLEN; + bfa_sge_to_be(sge); + + /** + * set up I/O command parameters + */ + bfa_os_assign(m->cmnd, cmnd_z0); + m->cmnd.lun = bfa_cb_ioim_get_lun(ioim->dio); + m->cmnd.iodir = bfa_cb_ioim_get_iodir(ioim->dio); + bfa_os_assign(m->cmnd.cdb, + *(struct scsi_cdb_s *)bfa_cb_ioim_get_cdb(ioim->dio)); + m->cmnd.fcp_dl = bfa_os_htonl(bfa_cb_ioim_get_size(ioim->dio)); + + /** + * set up I/O message header + */ + switch (m->cmnd.iodir) { + case FCP_IODIR_READ: + bfi_h2i_set(m->mh, BFI_MC_IOIM_READ, 0, bfa_lpuid(ioim->bfa)); + bfa_stats(itnim, input_reqs); + break; + case FCP_IODIR_WRITE: + bfi_h2i_set(m->mh, BFI_MC_IOIM_WRITE, 0, bfa_lpuid(ioim->bfa)); + bfa_stats(itnim, output_reqs); + break; + case FCP_IODIR_RW: + bfa_stats(itnim, input_reqs); + bfa_stats(itnim, output_reqs); + default: + bfi_h2i_set(m->mh, BFI_MC_IOIM_IO, 0, bfa_lpuid(ioim->bfa)); + } + if (itnim->seq_rec || + (bfa_cb_ioim_get_size(ioim->dio) & (sizeof(u32) - 1))) + bfi_h2i_set(m->mh, BFI_MC_IOIM_IO, 0, bfa_lpuid(ioim->bfa)); + +#ifdef IOIM_ADVANCED + m->cmnd.crn = bfa_cb_ioim_get_crn(ioim->dio); + m->cmnd.priority = bfa_cb_ioim_get_priority(ioim->dio); + m->cmnd.taskattr = bfa_cb_ioim_get_taskattr(ioim->dio); + + /** + * Handle large CDB (>16 bytes). + */ + m->cmnd.addl_cdb_len = (bfa_cb_ioim_get_cdblen(ioim->dio) - + FCP_CMND_CDB_LEN) / sizeof(u32); + if (m->cmnd.addl_cdb_len) { + bfa_os_memcpy(&m->cmnd.cdb + 1, (struct scsi_cdb_s *) + bfa_cb_ioim_get_cdb(ioim->dio) + 1, + m->cmnd.addl_cdb_len * sizeof(u32)); + fcp_cmnd_fcpdl(&m->cmnd) = + bfa_os_htonl(bfa_cb_ioim_get_size(ioim->dio)); + } +#endif + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(ioim->bfa, itnim->reqq); + return BFA_TRUE; +} + +/** + * Setup any additional SG pages needed.Inline SG element is setup + * at queuing time. + */ +static bfa_boolean_t +bfa_ioim_sge_setup(struct bfa_ioim_s *ioim) +{ + u16 nsgpgs; + + bfa_assert(ioim->nsges > BFI_SGE_INLINE); + + /** + * allocate SG pages needed + */ + nsgpgs = BFA_SGPG_NPAGE(ioim->nsges); + if (!nsgpgs) + return BFA_TRUE; + + if (bfa_sgpg_malloc(ioim->bfa, &ioim->sgpg_q, nsgpgs) + != BFA_STATUS_OK) { + bfa_sgpg_wait(ioim->bfa, &ioim->iosp->sgpg_wqe, nsgpgs); + return BFA_FALSE; + } + + ioim->nsgpgs = nsgpgs; + bfa_ioim_sgpg_setup(ioim); + + return BFA_TRUE; +} + +static void +bfa_ioim_sgpg_setup(struct bfa_ioim_s *ioim) +{ + int sgeid, nsges, i; + struct bfi_sge_s *sge; + struct bfa_sgpg_s *sgpg; + u32 pgcumsz; + + sgeid = BFI_SGE_INLINE; + ioim->sgpg = sgpg = bfa_q_first(&ioim->sgpg_q); + + do { + sge = sgpg->sgpg->sges; + nsges = ioim->nsges - sgeid; + if (nsges > BFI_SGPG_DATA_SGES) + nsges = BFI_SGPG_DATA_SGES; + + pgcumsz = 0; + for (i = 0; i < nsges; i++, sge++, sgeid++) { + sge->sga = bfa_cb_ioim_get_sgaddr(ioim->dio, sgeid); + sge->sg_len = bfa_cb_ioim_get_sglen(ioim->dio, sgeid); + pgcumsz += sge->sg_len; + + /** + * set flags + */ + if (i < (nsges - 1)) + sge->flags = BFI_SGE_DATA; + else if (sgeid < (ioim->nsges - 1)) + sge->flags = BFI_SGE_DATA_CPL; + else + sge->flags = BFI_SGE_DATA_LAST; + } + + sgpg = (struct bfa_sgpg_s *) bfa_q_next(sgpg); + + /** + * set the link element of each page + */ + if (sgeid == ioim->nsges) { + sge->flags = BFI_SGE_PGDLEN; + sge->sga.a32.addr_lo = 0; + sge->sga.a32.addr_hi = 0; + } else { + sge->flags = BFI_SGE_LINK; + sge->sga = sgpg->sgpg_pa; + } + sge->sg_len = pgcumsz; + } while (sgeid < ioim->nsges); +} + +/** + * Send I/O abort request to firmware. + */ +static bfa_boolean_t +bfa_ioim_send_abort(struct bfa_ioim_s *ioim) +{ + struct bfa_itnim_s *itnim = ioim->itnim; + struct bfi_ioim_abort_req_s *m; + enum bfi_ioim_h2i msgop; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(ioim->bfa, itnim->reqq); + if (!m) + return BFA_FALSE; + + /** + * build i/o request message next + */ + if (ioim->iosp->abort_explicit) + msgop = BFI_IOIM_H2I_IOABORT_REQ; + else + msgop = BFI_IOIM_H2I_IOCLEANUP_REQ; + + bfi_h2i_set(m->mh, BFI_MC_IOIM, msgop, bfa_lpuid(ioim->bfa)); + m->io_tag = bfa_os_htons(ioim->iotag); + m->abort_tag = ++ioim->abort_tag; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(ioim->bfa, itnim->reqq); + return BFA_TRUE; +} + +/** + * Call to resume any I/O requests waiting for room in request queue. + */ +static void +bfa_ioim_qresume(void *cbarg) +{ + struct bfa_ioim_s *ioim = cbarg; + + bfa_fcpim_stats(ioim->fcpim, qresumes); + bfa_sm_send_event(ioim, BFA_IOIM_SM_QRESUME); +} + + +static void +bfa_ioim_notify_cleanup(struct bfa_ioim_s *ioim) +{ + /** + * Move IO from itnim queue to fcpim global queue since itnim will be + * freed. + */ + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &ioim->fcpim->ioim_comp_q); + + if (!ioim->iosp->tskim) { + if (ioim->fcpim->delay_comp && ioim->itnim->iotov_active) { + bfa_cb_dequeue(&ioim->hcb_qe); + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &ioim->itnim->delay_comp_q); + } + bfa_itnim_iodone(ioim->itnim); + } else + bfa_tskim_iodone(ioim->iosp->tskim); +} + +/** + * or after the link comes back. + */ +void +bfa_ioim_delayed_comp(struct bfa_ioim_s *ioim, bfa_boolean_t iotov) +{ + /** + * If path tov timer expired, failback with PATHTOV status - these + * IO requests are not normally retried by IO stack. + * + * Otherwise device cameback online and fail it with normal failed + * status so that IO stack retries these failed IO requests. + */ + if (iotov) + ioim->io_cbfn = __bfa_cb_ioim_pathtov; + else + ioim->io_cbfn = __bfa_cb_ioim_failed; + + bfa_cb_queue(ioim->bfa, &ioim->hcb_qe, ioim->io_cbfn, ioim); + + /** + * Move IO to fcpim global queue since itnim will be + * freed. + */ + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &ioim->fcpim->ioim_comp_q); +} + + + +/** + * bfa_ioim_friend + */ + +/** + * Memory allocation and initialization. + */ +void +bfa_ioim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo) +{ + struct bfa_ioim_s *ioim; + struct bfa_ioim_sp_s *iosp; + u16 i; + u8 *snsinfo; + u32 snsbufsz; + + /** + * claim memory first + */ + ioim = (struct bfa_ioim_s *) bfa_meminfo_kva(minfo); + fcpim->ioim_arr = ioim; + bfa_meminfo_kva(minfo) = (u8 *) (ioim + fcpim->num_ioim_reqs); + + iosp = (struct bfa_ioim_sp_s *) bfa_meminfo_kva(minfo); + fcpim->ioim_sp_arr = iosp; + bfa_meminfo_kva(minfo) = (u8 *) (iosp + fcpim->num_ioim_reqs); + + /** + * Claim DMA memory for per IO sense data. + */ + snsbufsz = fcpim->num_ioim_reqs * BFI_IOIM_SNSLEN; + fcpim->snsbase.pa = bfa_meminfo_dma_phys(minfo); + bfa_meminfo_dma_phys(minfo) += snsbufsz; + + fcpim->snsbase.kva = bfa_meminfo_dma_virt(minfo); + bfa_meminfo_dma_virt(minfo) += snsbufsz; + snsinfo = fcpim->snsbase.kva; + bfa_iocfc_set_snsbase(fcpim->bfa, fcpim->snsbase.pa); + + /** + * Initialize ioim free queues + */ + INIT_LIST_HEAD(&fcpim->ioim_free_q); + INIT_LIST_HEAD(&fcpim->ioim_resfree_q); + INIT_LIST_HEAD(&fcpim->ioim_comp_q); + + for (i = 0; i < fcpim->num_ioim_reqs; + i++, ioim++, iosp++, snsinfo += BFI_IOIM_SNSLEN) { + /* + * initialize IOIM + */ + bfa_os_memset(ioim, 0, sizeof(struct bfa_ioim_s)); + ioim->iotag = i; + ioim->bfa = fcpim->bfa; + ioim->fcpim = fcpim; + ioim->iosp = iosp; + iosp->snsinfo = snsinfo; + INIT_LIST_HEAD(&ioim->sgpg_q); + bfa_reqq_winit(&ioim->iosp->reqq_wait, + bfa_ioim_qresume, ioim); + bfa_sgpg_winit(&ioim->iosp->sgpg_wqe, + bfa_ioim_sgpg_alloced, ioim); + bfa_sm_set_state(ioim, bfa_ioim_sm_uninit); + + list_add_tail(&ioim->qe, &fcpim->ioim_free_q); + } +} + +/** + * Driver detach time call. + */ +void +bfa_ioim_detach(struct bfa_fcpim_mod_s *fcpim) +{ +} + +void +bfa_ioim_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfi_ioim_rsp_s *rsp = (struct bfi_ioim_rsp_s *) m; + struct bfa_ioim_s *ioim; + u16 iotag; + enum bfa_ioim_event evt = BFA_IOIM_SM_COMP; + + iotag = bfa_os_ntohs(rsp->io_tag); + + ioim = BFA_IOIM_FROM_TAG(fcpim, iotag); + bfa_assert(ioim->iotag == iotag); + + bfa_trc(ioim->bfa, ioim->iotag); + bfa_trc(ioim->bfa, rsp->io_status); + bfa_trc(ioim->bfa, rsp->reuse_io_tag); + + if (bfa_sm_cmp_state(ioim, bfa_ioim_sm_active)) + bfa_os_assign(ioim->iosp->comp_rspmsg, *m); + + switch (rsp->io_status) { + case BFI_IOIM_STS_OK: + bfa_fcpim_stats(fcpim, iocomp_ok); + if (rsp->reuse_io_tag == 0) + evt = BFA_IOIM_SM_DONE; + else + evt = BFA_IOIM_SM_COMP; + break; + + case BFI_IOIM_STS_TIMEDOUT: + case BFI_IOIM_STS_ABORTED: + rsp->io_status = BFI_IOIM_STS_ABORTED; + bfa_fcpim_stats(fcpim, iocomp_aborted); + if (rsp->reuse_io_tag == 0) + evt = BFA_IOIM_SM_DONE; + else + evt = BFA_IOIM_SM_COMP; + break; + + case BFI_IOIM_STS_PROTO_ERR: + bfa_fcpim_stats(fcpim, iocom_proto_err); + bfa_assert(rsp->reuse_io_tag); + evt = BFA_IOIM_SM_COMP; + break; + + case BFI_IOIM_STS_SQER_NEEDED: + bfa_fcpim_stats(fcpim, iocom_sqer_needed); + bfa_assert(rsp->reuse_io_tag == 0); + evt = BFA_IOIM_SM_SQRETRY; + break; + + case BFI_IOIM_STS_RES_FREE: + bfa_fcpim_stats(fcpim, iocom_res_free); + evt = BFA_IOIM_SM_FREE; + break; + + case BFI_IOIM_STS_HOST_ABORTED: + bfa_fcpim_stats(fcpim, iocom_hostabrts); + if (rsp->abort_tag != ioim->abort_tag) { + bfa_trc(ioim->bfa, rsp->abort_tag); + bfa_trc(ioim->bfa, ioim->abort_tag); + return; + } + + if (rsp->reuse_io_tag) + evt = BFA_IOIM_SM_ABORT_COMP; + else + evt = BFA_IOIM_SM_ABORT_DONE; + break; + + case BFI_IOIM_STS_UTAG: + bfa_fcpim_stats(fcpim, iocom_utags); + evt = BFA_IOIM_SM_COMP_UTAG; + break; + + default: + bfa_assert(0); + } + + bfa_sm_send_event(ioim, evt); +} + +void +bfa_ioim_good_comp_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfi_ioim_rsp_s *rsp = (struct bfi_ioim_rsp_s *) m; + struct bfa_ioim_s *ioim; + u16 iotag; + + iotag = bfa_os_ntohs(rsp->io_tag); + + ioim = BFA_IOIM_FROM_TAG(fcpim, iotag); + bfa_assert(ioim->iotag == iotag); + + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_sm_send_event(ioim, BFA_IOIM_SM_COMP_GOOD); +} + +/** + * Called by itnim to clean up IO while going offline. + */ +void +bfa_ioim_cleanup(struct bfa_ioim_s *ioim) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_fcpim_stats(ioim->fcpim, io_cleanups); + + ioim->iosp->tskim = NULL; + bfa_sm_send_event(ioim, BFA_IOIM_SM_CLEANUP); +} + +void +bfa_ioim_cleanup_tm(struct bfa_ioim_s *ioim, struct bfa_tskim_s *tskim) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_fcpim_stats(ioim->fcpim, io_tmaborts); + + ioim->iosp->tskim = tskim; + bfa_sm_send_event(ioim, BFA_IOIM_SM_CLEANUP); +} + +/** + * IOC failure handling. + */ +void +bfa_ioim_iocdisable(struct bfa_ioim_s *ioim) +{ + bfa_sm_send_event(ioim, BFA_IOIM_SM_HWFAIL); +} + +/** + * IO offline TOV popped. Fail the pending IO. + */ +void +bfa_ioim_tov(struct bfa_ioim_s *ioim) +{ + bfa_sm_send_event(ioim, BFA_IOIM_SM_IOTOV); +} + + + +/** + * bfa_ioim_api + */ + +/** + * Allocate IOIM resource for initiator mode I/O request. + */ +struct bfa_ioim_s * +bfa_ioim_alloc(struct bfa_s *bfa, struct bfad_ioim_s *dio, + struct bfa_itnim_s *itnim, u16 nsges) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfa_ioim_s *ioim; + + /** + * alocate IOIM resource + */ + bfa_q_deq(&fcpim->ioim_free_q, &ioim); + if (!ioim) { + bfa_fcpim_stats(fcpim, no_iotags); + return NULL; + } + + ioim->dio = dio; + ioim->itnim = itnim; + ioim->nsges = nsges; + ioim->nsgpgs = 0; + + bfa_stats(fcpim, total_ios); + bfa_stats(itnim, ios); + fcpim->ios_active++; + + list_add_tail(&ioim->qe, &itnim->io_q); + bfa_trc_fp(ioim->bfa, ioim->iotag); + + return ioim; +} + +void +bfa_ioim_free(struct bfa_ioim_s *ioim) +{ + struct bfa_fcpim_mod_s *fcpim = ioim->fcpim; + + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_assert_fp(bfa_sm_cmp_state(ioim, bfa_ioim_sm_uninit)); + + bfa_assert_fp(list_empty(&ioim->sgpg_q) + || (ioim->nsges > BFI_SGE_INLINE)); + + if (ioim->nsgpgs > 0) + bfa_sgpg_mfree(ioim->bfa, &ioim->sgpg_q, ioim->nsgpgs); + + bfa_stats(ioim->itnim, io_comps); + fcpim->ios_active--; + + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &fcpim->ioim_free_q); +} + +void +bfa_ioim_start(struct bfa_ioim_s *ioim) +{ + bfa_trc_fp(ioim->bfa, ioim->iotag); + bfa_sm_send_event(ioim, BFA_IOIM_SM_START); +} + +/** + * Driver I/O abort request. + */ +void +bfa_ioim_abort(struct bfa_ioim_s *ioim) +{ + bfa_trc(ioim->bfa, ioim->iotag); + bfa_fcpim_stats(ioim->fcpim, io_aborts); + bfa_sm_send_event(ioim, BFA_IOIM_SM_ABORT); +} + + diff --git a/drivers/scsi/bfa/bfa_itnim.c b/drivers/scsi/bfa/bfa_itnim.c new file mode 100644 index 000000000000..4d5c61a4f85c --- /dev/null +++ b/drivers/scsi/bfa/bfa_itnim.c @@ -0,0 +1,1088 @@ +/* + * 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. + */ + +#include +#include +#include "bfa_fcpim_priv.h" + +BFA_TRC_FILE(HAL, ITNIM); + +#define BFA_ITNIM_FROM_TAG(_fcpim, _tag) \ + ((_fcpim)->itnim_arr + ((_tag) & ((_fcpim)->num_itnims - 1))) + +#define bfa_fcpim_additn(__itnim) \ + list_add_tail(&(__itnim)->qe, &(__itnim)->fcpim->itnim_q) +#define bfa_fcpim_delitn(__itnim) do { \ + bfa_assert(bfa_q_is_on_q(&(__itnim)->fcpim->itnim_q, __itnim)); \ + list_del(&(__itnim)->qe); \ + bfa_assert(list_empty(&(__itnim)->io_q)); \ + bfa_assert(list_empty(&(__itnim)->io_cleanup_q)); \ + bfa_assert(list_empty(&(__itnim)->pending_q)); \ +} while (0) + +#define bfa_itnim_online_cb(__itnim) do { \ + if ((__itnim)->bfa->fcs) \ + bfa_cb_itnim_online((__itnim)->ditn); \ + else { \ + bfa_cb_queue((__itnim)->bfa, &(__itnim)->hcb_qe, \ + __bfa_cb_itnim_online, (__itnim)); \ + } \ +} while (0) + +#define bfa_itnim_offline_cb(__itnim) do { \ + if ((__itnim)->bfa->fcs) \ + bfa_cb_itnim_offline((__itnim)->ditn); \ + else { \ + bfa_cb_queue((__itnim)->bfa, &(__itnim)->hcb_qe, \ + __bfa_cb_itnim_offline, (__itnim)); \ + } \ +} while (0) + +#define bfa_itnim_sler_cb(__itnim) do { \ + if ((__itnim)->bfa->fcs) \ + bfa_cb_itnim_sler((__itnim)->ditn); \ + else { \ + bfa_cb_queue((__itnim)->bfa, &(__itnim)->hcb_qe, \ + __bfa_cb_itnim_sler, (__itnim)); \ + } \ +} while (0) + +/* + * forward declarations + */ +static void bfa_itnim_iocdisable_cleanup(struct bfa_itnim_s *itnim); +static bfa_boolean_t bfa_itnim_send_fwcreate(struct bfa_itnim_s *itnim); +static bfa_boolean_t bfa_itnim_send_fwdelete(struct bfa_itnim_s *itnim); +static void bfa_itnim_cleanp_comp(void *itnim_cbarg); +static void bfa_itnim_cleanup(struct bfa_itnim_s *itnim); +static void __bfa_cb_itnim_online(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_itnim_offline(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_itnim_sler(void *cbarg, bfa_boolean_t complete); +static void bfa_itnim_iotov_online(struct bfa_itnim_s *itnim); +static void bfa_itnim_iotov_cleanup(struct bfa_itnim_s *itnim); +static void bfa_itnim_iotov(void *itnim_arg); +static void bfa_itnim_iotov_start(struct bfa_itnim_s *itnim); +static void bfa_itnim_iotov_stop(struct bfa_itnim_s *itnim); +static void bfa_itnim_iotov_delete(struct bfa_itnim_s *itnim); + +/** + * bfa_itnim_sm BFA itnim state machine + */ + + +enum bfa_itnim_event { + BFA_ITNIM_SM_CREATE = 1, /* itnim is created */ + BFA_ITNIM_SM_ONLINE = 2, /* itnim is online */ + BFA_ITNIM_SM_OFFLINE = 3, /* itnim is offline */ + BFA_ITNIM_SM_FWRSP = 4, /* firmware response */ + BFA_ITNIM_SM_DELETE = 5, /* deleting an existing itnim */ + BFA_ITNIM_SM_CLEANUP = 6, /* IO cleanup completion */ + BFA_ITNIM_SM_SLER = 7, /* second level error recovery */ + BFA_ITNIM_SM_HWFAIL = 8, /* IOC h/w failure event */ + BFA_ITNIM_SM_QRESUME = 9, /* queue space available */ +}; + +static void bfa_itnim_sm_uninit(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_created(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_fwcreate(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_delete_pending(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_online(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_sler(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_cleanup_offline(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_cleanup_delete(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_fwdelete(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_offline(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_iocdisable(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_deleting(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_fwcreate_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_fwdelete_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); +static void bfa_itnim_sm_deleting_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event); + +/** + * Beginning/unallocated state - no events expected. + */ +static void +bfa_itnim_sm_uninit(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_CREATE: + bfa_sm_set_state(itnim, bfa_itnim_sm_created); + itnim->is_online = BFA_FALSE; + bfa_fcpim_additn(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Beginning state, only online event expected. + */ +static void +bfa_itnim_sm_created(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_ONLINE: + if (bfa_itnim_send_fwcreate(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate_qfull); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_fcpim_delitn(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + break; + + default: + bfa_assert(0); + } +} + +/** + * Waiting for itnim create response from firmware. + */ +static void +bfa_itnim_sm_fwcreate(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_FWRSP: + bfa_sm_set_state(itnim, bfa_itnim_sm_online); + itnim->is_online = BFA_TRUE; + bfa_itnim_iotov_online(itnim); + bfa_itnim_online_cb(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_delete_pending); + break; + + case BFA_ITNIM_SM_OFFLINE: + if (bfa_itnim_send_fwdelete(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_itnim_sm_fwcreate_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_QRESUME: + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate); + bfa_itnim_send_fwcreate(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_reqq_wcancel(&itnim->reqq_wait); + bfa_fcpim_delitn(itnim); + break; + + case BFA_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_itnim_sm_offline); + bfa_reqq_wcancel(&itnim->reqq_wait); + bfa_itnim_offline_cb(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_reqq_wcancel(&itnim->reqq_wait); + break; + + default: + bfa_assert(0); + } +} + +/** + * Waiting for itnim create response from firmware, a delete is pending. + */ +static void +bfa_itnim_sm_delete_pending(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_FWRSP: + if (bfa_itnim_send_fwdelete(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_fcpim_delitn(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Online state - normal parking state. + */ +static void +bfa_itnim_sm_online(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_offline); + itnim->is_online = BFA_FALSE; + bfa_itnim_iotov_start(itnim); + bfa_itnim_cleanup(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_delete); + itnim->is_online = BFA_FALSE; + bfa_itnim_cleanup(itnim); + break; + + case BFA_ITNIM_SM_SLER: + bfa_sm_set_state(itnim, bfa_itnim_sm_sler); + itnim->is_online = BFA_FALSE; + bfa_itnim_iotov_start(itnim); + bfa_itnim_sler_cb(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + itnim->is_online = BFA_FALSE; + bfa_itnim_iotov_start(itnim); + bfa_itnim_iocdisable_cleanup(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Second level error recovery need. + */ +static void +bfa_itnim_sm_sler(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_offline); + bfa_itnim_cleanup(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_delete); + bfa_itnim_cleanup(itnim); + bfa_itnim_iotov_delete(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_itnim_iocdisable_cleanup(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Going offline. Waiting for active IO cleanup. + */ +static void +bfa_itnim_sm_cleanup_offline(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_CLEANUP: + if (bfa_itnim_send_fwdelete(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete_qfull); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_cleanup_delete); + bfa_itnim_iotov_delete(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_itnim_iocdisable_cleanup(itnim); + bfa_itnim_offline_cb(itnim); + break; + + case BFA_ITNIM_SM_SLER: + break; + + default: + bfa_assert(0); + } +} + +/** + * Deleting itnim. Waiting for active IO cleanup. + */ +static void +bfa_itnim_sm_cleanup_delete(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_CLEANUP: + if (bfa_itnim_send_fwdelete(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_itnim_iocdisable_cleanup(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport offline. Fimrware itnim is being deleted - awaiting f/w response. + */ +static void +bfa_itnim_sm_fwdelete(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_FWRSP: + bfa_sm_set_state(itnim, bfa_itnim_sm_offline); + bfa_itnim_offline_cb(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_itnim_offline_cb(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_itnim_sm_fwdelete_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_QRESUME: + bfa_sm_set_state(itnim, bfa_itnim_sm_fwdelete); + bfa_itnim_send_fwdelete(itnim); + break; + + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + bfa_reqq_wcancel(&itnim->reqq_wait); + bfa_itnim_offline_cb(itnim); + break; + + default: + bfa_assert(0); + } +} + +/** + * Offline state. + */ +static void +bfa_itnim_sm_offline(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_itnim_iotov_delete(itnim); + bfa_fcpim_delitn(itnim); + break; + + case BFA_ITNIM_SM_ONLINE: + if (bfa_itnim_send_fwcreate(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_iocdisable); + break; + + default: + bfa_assert(0); + } +} + +/** + * IOC h/w failed state. + */ +static void +bfa_itnim_sm_iocdisable(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_itnim_iotov_delete(itnim); + bfa_fcpim_delitn(itnim); + break; + + case BFA_ITNIM_SM_OFFLINE: + bfa_itnim_offline_cb(itnim); + break; + + case BFA_ITNIM_SM_ONLINE: + if (bfa_itnim_send_fwcreate(itnim)) + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate); + else + bfa_sm_set_state(itnim, bfa_itnim_sm_fwcreate_qfull); + break; + + case BFA_ITNIM_SM_HWFAIL: + break; + + default: + bfa_assert(0); + } +} + +/** + * Itnim is deleted, waiting for firmware response to delete. + */ +static void +bfa_itnim_sm_deleting(struct bfa_itnim_s *itnim, enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_FWRSP: + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_fcpim_delitn(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_itnim_sm_deleting_qfull(struct bfa_itnim_s *itnim, + enum bfa_itnim_event event) +{ + bfa_trc(itnim->bfa, itnim->rport->rport_tag); + bfa_trc(itnim->bfa, event); + + switch (event) { + case BFA_ITNIM_SM_QRESUME: + bfa_sm_set_state(itnim, bfa_itnim_sm_deleting); + bfa_itnim_send_fwdelete(itnim); + break; + + case BFA_ITNIM_SM_HWFAIL: + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + bfa_reqq_wcancel(&itnim->reqq_wait); + bfa_fcpim_delitn(itnim); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * bfa_itnim_private + */ + +/** + * Initiate cleanup of all IOs on an IOC failure. + */ +static void +bfa_itnim_iocdisable_cleanup(struct bfa_itnim_s *itnim) +{ + struct bfa_tskim_s *tskim; + struct bfa_ioim_s *ioim; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &itnim->tsk_q) { + tskim = (struct bfa_tskim_s *) qe; + bfa_tskim_iocdisable(tskim); + } + + list_for_each_safe(qe, qen, &itnim->io_q) { + ioim = (struct bfa_ioim_s *) qe; + bfa_ioim_iocdisable(ioim); + } + + /** + * For IO request in pending queue, we pretend an early timeout. + */ + list_for_each_safe(qe, qen, &itnim->pending_q) { + ioim = (struct bfa_ioim_s *) qe; + bfa_ioim_tov(ioim); + } + + list_for_each_safe(qe, qen, &itnim->io_cleanup_q) { + ioim = (struct bfa_ioim_s *) qe; + bfa_ioim_iocdisable(ioim); + } +} + +/** + * IO cleanup completion + */ +static void +bfa_itnim_cleanp_comp(void *itnim_cbarg) +{ + struct bfa_itnim_s *itnim = itnim_cbarg; + + bfa_stats(itnim, cleanup_comps); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_CLEANUP); +} + +/** + * Initiate cleanup of all IOs. + */ +static void +bfa_itnim_cleanup(struct bfa_itnim_s *itnim) +{ + struct bfa_ioim_s *ioim; + struct bfa_tskim_s *tskim; + struct list_head *qe, *qen; + + bfa_wc_init(&itnim->wc, bfa_itnim_cleanp_comp, itnim); + + list_for_each_safe(qe, qen, &itnim->io_q) { + ioim = (struct bfa_ioim_s *) qe; + + /** + * Move IO to a cleanup queue from active queue so that a later + * TM will not pickup this IO. + */ + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &itnim->io_cleanup_q); + + bfa_wc_up(&itnim->wc); + bfa_ioim_cleanup(ioim); + } + + list_for_each_safe(qe, qen, &itnim->tsk_q) { + tskim = (struct bfa_tskim_s *) qe; + bfa_wc_up(&itnim->wc); + bfa_tskim_cleanup(tskim); + } + + bfa_wc_wait(&itnim->wc); +} + +static void +__bfa_cb_itnim_online(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_itnim_s *itnim = cbarg; + + if (complete) + bfa_cb_itnim_online(itnim->ditn); +} + +static void +__bfa_cb_itnim_offline(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_itnim_s *itnim = cbarg; + + if (complete) + bfa_cb_itnim_offline(itnim->ditn); +} + +static void +__bfa_cb_itnim_sler(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_itnim_s *itnim = cbarg; + + if (complete) + bfa_cb_itnim_sler(itnim->ditn); +} + +/** + * Call to resume any I/O requests waiting for room in request queue. + */ +static void +bfa_itnim_qresume(void *cbarg) +{ + struct bfa_itnim_s *itnim = cbarg; + + bfa_sm_send_event(itnim, BFA_ITNIM_SM_QRESUME); +} + + + + +/** + * bfa_itnim_public + */ + +void +bfa_itnim_iodone(struct bfa_itnim_s *itnim) +{ + bfa_wc_down(&itnim->wc); +} + +void +bfa_itnim_tskdone(struct bfa_itnim_s *itnim) +{ + bfa_wc_down(&itnim->wc); +} + +void +bfa_itnim_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len) +{ + /** + * ITN memory + */ + *km_len += cfg->fwcfg.num_rports * sizeof(struct bfa_itnim_s); +} + +void +bfa_itnim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo) +{ + struct bfa_s *bfa = fcpim->bfa; + struct bfa_itnim_s *itnim; + int i; + + INIT_LIST_HEAD(&fcpim->itnim_q); + + itnim = (struct bfa_itnim_s *) bfa_meminfo_kva(minfo); + fcpim->itnim_arr = itnim; + + for (i = 0; i < fcpim->num_itnims; i++, itnim++) { + bfa_os_memset(itnim, 0, sizeof(struct bfa_itnim_s)); + itnim->bfa = bfa; + itnim->fcpim = fcpim; + itnim->reqq = BFA_REQQ_QOS_LO; + itnim->rport = BFA_RPORT_FROM_TAG(bfa, i); + itnim->iotov_active = BFA_FALSE; + bfa_reqq_winit(&itnim->reqq_wait, bfa_itnim_qresume, itnim); + + INIT_LIST_HEAD(&itnim->io_q); + INIT_LIST_HEAD(&itnim->io_cleanup_q); + INIT_LIST_HEAD(&itnim->pending_q); + INIT_LIST_HEAD(&itnim->tsk_q); + INIT_LIST_HEAD(&itnim->delay_comp_q); + bfa_sm_set_state(itnim, bfa_itnim_sm_uninit); + } + + bfa_meminfo_kva(minfo) = (u8 *) itnim; +} + +void +bfa_itnim_iocdisable(struct bfa_itnim_s *itnim) +{ + bfa_stats(itnim, ioc_disabled); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_HWFAIL); +} + +static bfa_boolean_t +bfa_itnim_send_fwcreate(struct bfa_itnim_s *itnim) +{ + struct bfi_itnim_create_req_s *m; + + itnim->msg_no++; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(itnim->bfa, itnim->reqq); + if (!m) { + bfa_reqq_wait(itnim->bfa, itnim->reqq, &itnim->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_ITNIM, BFI_ITNIM_H2I_CREATE_REQ, + bfa_lpuid(itnim->bfa)); + m->fw_handle = itnim->rport->fw_handle; + m->class = FC_CLASS_3; + m->seq_rec = itnim->seq_rec; + m->msg_no = itnim->msg_no; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(itnim->bfa, itnim->reqq); + return BFA_TRUE; +} + +static bfa_boolean_t +bfa_itnim_send_fwdelete(struct bfa_itnim_s *itnim) +{ + struct bfi_itnim_delete_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(itnim->bfa, itnim->reqq); + if (!m) { + bfa_reqq_wait(itnim->bfa, itnim->reqq, &itnim->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_ITNIM, BFI_ITNIM_H2I_DELETE_REQ, + bfa_lpuid(itnim->bfa)); + m->fw_handle = itnim->rport->fw_handle; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(itnim->bfa, itnim->reqq); + return BFA_TRUE; +} + +/** + * Cleanup all pending failed inflight requests. + */ +static void +bfa_itnim_delayed_comp(struct bfa_itnim_s *itnim, bfa_boolean_t iotov) +{ + struct bfa_ioim_s *ioim; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &itnim->delay_comp_q) { + ioim = (struct bfa_ioim_s *)qe; + bfa_ioim_delayed_comp(ioim, iotov); + } +} + +/** + * Start all pending IO requests. + */ +static void +bfa_itnim_iotov_online(struct bfa_itnim_s *itnim) +{ + struct bfa_ioim_s *ioim; + + bfa_itnim_iotov_stop(itnim); + + /** + * Abort all inflight IO requests in the queue + */ + bfa_itnim_delayed_comp(itnim, BFA_FALSE); + + /** + * Start all pending IO requests. + */ + while (!list_empty(&itnim->pending_q)) { + bfa_q_deq(&itnim->pending_q, &ioim); + list_add_tail(&ioim->qe, &itnim->io_q); + bfa_ioim_start(ioim); + } +} + +/** + * Fail all pending IO requests + */ +static void +bfa_itnim_iotov_cleanup(struct bfa_itnim_s *itnim) +{ + struct bfa_ioim_s *ioim; + + /** + * Fail all inflight IO requests in the queue + */ + bfa_itnim_delayed_comp(itnim, BFA_TRUE); + + /** + * Fail any pending IO requests. + */ + while (!list_empty(&itnim->pending_q)) { + bfa_q_deq(&itnim->pending_q, &ioim); + list_add_tail(&ioim->qe, &ioim->fcpim->ioim_comp_q); + bfa_ioim_tov(ioim); + } +} + +/** + * IO TOV timer callback. Fail any pending IO requests. + */ +static void +bfa_itnim_iotov(void *itnim_arg) +{ + struct bfa_itnim_s *itnim = itnim_arg; + + itnim->iotov_active = BFA_FALSE; + + bfa_cb_itnim_tov_begin(itnim->ditn); + bfa_itnim_iotov_cleanup(itnim); + bfa_cb_itnim_tov(itnim->ditn); +} + +/** + * Start IO TOV timer for failing back pending IO requests in offline state. + */ +static void +bfa_itnim_iotov_start(struct bfa_itnim_s *itnim) +{ + if (itnim->fcpim->path_tov > 0) { + + itnim->iotov_active = BFA_TRUE; + bfa_assert(bfa_itnim_hold_io(itnim)); + bfa_timer_start(itnim->bfa, &itnim->timer, + bfa_itnim_iotov, itnim, itnim->fcpim->path_tov); + } +} + +/** + * Stop IO TOV timer. + */ +static void +bfa_itnim_iotov_stop(struct bfa_itnim_s *itnim) +{ + if (itnim->iotov_active) { + itnim->iotov_active = BFA_FALSE; + bfa_timer_stop(&itnim->timer); + } +} + +/** + * Stop IO TOV timer. + */ +static void +bfa_itnim_iotov_delete(struct bfa_itnim_s *itnim) +{ + bfa_boolean_t pathtov_active = BFA_FALSE; + + if (itnim->iotov_active) + pathtov_active = BFA_TRUE; + + bfa_itnim_iotov_stop(itnim); + if (pathtov_active) + bfa_cb_itnim_tov_begin(itnim->ditn); + bfa_itnim_iotov_cleanup(itnim); + if (pathtov_active) + bfa_cb_itnim_tov(itnim->ditn); +} + + + +/** + * bfa_itnim_public + */ + +/** + * Itnim interrupt processing. + */ +void +bfa_itnim_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + union bfi_itnim_i2h_msg_u msg; + struct bfa_itnim_s *itnim; + + bfa_trc(bfa, m->mhdr.msg_id); + + msg.msg = m; + + switch (m->mhdr.msg_id) { + case BFI_ITNIM_I2H_CREATE_RSP: + itnim = BFA_ITNIM_FROM_TAG(fcpim, + msg.create_rsp->bfa_handle); + bfa_assert(msg.create_rsp->status == BFA_STATUS_OK); + bfa_stats(itnim, create_comps); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_FWRSP); + break; + + case BFI_ITNIM_I2H_DELETE_RSP: + itnim = BFA_ITNIM_FROM_TAG(fcpim, + msg.delete_rsp->bfa_handle); + bfa_assert(msg.delete_rsp->status == BFA_STATUS_OK); + bfa_stats(itnim, delete_comps); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_FWRSP); + break; + + case BFI_ITNIM_I2H_SLER_EVENT: + itnim = BFA_ITNIM_FROM_TAG(fcpim, + msg.sler_event->bfa_handle); + bfa_stats(itnim, sler_events); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_SLER); + break; + + default: + bfa_trc(bfa, m->mhdr.msg_id); + bfa_assert(0); + } +} + + + +/** + * bfa_itnim_api + */ + +struct bfa_itnim_s * +bfa_itnim_create(struct bfa_s *bfa, struct bfa_rport_s *rport, void *ditn) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfa_itnim_s *itnim; + + itnim = BFA_ITNIM_FROM_TAG(fcpim, rport->rport_tag); + bfa_assert(itnim->rport == rport); + + itnim->ditn = ditn; + + bfa_stats(itnim, creates); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_CREATE); + + return (itnim); +} + +void +bfa_itnim_delete(struct bfa_itnim_s *itnim) +{ + bfa_stats(itnim, deletes); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_DELETE); +} + +void +bfa_itnim_online(struct bfa_itnim_s *itnim, bfa_boolean_t seq_rec) +{ + itnim->seq_rec = seq_rec; + bfa_stats(itnim, onlines); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_ONLINE); +} + +void +bfa_itnim_offline(struct bfa_itnim_s *itnim) +{ + bfa_stats(itnim, offlines); + bfa_sm_send_event(itnim, BFA_ITNIM_SM_OFFLINE); +} + +/** + * Return true if itnim is considered offline for holding off IO request. + * IO is not held if itnim is being deleted. + */ +bfa_boolean_t +bfa_itnim_hold_io(struct bfa_itnim_s *itnim) +{ + return ( + itnim->fcpim->path_tov && itnim->iotov_active && + (bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwcreate) || + bfa_sm_cmp_state(itnim, bfa_itnim_sm_sler) || + bfa_sm_cmp_state(itnim, bfa_itnim_sm_cleanup_offline) || + bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwdelete) || + bfa_sm_cmp_state(itnim, bfa_itnim_sm_offline) || + bfa_sm_cmp_state(itnim, bfa_itnim_sm_iocdisable)) +); +} + +void +bfa_itnim_get_stats(struct bfa_itnim_s *itnim, + struct bfa_itnim_hal_stats_s *stats) +{ + *stats = itnim->stats; +} + +void +bfa_itnim_clear_stats(struct bfa_itnim_s *itnim) +{ + bfa_os_memset(&itnim->stats, 0, sizeof(itnim->stats)); +} + + diff --git a/drivers/scsi/bfa/bfa_log.c b/drivers/scsi/bfa/bfa_log.c new file mode 100644 index 000000000000..c2735e55cf03 --- /dev/null +++ b/drivers/scsi/bfa/bfa_log.c @@ -0,0 +1,346 @@ +/* + * 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. + */ + +/** + * bfa_log.c BFA log library + */ + +#include +#include + +/* + * global log info structure + */ +struct bfa_log_info_s { + u32 start_idx; /* start index for a module */ + u32 total_count; /* total count for a module */ + enum bfa_log_severity level; /* global log level */ + bfa_log_cb_t cbfn; /* callback function */ +}; + +static struct bfa_log_info_s bfa_log_info[BFA_LOG_MODULE_ID_MAX + 1]; +static u32 bfa_log_msg_total_count; +static int bfa_log_initialized; + +static char *bfa_log_severity[] = + { "[none]", "[critical]", "[error]", "[warn]", "[info]", "" }; + +/** + * BFA log library initialization + * + * The log library initialization includes the following, + * - set log instance name and callback function + * - read the message array generated from xml files + * - calculate start index for each module + * - calculate message count for each module + * - perform error checking + * + * @param[in] log_mod - log module info + * @param[in] instance_name - instance name + * @param[in] cbfn - callback function + * + * It return 0 on success, or -1 on failure + */ +int +bfa_log_init(struct bfa_log_mod_s *log_mod, char *instance_name, + bfa_log_cb_t cbfn) +{ + struct bfa_log_msgdef_s *msg; + u32 pre_mod_id = 0; + u32 cur_mod_id = 0; + u32 i, pre_idx, idx, msg_id; + + /* + * set instance name + */ + if (log_mod) { + strncpy(log_mod->instance_info, instance_name, + sizeof(log_mod->instance_info)); + log_mod->cbfn = cbfn; + for (i = 0; i <= BFA_LOG_MODULE_ID_MAX; i++) + log_mod->log_level[i] = BFA_LOG_WARNING; + } + + if (bfa_log_initialized) + return 0; + + for (i = 0; i <= BFA_LOG_MODULE_ID_MAX; i++) { + bfa_log_info[i].start_idx = 0; + bfa_log_info[i].total_count = 0; + bfa_log_info[i].level = BFA_LOG_WARNING; + bfa_log_info[i].cbfn = cbfn; + } + + pre_idx = 0; + idx = 0; + msg = bfa_log_msg_array; + msg_id = BFA_LOG_GET_MSG_ID(msg); + pre_mod_id = BFA_LOG_GET_MOD_ID(msg_id); + while (msg_id != 0) { + cur_mod_id = BFA_LOG_GET_MOD_ID(msg_id); + + if (cur_mod_id > BFA_LOG_MODULE_ID_MAX) { + cbfn(log_mod, msg_id, + "%s%s log: module id %u out of range\n", + BFA_LOG_CAT_NAME, + bfa_log_severity[BFA_LOG_ERROR], + cur_mod_id); + return -1; + } + + if (pre_mod_id > BFA_LOG_MODULE_ID_MAX) { + cbfn(log_mod, msg_id, + "%s%s log: module id %u out of range\n", + BFA_LOG_CAT_NAME, + bfa_log_severity[BFA_LOG_ERROR], + pre_mod_id); + return -1; + } + + if (cur_mod_id != pre_mod_id) { + bfa_log_info[pre_mod_id].start_idx = pre_idx; + bfa_log_info[pre_mod_id].total_count = idx - pre_idx; + pre_mod_id = cur_mod_id; + pre_idx = idx; + } + + idx++; + msg++; + msg_id = BFA_LOG_GET_MSG_ID(msg); + } + + bfa_log_info[cur_mod_id].start_idx = pre_idx; + bfa_log_info[cur_mod_id].total_count = idx - pre_idx; + bfa_log_msg_total_count = idx; + + cbfn(log_mod, msg_id, "%s%s log: init OK, msg total count %u\n", + BFA_LOG_CAT_NAME, + bfa_log_severity[BFA_LOG_INFO], bfa_log_msg_total_count); + + bfa_log_initialized = 1; + + return 0; +} + +/** + * BFA log set log level for a module + * + * @param[in] log_mod - log module info + * @param[in] mod_id - module id + * @param[in] log_level - log severity level + * + * It return BFA_STATUS_OK on success, or > 0 on failure + */ +bfa_status_t +bfa_log_set_level(struct bfa_log_mod_s *log_mod, int mod_id, + enum bfa_log_severity log_level) +{ + if (mod_id <= BFA_LOG_UNUSED_ID || mod_id > BFA_LOG_MODULE_ID_MAX) + return BFA_STATUS_EINVAL; + + if (log_level <= BFA_LOG_INVALID || log_level > BFA_LOG_LEVEL_MAX) + return BFA_STATUS_EINVAL; + + if (log_mod) + log_mod->log_level[mod_id] = log_level; + else + bfa_log_info[mod_id].level = log_level; + + return BFA_STATUS_OK; +} + +/** + * BFA log set log level for all modules + * + * @param[in] log_mod - log module info + * @param[in] log_level - log severity level + * + * It return BFA_STATUS_OK on success, or > 0 on failure + */ +bfa_status_t +bfa_log_set_level_all(struct bfa_log_mod_s *log_mod, + enum bfa_log_severity log_level) +{ + int mod_id = BFA_LOG_UNUSED_ID + 1; + + if (log_level <= BFA_LOG_INVALID || log_level > BFA_LOG_LEVEL_MAX) + return BFA_STATUS_EINVAL; + + if (log_mod) { + for (; mod_id <= BFA_LOG_MODULE_ID_MAX; mod_id++) + log_mod->log_level[mod_id] = log_level; + } else { + for (; mod_id <= BFA_LOG_MODULE_ID_MAX; mod_id++) + bfa_log_info[mod_id].level = log_level; + } + + return BFA_STATUS_OK; +} + +/** + * BFA log set log level for all aen sub-modules + * + * @param[in] log_mod - log module info + * @param[in] log_level - log severity level + * + * It return BFA_STATUS_OK on success, or > 0 on failure + */ +bfa_status_t +bfa_log_set_level_aen(struct bfa_log_mod_s *log_mod, + enum bfa_log_severity log_level) +{ + int mod_id = BFA_LOG_AEN_MIN + 1; + + if (log_mod) { + for (; mod_id <= BFA_LOG_AEN_MAX; mod_id++) + log_mod->log_level[mod_id] = log_level; + } else { + for (; mod_id <= BFA_LOG_AEN_MAX; mod_id++) + bfa_log_info[mod_id].level = log_level; + } + + return BFA_STATUS_OK; +} + +/** + * BFA log get log level for a module + * + * @param[in] log_mod - log module info + * @param[in] mod_id - module id + * + * It returns log level or -1 on error + */ +enum bfa_log_severity +bfa_log_get_level(struct bfa_log_mod_s *log_mod, int mod_id) +{ + if (mod_id <= BFA_LOG_UNUSED_ID || mod_id > BFA_LOG_MODULE_ID_MAX) + return BFA_LOG_INVALID; + + if (log_mod) + return (log_mod->log_level[mod_id]); + else + return (bfa_log_info[mod_id].level); +} + +enum bfa_log_severity +bfa_log_get_msg_level(struct bfa_log_mod_s *log_mod, u32 msg_id) +{ + struct bfa_log_msgdef_s *msg; + u32 mod = BFA_LOG_GET_MOD_ID(msg_id); + u32 idx = BFA_LOG_GET_MSG_IDX(msg_id) - 1; + + if (!bfa_log_initialized) + return BFA_LOG_INVALID; + + if (mod > BFA_LOG_MODULE_ID_MAX) + return BFA_LOG_INVALID; + + if (idx >= bfa_log_info[mod].total_count) { + bfa_log_info[mod].cbfn(log_mod, msg_id, + "%s%s log: inconsistent idx %u vs. total count %u\n", + BFA_LOG_CAT_NAME, bfa_log_severity[BFA_LOG_ERROR], idx, + bfa_log_info[mod].total_count); + return BFA_LOG_INVALID; + } + + msg = bfa_log_msg_array + bfa_log_info[mod].start_idx + idx; + if (msg_id != BFA_LOG_GET_MSG_ID(msg)) { + bfa_log_info[mod].cbfn(log_mod, msg_id, + "%s%s log: inconsistent msg id %u array msg id %u\n", + BFA_LOG_CAT_NAME, bfa_log_severity[BFA_LOG_ERROR], + msg_id, BFA_LOG_GET_MSG_ID(msg)); + return BFA_LOG_INVALID; + } + + return BFA_LOG_GET_SEVERITY(msg); +} + +/** + * BFA log message handling + * + * BFA log message handling finds the message based on message id and prints + * out the message based on its format and arguments. It also does prefix + * the severity etc. + * + * @param[in] log_mod - log module info + * @param[in] msg_id - message id + * @param[in] ... - message arguments + * + * It return 0 on success, or -1 on errors + */ +int +bfa_log(struct bfa_log_mod_s *log_mod, u32 msg_id, ...) +{ + va_list ap; + char buf[256]; + struct bfa_log_msgdef_s *msg; + int log_level; + u32 mod = BFA_LOG_GET_MOD_ID(msg_id); + u32 idx = BFA_LOG_GET_MSG_IDX(msg_id) - 1; + + if (!bfa_log_initialized) + return -1; + + if (mod > BFA_LOG_MODULE_ID_MAX) + return -1; + + if (idx >= bfa_log_info[mod].total_count) { + bfa_log_info[mod]. + cbfn + (log_mod, msg_id, + "%s%s log: inconsistent idx %u vs. total count %u\n", + BFA_LOG_CAT_NAME, bfa_log_severity[BFA_LOG_ERROR], idx, + bfa_log_info[mod].total_count); + return -1; + } + + msg = bfa_log_msg_array + bfa_log_info[mod].start_idx + idx; + if (msg_id != BFA_LOG_GET_MSG_ID(msg)) { + bfa_log_info[mod]. + cbfn + (log_mod, msg_id, + "%s%s log: inconsistent msg id %u array msg id %u\n", + BFA_LOG_CAT_NAME, bfa_log_severity[BFA_LOG_ERROR], + msg_id, BFA_LOG_GET_MSG_ID(msg)); + return -1; + } + + log_level = log_mod ? log_mod->log_level[mod] : bfa_log_info[mod].level; + if ((BFA_LOG_GET_SEVERITY(msg) > log_level) && + (msg->attributes != BFA_LOG_ATTR_NONE)) + return 0; + + va_start(ap, msg_id); + bfa_os_vsprintf(buf, BFA_LOG_GET_MSG_FMT_STRING(msg), ap); + va_end(ap); + + if (log_mod) + log_mod->cbfn(log_mod, msg_id, "%s[%s]%s%s %s: %s\n", + BFA_LOG_CAT_NAME, log_mod->instance_info, + bfa_log_severity[BFA_LOG_GET_SEVERITY(msg)], + (msg->attributes & BFA_LOG_ATTR_AUDIT) + ? " (audit) " : "", msg->msg_value, buf); + else + bfa_log_info[mod].cbfn(log_mod, msg_id, "%s%s%s %s: %s\n", + BFA_LOG_CAT_NAME, + bfa_log_severity[BFA_LOG_GET_SEVERITY(msg)], + (msg->attributes & BFA_LOG_ATTR_AUDIT) ? + " (audit) " : "", msg->msg_value, buf); + + return 0; +} + diff --git a/drivers/scsi/bfa/bfa_log_module.c b/drivers/scsi/bfa/bfa_log_module.c new file mode 100644 index 000000000000..5c154d341d69 --- /dev/null +++ b/drivers/scsi/bfa/bfa_log_module.c @@ -0,0 +1,451 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct bfa_log_msgdef_s bfa_log_msg_array[] = { + + +/* messages define for BFA_AEN_CAT_ADAPTER Module */ +{BFA_AEN_ADAPTER_ADD, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ADAPTER_ADD", + "New adapter found: SN = %s, base port WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_ADAPTER_REMOVE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_ADAPTER_REMOVE", + "Adapter removed: SN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + + + + +/* messages define for BFA_AEN_CAT_AUDIT Module */ +{BFA_AEN_AUDIT_AUTH_ENABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "BFA_AEN_AUDIT_AUTH_ENABLE", + "Authentication enabled for base port: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_AUDIT_AUTH_DISABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "BFA_AEN_AUDIT_AUTH_DISABLE", + "Authentication disabled for base port: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + + + + +/* messages define for BFA_AEN_CAT_ETHPORT Module */ +{BFA_AEN_ETHPORT_LINKUP, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ETHPORT_LINKUP", + "Base port ethernet linkup: mac = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_ETHPORT_LINKDOWN, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ETHPORT_LINKDOWN", + "Base port ethernet linkdown: mac = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_ETHPORT_ENABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ETHPORT_ENABLE", + "Base port ethernet interface enabled: mac = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_ETHPORT_DISABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ETHPORT_DISABLE", + "Base port ethernet interface disabled: mac = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + + + + +/* messages define for BFA_AEN_CAT_IOC Module */ +{BFA_AEN_IOC_HBGOOD, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_IOC_HBGOOD", + "Heart Beat of IOC %d is good.", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_IOC_HBFAIL, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_CRITICAL, + "BFA_AEN_IOC_HBFAIL", + "Heart Beat of IOC %d has failed.", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_IOC_ENABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_IOC_ENABLE", + "IOC %d is enabled.", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_IOC_DISABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_IOC_DISABLE", + "IOC %d is disabled.", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_IOC_FWMISMATCH, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_CRITICAL, "BFA_AEN_IOC_FWMISMATCH", + "Running firmware version is incompatible with the driver version.", + (0), 0}, + + + + +/* messages define for BFA_AEN_CAT_ITNIM Module */ +{BFA_AEN_ITNIM_ONLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ITNIM_ONLINE", + "Target (WWN = %s) is online for initiator (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_ITNIM_OFFLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_ITNIM_OFFLINE", + "Target (WWN = %s) offlined by initiator (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_ITNIM_DISCONNECT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_ERROR, "BFA_AEN_ITNIM_DISCONNECT", + "Target (WWN = %s) connectivity lost for initiator (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + + + + +/* messages define for BFA_AEN_CAT_LPORT Module */ +{BFA_AEN_LPORT_NEW, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_LPORT_NEW", + "New logical port created: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_DELETE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_LPORT_DELETE", + "Logical port deleted: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_ONLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_LPORT_ONLINE", + "Logical port online: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_OFFLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_LPORT_OFFLINE", + "Logical port taken offline: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_DISCONNECT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_ERROR, "BFA_AEN_LPORT_DISCONNECT", + "Logical port lost fabric connectivity: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_NEW_PROP, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_LPORT_NEW_PROP", + "New virtual port created using proprietary interface: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_DELETE_PROP, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "BFA_AEN_LPORT_DELETE_PROP", + "Virtual port deleted using proprietary interface: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_NEW_STANDARD, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "BFA_AEN_LPORT_NEW_STANDARD", + "New virtual port created using standard interface: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_DELETE_STANDARD, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "BFA_AEN_LPORT_DELETE_STANDARD", + "Virtual port deleted using standard interface: WWN = %s, Role = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_LPORT_NPIV_DUP_WWN, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_LPORT_NPIV_DUP_WWN", + "Virtual port login failed. Duplicate WWN = %s reported by fabric.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_LPORT_NPIV_FABRIC_MAX, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_LPORT_NPIV_FABRIC_MAX", + "Virtual port (WWN = %s) login failed. Max NPIV ports already exist in" + " fabric/fport.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_LPORT_NPIV_UNKNOWN, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_LPORT_NPIV_UNKNOWN", + "Virtual port (WWN = %s) login failed.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + + + + +/* messages define for BFA_AEN_CAT_PORT Module */ +{BFA_AEN_PORT_ONLINE, BFA_LOG_ATTR_NONE, BFA_LOG_INFO, "BFA_AEN_PORT_ONLINE", + "Base port online: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_OFFLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_WARNING, + "BFA_AEN_PORT_OFFLINE", + "Base port offline: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_RLIR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_PORT_RLIR", + "RLIR event not supported.", + (0), 0}, + +{BFA_AEN_PORT_SFP_INSERT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_PORT_SFP_INSERT", + "New SFP found: WWN/MAC = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_SFP_REMOVE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_PORT_SFP_REMOVE", + "SFP removed: WWN/MAC = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_SFP_POM, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_WARNING, + "BFA_AEN_PORT_SFP_POM", + "SFP POM level to %s: WWN/MAC = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_PORT_ENABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_PORT_ENABLE", + "Base port enabled: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_DISABLE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_PORT_DISABLE", + "Base port disabled: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_AUTH_ON, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_PORT_AUTH_ON", + "Authentication successful for base port: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_AUTH_OFF, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_ERROR, + "BFA_AEN_PORT_AUTH_OFF", + "Authentication unsuccessful for base port: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_DISCONNECT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_ERROR, + "BFA_AEN_PORT_DISCONNECT", + "Base port (WWN = %s) lost fabric connectivity.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_QOS_NEG, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_WARNING, + "BFA_AEN_PORT_QOS_NEG", + "QOS negotiation failed for base port: WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_FABRIC_NAME_CHANGE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_PORT_FABRIC_NAME_CHANGE", + "Base port WWN = %s, Fabric WWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_PORT_SFP_ACCESS_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_PORT_SFP_ACCESS_ERROR", + "SFP access error: WWN/MAC = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_AEN_PORT_SFP_UNSUPPORT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_WARNING, "BFA_AEN_PORT_SFP_UNSUPPORT", + "Unsupported SFP found: WWN/MAC = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + + + + +/* messages define for BFA_AEN_CAT_RPORT Module */ +{BFA_AEN_RPORT_ONLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_RPORT_ONLINE", + "Remote port (WWN = %s) online for logical port (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_RPORT_OFFLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_RPORT_OFFLINE", + "Remote port (WWN = %s) offlined by logical port (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_RPORT_DISCONNECT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_ERROR, "BFA_AEN_RPORT_DISCONNECT", + "Remote port (WWN = %s) connectivity lost for logical port (WWN = %s).", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | 0), 2}, + +{BFA_AEN_RPORT_QOS_PRIO, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_RPORT_QOS_PRIO", + "QOS priority changed to %s: RPWWN = %s and LPWWN = %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | 0), 3}, + +{BFA_AEN_RPORT_QOS_FLOWID, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "BFA_AEN_RPORT_QOS_FLOWID", + "QOS flow ID changed to %d: RPWWN = %s and LPWWN = %s.", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | 0), 3}, + + + + +/* messages define for FCS Module */ +{BFA_LOG_FCS_FABRIC_NOSWITCH, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "FCS_FABRIC_NOSWITCH", + "No switched fabric presence is detected.", + (0), 0}, + +{BFA_LOG_FCS_FABRIC_ISOLATED, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "FCS_FABRIC_ISOLATED", + "Port is isolated due to VF_ID mismatch. PWWN: %s, Port VF_ID: %04x and" + " switch port VF_ID: %04x.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_X << BFA_LOG_ARG1) | + (BFA_LOG_X << BFA_LOG_ARG2) | 0), 3}, + + + + +/* messages define for HAL Module */ +{BFA_LOG_HAL_ASSERT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_ERROR, + "HAL_ASSERT", + "Assertion failure: %s:%d: %s", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_D << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | 0), 3}, + +{BFA_LOG_HAL_HEARTBEAT_FAILURE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_CRITICAL, "HAL_HEARTBEAT_FAILURE", + "Firmware heartbeat failure at %d", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_HAL_FCPIM_PARM_INVALID, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "HAL_FCPIM_PARM_INVALID", + "Driver configuration %s value %d is invalid. Value should be within" + " %d and %d.", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_D << BFA_LOG_ARG1) | + (BFA_LOG_D << BFA_LOG_ARG2) | (BFA_LOG_D << BFA_LOG_ARG3) | 0), 4}, + +{BFA_LOG_HAL_SM_ASSERT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_ERROR, + "HAL_SM_ASSERT", + "SM Assertion failure: %s:%d: event = %d", + ((BFA_LOG_S << BFA_LOG_ARG0) | (BFA_LOG_D << BFA_LOG_ARG1) | + (BFA_LOG_D << BFA_LOG_ARG2) | 0), 3}, + + + + +/* messages define for LINUX Module */ +{BFA_LOG_LINUX_DEVICE_CLAIMED, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_DEVICE_CLAIMED", + "bfa device at %s claimed.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_HASH_INIT_FAILED, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_HASH_INIT_FAILED", + "Hash table initialization failure for the port %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_SYSFS_FAILED, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_SYSFS_FAILED", + "sysfs file creation failure for the port %s.", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_MEM_ALLOC_FAILED, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_MEM_ALLOC_FAILED", + "Memory allocation failed: %s. ", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_DRIVER_REGISTRATION_FAILED, + BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "LINUX_DRIVER_REGISTRATION_FAILED", + "%s. ", + ((BFA_LOG_S << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_ITNIM_FREE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "LINUX_ITNIM_FREE", + "scsi%d: FCID: %s WWPN: %s", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_S << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | 0), 3}, + +{BFA_LOG_LINUX_ITNIM_ONLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_ITNIM_ONLINE", + "Target: %d:0:%d FCID: %s WWPN: %s", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_D << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | (BFA_LOG_S << BFA_LOG_ARG3) | 0), 4}, + +{BFA_LOG_LINUX_ITNIM_OFFLINE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_ITNIM_OFFLINE", + "Target: %d:0:%d FCID: %s WWPN: %s", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_D << BFA_LOG_ARG1) | + (BFA_LOG_S << BFA_LOG_ARG2) | (BFA_LOG_S << BFA_LOG_ARG3) | 0), 4}, + +{BFA_LOG_LINUX_SCSI_HOST_FREE, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_SCSI_HOST_FREE", + "Free scsi%d", + ((BFA_LOG_D << BFA_LOG_ARG0) | 0), 1}, + +{BFA_LOG_LINUX_SCSI_ABORT, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, BFA_LOG_INFO, + "LINUX_SCSI_ABORT", + "scsi%d: abort cmnd %p, iotag %x", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_P << BFA_LOG_ARG1) | + (BFA_LOG_X << BFA_LOG_ARG2) | 0), 3}, + +{BFA_LOG_LINUX_SCSI_ABORT_COMP, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "LINUX_SCSI_ABORT_COMP", + "scsi%d: complete abort 0x%p, iotag 0x%x", + ((BFA_LOG_D << BFA_LOG_ARG0) | (BFA_LOG_P << BFA_LOG_ARG1) | + (BFA_LOG_X << BFA_LOG_ARG2) | 0), 3}, + + + + +/* messages define for WDRV Module */ +{BFA_LOG_WDRV_IOC_INIT_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_IOC_INIT_ERROR", + "IOC initialization has failed.", + (0), 0}, + +{BFA_LOG_WDRV_IOC_INTERNAL_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_IOC_INTERNAL_ERROR", + "IOC internal error. ", + (0), 0}, + +{BFA_LOG_WDRV_IOC_START_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_IOC_START_ERROR", + "IOC could not be started. ", + (0), 0}, + +{BFA_LOG_WDRV_IOC_STOP_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_IOC_STOP_ERROR", + "IOC could not be stopped. ", + (0), 0}, + +{BFA_LOG_WDRV_INSUFFICIENT_RESOURCES, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_INSUFFICIENT_RESOURCES", + "Insufficient memory. ", + (0), 0}, + +{BFA_LOG_WDRV_BASE_ADDRESS_MAP_ERROR, BFA_LOG_ATTR_NONE | BFA_LOG_ATTR_LOG, + BFA_LOG_INFO, "WDRV_BASE_ADDRESS_MAP_ERROR", + "Unable to map the IOC onto the system address space. ", + (0), 0}, + + +{0, 0, 0, "", "", 0, 0}, +}; diff --git a/drivers/scsi/bfa/bfa_lps.c b/drivers/scsi/bfa/bfa_lps.c new file mode 100644 index 000000000000..9844b45412b6 --- /dev/null +++ b/drivers/scsi/bfa/bfa_lps.c @@ -0,0 +1,782 @@ +/* + * 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. + */ + +#include +#include +#include + +BFA_TRC_FILE(HAL, LPS); +BFA_MODULE(lps); + +#define BFA_LPS_MIN_LPORTS (1) +#define BFA_LPS_MAX_LPORTS (256) + +/** + * forward declarations + */ +static void bfa_lps_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, + u32 *dm_len); +static void bfa_lps_attach(struct bfa_s *bfa, void *bfad, + struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, + struct bfa_pcidev_s *pcidev); +static void bfa_lps_initdone(struct bfa_s *bfa); +static void bfa_lps_detach(struct bfa_s *bfa); +static void bfa_lps_start(struct bfa_s *bfa); +static void bfa_lps_stop(struct bfa_s *bfa); +static void bfa_lps_iocdisable(struct bfa_s *bfa); +static void bfa_lps_login_rsp(struct bfa_s *bfa, + struct bfi_lps_login_rsp_s *rsp); +static void bfa_lps_logout_rsp(struct bfa_s *bfa, + struct bfi_lps_logout_rsp_s *rsp); +static void bfa_lps_reqq_resume(void *lps_arg); +static void bfa_lps_free(struct bfa_lps_s *lps); +static void bfa_lps_send_login(struct bfa_lps_s *lps); +static void bfa_lps_send_logout(struct bfa_lps_s *lps); +static void bfa_lps_login_comp(struct bfa_lps_s *lps); +static void bfa_lps_logout_comp(struct bfa_lps_s *lps); + + +/** + * lps_pvt BFA LPS private functions + */ + +enum bfa_lps_event { + BFA_LPS_SM_LOGIN = 1, /* login request from user */ + BFA_LPS_SM_LOGOUT = 2, /* logout request from user */ + BFA_LPS_SM_FWRSP = 3, /* f/w response to login/logout */ + BFA_LPS_SM_RESUME = 4, /* space present in reqq queue */ + BFA_LPS_SM_DELETE = 5, /* lps delete from user */ + BFA_LPS_SM_OFFLINE = 6, /* Link is offline */ +}; + +static void bfa_lps_sm_init(struct bfa_lps_s *lps, enum bfa_lps_event event); +static void bfa_lps_sm_login(struct bfa_lps_s *lps, enum bfa_lps_event event); +static void bfa_lps_sm_loginwait(struct bfa_lps_s *lps, + enum bfa_lps_event event); +static void bfa_lps_sm_online(struct bfa_lps_s *lps, enum bfa_lps_event event); +static void bfa_lps_sm_logout(struct bfa_lps_s *lps, enum bfa_lps_event event); +static void bfa_lps_sm_logowait(struct bfa_lps_s *lps, + enum bfa_lps_event event); + +/** + * Init state -- no login + */ +static void +bfa_lps_sm_init(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_LOGIN: + if (bfa_reqq_full(lps->bfa, lps->reqq)) { + bfa_sm_set_state(lps, bfa_lps_sm_loginwait); + bfa_reqq_wait(lps->bfa, lps->reqq, &lps->wqe); + } else { + bfa_sm_set_state(lps, bfa_lps_sm_login); + bfa_lps_send_login(lps); + } + break; + + case BFA_LPS_SM_LOGOUT: + bfa_lps_logout_comp(lps); + break; + + case BFA_LPS_SM_DELETE: + bfa_lps_free(lps); + break; + + case BFA_LPS_SM_OFFLINE: + break; + + case BFA_LPS_SM_FWRSP: + /* Could happen when fabric detects loopback and discards + * the lps request. Fw will eventually sent out the timeout + * Just ignore + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * login is in progress -- awaiting response from firmware + */ +static void +bfa_lps_sm_login(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_FWRSP: + if (lps->status == BFA_STATUS_OK) + bfa_sm_set_state(lps, bfa_lps_sm_online); + else + bfa_sm_set_state(lps, bfa_lps_sm_init); + bfa_lps_login_comp(lps); + break; + + case BFA_LPS_SM_OFFLINE: + bfa_sm_set_state(lps, bfa_lps_sm_init); + break; + + default: + bfa_assert(0); + } +} + +/** + * login pending - awaiting space in request queue + */ +static void +bfa_lps_sm_loginwait(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_RESUME: + bfa_sm_set_state(lps, bfa_lps_sm_login); + break; + + case BFA_LPS_SM_OFFLINE: + bfa_sm_set_state(lps, bfa_lps_sm_init); + bfa_reqq_wcancel(&lps->wqe); + break; + + default: + bfa_assert(0); + } +} + +/** + * login complete + */ +static void +bfa_lps_sm_online(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_LOGOUT: + if (bfa_reqq_full(lps->bfa, lps->reqq)) { + bfa_sm_set_state(lps, bfa_lps_sm_logowait); + bfa_reqq_wait(lps->bfa, lps->reqq, &lps->wqe); + } else { + bfa_sm_set_state(lps, bfa_lps_sm_logout); + bfa_lps_send_logout(lps); + } + break; + + case BFA_LPS_SM_OFFLINE: + case BFA_LPS_SM_DELETE: + bfa_sm_set_state(lps, bfa_lps_sm_init); + break; + + default: + bfa_assert(0); + } +} + +/** + * logout in progress - awaiting firmware response + */ +static void +bfa_lps_sm_logout(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_FWRSP: + bfa_sm_set_state(lps, bfa_lps_sm_init); + bfa_lps_logout_comp(lps); + break; + + case BFA_LPS_SM_OFFLINE: + bfa_sm_set_state(lps, bfa_lps_sm_init); + break; + + default: + bfa_assert(0); + } +} + +/** + * logout pending -- awaiting space in request queue + */ +static void +bfa_lps_sm_logowait(struct bfa_lps_s *lps, enum bfa_lps_event event) +{ + bfa_trc(lps->bfa, lps->lp_tag); + bfa_trc(lps->bfa, event); + + switch (event) { + case BFA_LPS_SM_RESUME: + bfa_sm_set_state(lps, bfa_lps_sm_logout); + bfa_lps_send_logout(lps); + break; + + case BFA_LPS_SM_OFFLINE: + bfa_sm_set_state(lps, bfa_lps_sm_init); + bfa_reqq_wcancel(&lps->wqe); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * lps_pvt BFA LPS private functions + */ + +/** + * return memory requirement + */ +static void +bfa_lps_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, u32 *dm_len) +{ + if (cfg->drvcfg.min_cfg) + *ndm_len += sizeof(struct bfa_lps_s) * BFA_LPS_MIN_LPORTS; + else + *ndm_len += sizeof(struct bfa_lps_s) * BFA_LPS_MAX_LPORTS; +} + +/** + * bfa module attach at initialization time + */ +static void +bfa_lps_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps; + int i; + + bfa_os_memset(mod, 0, sizeof(struct bfa_lps_mod_s)); + mod->num_lps = BFA_LPS_MAX_LPORTS; + if (cfg->drvcfg.min_cfg) + mod->num_lps = BFA_LPS_MIN_LPORTS; + else + mod->num_lps = BFA_LPS_MAX_LPORTS; + mod->lps_arr = lps = (struct bfa_lps_s *) bfa_meminfo_kva(meminfo); + + bfa_meminfo_kva(meminfo) += mod->num_lps * sizeof(struct bfa_lps_s); + + INIT_LIST_HEAD(&mod->lps_free_q); + INIT_LIST_HEAD(&mod->lps_active_q); + + for (i = 0; i < mod->num_lps; i++, lps++) { + lps->bfa = bfa; + lps->lp_tag = (u8) i; + lps->reqq = BFA_REQQ_LPS; + bfa_reqq_winit(&lps->wqe, bfa_lps_reqq_resume, lps); + list_add_tail(&lps->qe, &mod->lps_free_q); + } +} + +static void +bfa_lps_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_lps_detach(struct bfa_s *bfa) +{ +} + +static void +bfa_lps_start(struct bfa_s *bfa) +{ +} + +static void +bfa_lps_stop(struct bfa_s *bfa) +{ +} + +/** + * IOC in disabled state -- consider all lps offline + */ +static void +bfa_lps_iocdisable(struct bfa_s *bfa) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &mod->lps_active_q) { + lps = (struct bfa_lps_s *) qe; + bfa_sm_send_event(lps, BFA_LPS_SM_OFFLINE); + } +} + +/** + * Firmware login response + */ +static void +bfa_lps_login_rsp(struct bfa_s *bfa, struct bfi_lps_login_rsp_s *rsp) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps; + + bfa_assert(rsp->lp_tag < mod->num_lps); + lps = BFA_LPS_FROM_TAG(mod, rsp->lp_tag); + + lps->status = rsp->status; + switch (rsp->status) { + case BFA_STATUS_OK: + lps->fport = rsp->f_port; + lps->npiv_en = rsp->npiv_en; + lps->lp_pid = rsp->lp_pid; + lps->pr_bbcred = bfa_os_ntohs(rsp->bb_credit); + lps->pr_pwwn = rsp->port_name; + lps->pr_nwwn = rsp->node_name; + lps->auth_req = rsp->auth_req; + lps->lp_mac = rsp->lp_mac; + lps->brcd_switch = rsp->brcd_switch; + lps->fcf_mac = rsp->fcf_mac; + + break; + + case BFA_STATUS_FABRIC_RJT: + lps->lsrjt_rsn = rsp->lsrjt_rsn; + lps->lsrjt_expl = rsp->lsrjt_expl; + + break; + + case BFA_STATUS_EPROTOCOL: + lps->ext_status = rsp->ext_status; + + break; + + default: + /* Nothing to do with other status */ + break; + } + + bfa_sm_send_event(lps, BFA_LPS_SM_FWRSP); +} + +/** + * Firmware logout response + */ +static void +bfa_lps_logout_rsp(struct bfa_s *bfa, struct bfi_lps_logout_rsp_s *rsp) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps; + + bfa_assert(rsp->lp_tag < mod->num_lps); + lps = BFA_LPS_FROM_TAG(mod, rsp->lp_tag); + + bfa_sm_send_event(lps, BFA_LPS_SM_FWRSP); +} + +/** + * Space is available in request queue, resume queueing request to firmware. + */ +static void +bfa_lps_reqq_resume(void *lps_arg) +{ + struct bfa_lps_s *lps = lps_arg; + + bfa_sm_send_event(lps, BFA_LPS_SM_RESUME); +} + +/** + * lps is freed -- triggered by vport delete + */ +static void +bfa_lps_free(struct bfa_lps_s *lps) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(lps->bfa); + + list_del(&lps->qe); + list_add_tail(&lps->qe, &mod->lps_free_q); +} + +/** + * send login request to firmware + */ +static void +bfa_lps_send_login(struct bfa_lps_s *lps) +{ + struct bfi_lps_login_req_s *m; + + m = bfa_reqq_next(lps->bfa, lps->reqq); + bfa_assert(m); + + bfi_h2i_set(m->mh, BFI_MC_LPS, BFI_LPS_H2I_LOGIN_REQ, + bfa_lpuid(lps->bfa)); + + m->lp_tag = lps->lp_tag; + m->alpa = lps->alpa; + m->pdu_size = bfa_os_htons(lps->pdusz); + m->pwwn = lps->pwwn; + m->nwwn = lps->nwwn; + m->fdisc = lps->fdisc; + m->auth_en = lps->auth_en; + + bfa_reqq_produce(lps->bfa, lps->reqq); +} + +/** + * send logout request to firmware + */ +static void +bfa_lps_send_logout(struct bfa_lps_s *lps) +{ + struct bfi_lps_logout_req_s *m; + + m = bfa_reqq_next(lps->bfa, lps->reqq); + bfa_assert(m); + + bfi_h2i_set(m->mh, BFI_MC_LPS, BFI_LPS_H2I_LOGOUT_REQ, + bfa_lpuid(lps->bfa)); + + m->lp_tag = lps->lp_tag; + m->port_name = lps->pwwn; + bfa_reqq_produce(lps->bfa, lps->reqq); +} + +/** + * Indirect login completion handler for non-fcs + */ +static void +bfa_lps_login_comp_cb(void *arg, bfa_boolean_t complete) +{ + struct bfa_lps_s *lps = arg; + + if (!complete) + return; + + if (lps->fdisc) + bfa_cb_lps_fdisc_comp(lps->bfa->bfad, lps->uarg, lps->status); + else + bfa_cb_lps_flogi_comp(lps->bfa->bfad, lps->uarg, lps->status); +} + +/** + * Login completion handler -- direct call for fcs, queue for others + */ +static void +bfa_lps_login_comp(struct bfa_lps_s *lps) +{ + if (!lps->bfa->fcs) { + bfa_cb_queue(lps->bfa, &lps->hcb_qe, + bfa_lps_login_comp_cb, lps); + return; + } + + if (lps->fdisc) + bfa_cb_lps_fdisc_comp(lps->bfa->bfad, lps->uarg, lps->status); + else + bfa_cb_lps_flogi_comp(lps->bfa->bfad, lps->uarg, lps->status); +} + +/** + * Indirect logout completion handler for non-fcs + */ +static void +bfa_lps_logout_comp_cb(void *arg, bfa_boolean_t complete) +{ + struct bfa_lps_s *lps = arg; + + if (!complete) + return; + + if (lps->fdisc) + bfa_cb_lps_fdisclogo_comp(lps->bfa->bfad, lps->uarg); + else + bfa_cb_lps_flogo_comp(lps->bfa->bfad, lps->uarg); +} + +/** + * Logout completion handler -- direct call for fcs, queue for others + */ +static void +bfa_lps_logout_comp(struct bfa_lps_s *lps) +{ + if (!lps->bfa->fcs) { + bfa_cb_queue(lps->bfa, &lps->hcb_qe, + bfa_lps_logout_comp_cb, lps); + return; + } + if (lps->fdisc) + bfa_cb_lps_fdisclogo_comp(lps->bfa->bfad, lps->uarg); + else + bfa_cb_lps_flogo_comp(lps->bfa->bfad, lps->uarg); +} + + + +/** + * lps_public BFA LPS public functions + */ + +/** + * Allocate a lport srvice tag. + */ +struct bfa_lps_s * +bfa_lps_alloc(struct bfa_s *bfa) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps = NULL; + + bfa_q_deq(&mod->lps_free_q, &lps); + + if (lps == NULL) + return NULL; + + list_add_tail(&lps->qe, &mod->lps_active_q); + + bfa_sm_set_state(lps, bfa_lps_sm_init); + return lps; +} + +/** + * Free lport service tag. This can be called anytime after an alloc. + * No need to wait for any pending login/logout completions. + */ +void +bfa_lps_delete(struct bfa_lps_s *lps) +{ + bfa_sm_send_event(lps, BFA_LPS_SM_DELETE); +} + +/** + * Initiate a lport login. + */ +void +bfa_lps_flogi(struct bfa_lps_s *lps, void *uarg, u8 alpa, u16 pdusz, + wwn_t pwwn, wwn_t nwwn, bfa_boolean_t auth_en) +{ + lps->uarg = uarg; + lps->alpa = alpa; + lps->pdusz = pdusz; + lps->pwwn = pwwn; + lps->nwwn = nwwn; + lps->fdisc = BFA_FALSE; + lps->auth_en = auth_en; + bfa_sm_send_event(lps, BFA_LPS_SM_LOGIN); +} + +/** + * Initiate a lport fdisc login. + */ +void +bfa_lps_fdisc(struct bfa_lps_s *lps, void *uarg, u16 pdusz, wwn_t pwwn, + wwn_t nwwn) +{ + lps->uarg = uarg; + lps->alpa = 0; + lps->pdusz = pdusz; + lps->pwwn = pwwn; + lps->nwwn = nwwn; + lps->fdisc = BFA_TRUE; + lps->auth_en = BFA_FALSE; + bfa_sm_send_event(lps, BFA_LPS_SM_LOGIN); +} + +/** + * Initiate a lport logout (flogi). + */ +void +bfa_lps_flogo(struct bfa_lps_s *lps) +{ + bfa_sm_send_event(lps, BFA_LPS_SM_LOGOUT); +} + +/** + * Initiate a lport FDSIC logout. + */ +void +bfa_lps_fdisclogo(struct bfa_lps_s *lps) +{ + bfa_sm_send_event(lps, BFA_LPS_SM_LOGOUT); +} + +/** + * Discard a pending login request -- should be called only for + * link down handling. + */ +void +bfa_lps_discard(struct bfa_lps_s *lps) +{ + bfa_sm_send_event(lps, BFA_LPS_SM_OFFLINE); +} + +/** + * Return lport services tag + */ +u8 +bfa_lps_get_tag(struct bfa_lps_s *lps) +{ + return lps->lp_tag; +} + +/** + * Return lport services tag given the pid + */ +u8 +bfa_lps_get_tag_from_pid(struct bfa_s *bfa, u32 pid) +{ + struct bfa_lps_mod_s *mod = BFA_LPS_MOD(bfa); + struct bfa_lps_s *lps; + int i; + + for (i = 0, lps = mod->lps_arr; i < mod->num_lps; i++, lps++) { + if (lps->lp_pid == pid) + return lps->lp_tag; + } + + /* Return base port tag anyway */ + return 0; +} + +/** + * return if fabric login indicates support for NPIV + */ +bfa_boolean_t +bfa_lps_is_npiv_en(struct bfa_lps_s *lps) +{ + return lps->npiv_en; +} + +/** + * Return TRUE if attached to F-Port, else return FALSE + */ +bfa_boolean_t +bfa_lps_is_fport(struct bfa_lps_s *lps) +{ + return lps->fport; +} + +/** + * Return TRUE if attached to a Brocade Fabric + */ +bfa_boolean_t +bfa_lps_is_brcd_fabric(struct bfa_lps_s *lps) +{ + return lps->brcd_switch; +} +/** + * return TRUE if authentication is required + */ +bfa_boolean_t +bfa_lps_is_authreq(struct bfa_lps_s *lps) +{ + return lps->auth_req; +} + +bfa_eproto_status_t +bfa_lps_get_extstatus(struct bfa_lps_s *lps) +{ + return lps->ext_status; +} + +/** + * return port id assigned to the lport + */ +u32 +bfa_lps_get_pid(struct bfa_lps_s *lps) +{ + return lps->lp_pid; +} + +/** + * Return bb_credit assigned in FLOGI response + */ +u16 +bfa_lps_get_peer_bbcredit(struct bfa_lps_s *lps) +{ + return lps->pr_bbcred; +} + +/** + * Return peer port name + */ +wwn_t +bfa_lps_get_peer_pwwn(struct bfa_lps_s *lps) +{ + return lps->pr_pwwn; +} + +/** + * Return peer node name + */ +wwn_t +bfa_lps_get_peer_nwwn(struct bfa_lps_s *lps) +{ + return lps->pr_nwwn; +} + +/** + * return reason code if login request is rejected + */ +u8 +bfa_lps_get_lsrjt_rsn(struct bfa_lps_s *lps) +{ + return lps->lsrjt_rsn; +} + +/** + * return explanation code if login request is rejected + */ +u8 +bfa_lps_get_lsrjt_expl(struct bfa_lps_s *lps) +{ + return lps->lsrjt_expl; +} + + +/** + * LPS firmware message class handler. + */ +void +bfa_lps_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + union bfi_lps_i2h_msg_u msg; + + bfa_trc(bfa, m->mhdr.msg_id); + msg.msg = m; + + switch (m->mhdr.msg_id) { + case BFI_LPS_H2I_LOGIN_RSP: + bfa_lps_login_rsp(bfa, msg.login_rsp); + break; + + case BFI_LPS_H2I_LOGOUT_RSP: + bfa_lps_logout_rsp(bfa, msg.logout_rsp); + break; + + default: + bfa_trc(bfa, m->mhdr.msg_id); + bfa_assert(0); + } +} + + diff --git a/drivers/scsi/bfa/bfa_lps_priv.h b/drivers/scsi/bfa/bfa_lps_priv.h new file mode 100644 index 000000000000..d16c6ce995df --- /dev/null +++ b/drivers/scsi/bfa/bfa_lps_priv.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef __BFA_LPS_PRIV_H__ +#define __BFA_LPS_PRIV_H__ + +#include + +struct bfa_lps_mod_s { + struct list_head lps_free_q; + struct list_head lps_active_q; + struct bfa_lps_s *lps_arr; + int num_lps; +}; + +#define BFA_LPS_MOD(__bfa) (&(__bfa)->modules.lps_mod) +#define BFA_LPS_FROM_TAG(__mod, __tag) (&(__mod)->lps_arr[__tag]) + +/* + * external functions + */ +void bfa_lps_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); + +#endif /* __BFA_LPS_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_module.c b/drivers/scsi/bfa/bfa_module.c new file mode 100644 index 000000000000..32eda8e1ec65 --- /dev/null +++ b/drivers/scsi/bfa/bfa_module.c @@ -0,0 +1,90 @@ +/* + * 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. + */ +#include +#include +#include +#include + +/** + * BFA module list terminated by NULL + */ +struct bfa_module_s *hal_mods[] = { + &hal_mod_sgpg, + &hal_mod_pport, + &hal_mod_fcxp, + &hal_mod_lps, + &hal_mod_uf, + &hal_mod_rport, + &hal_mod_fcpim, +#ifdef BFA_CFG_PBIND + &hal_mod_pbind, +#endif + NULL +}; + +/** + * Message handlers for various modules. + */ +bfa_isr_func_t bfa_isrs[BFI_MC_MAX] = { + bfa_isr_unhandled, /* NONE */ + bfa_isr_unhandled, /* BFI_MC_IOC */ + bfa_isr_unhandled, /* BFI_MC_DIAG */ + bfa_isr_unhandled, /* BFI_MC_FLASH */ + bfa_isr_unhandled, /* BFI_MC_CEE */ + bfa_pport_isr, /* BFI_MC_PORT */ + bfa_isr_unhandled, /* BFI_MC_IOCFC */ + bfa_isr_unhandled, /* BFI_MC_LL */ + bfa_uf_isr, /* BFI_MC_UF */ + bfa_fcxp_isr, /* BFI_MC_FCXP */ + bfa_lps_isr, /* BFI_MC_LPS */ + bfa_rport_isr, /* BFI_MC_RPORT */ + bfa_itnim_isr, /* BFI_MC_ITNIM */ + bfa_isr_unhandled, /* BFI_MC_IOIM_READ */ + bfa_isr_unhandled, /* BFI_MC_IOIM_WRITE */ + bfa_isr_unhandled, /* BFI_MC_IOIM_IO */ + bfa_ioim_isr, /* BFI_MC_IOIM */ + bfa_ioim_good_comp_isr, /* BFI_MC_IOIM_IOCOM */ + bfa_tskim_isr, /* BFI_MC_TSKIM */ + bfa_isr_unhandled, /* BFI_MC_SBOOT */ + bfa_isr_unhandled, /* BFI_MC_IPFC */ + bfa_isr_unhandled, /* BFI_MC_PORT */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ + bfa_isr_unhandled, /* --------- */ +}; + +/** + * Message handlers for mailbox command classes + */ +bfa_ioc_mbox_mcfunc_t bfa_mbox_isrs[BFI_MC_MAX] = { + NULL, + NULL, /* BFI_MC_IOC */ + NULL, /* BFI_MC_DIAG */ + NULL, /* BFI_MC_FLASH */ + NULL, /* BFI_MC_CEE */ + NULL, /* BFI_MC_PORT */ + bfa_iocfc_isr, /* BFI_MC_IOCFC */ + NULL, +}; + diff --git a/drivers/scsi/bfa/bfa_modules_priv.h b/drivers/scsi/bfa/bfa_modules_priv.h new file mode 100644 index 000000000000..96f70534593c --- /dev/null +++ b/drivers/scsi/bfa/bfa_modules_priv.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef __BFA_MODULES_PRIV_H__ +#define __BFA_MODULES_PRIV_H__ + +#include "bfa_uf_priv.h" +#include "bfa_port_priv.h" +#include "bfa_rport_priv.h" +#include "bfa_fcxp_priv.h" +#include "bfa_lps_priv.h" +#include "bfa_fcpim_priv.h" +#include +#include + + +struct bfa_modules_s { + struct bfa_pport_s pport; /* physical port module */ + struct bfa_fcxp_mod_s fcxp_mod; /* fcxp module */ + struct bfa_lps_mod_s lps_mod; /* fcxp module */ + struct bfa_uf_mod_s uf_mod; /* unsolicited frame module */ + struct bfa_rport_mod_s rport_mod; /* remote port module */ + struct bfa_fcpim_mod_s fcpim_mod; /* FCP initiator module */ + struct bfa_sgpg_mod_s sgpg_mod; /* SG page module */ + struct bfa_cee_s cee; /* CEE Module */ + struct bfa_port_s port; /* Physical port module */ +}; + +#endif /* __BFA_MODULES_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_os_inc.h b/drivers/scsi/bfa/bfa_os_inc.h new file mode 100644 index 000000000000..10a89f75fa94 --- /dev/null +++ b/drivers/scsi/bfa/bfa_os_inc.h @@ -0,0 +1,222 @@ +/* + * 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. + */ + +/** + * Contains declarations all OS Specific files needed for BFA layer + */ + +#ifndef __BFA_OS_INC_H__ +#define __BFA_OS_INC_H__ + +#ifndef __KERNEL__ +#include +#else +#include + +#include +#include + +#include +#define SET_MODULE_VERSION(VER) + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#define BFA_ERR KERN_ERR +#define BFA_WARNING KERN_WARNING +#define BFA_NOTICE KERN_NOTICE +#define BFA_INFO KERN_INFO +#define BFA_DEBUG KERN_DEBUG + +#define LOG_BFAD_INIT 0x00000001 +#define LOG_FCP_IO 0x00000002 + +#ifdef DEBUG +#define BFA_LOG_TRACE(bfad, level, mask, fmt, arg...) \ + BFA_LOG(bfad, level, mask, fmt, ## arg) +#define BFA_DEV_TRACE(bfad, level, fmt, arg...) \ + BFA_DEV_PRINTF(bfad, level, fmt, ## arg) +#define BFA_TRACE(level, fmt, arg...) \ + BFA_PRINTF(level, fmt, ## arg) +#else +#define BFA_LOG_TRACE(bfad, level, mask, fmt, arg...) +#define BFA_DEV_TRACE(bfad, level, fmt, arg...) +#define BFA_TRACE(level, fmt, arg...) +#endif + +#define BFA_ASSERT(p) do { \ + if (!(p)) { \ + printk(KERN_ERR "assert(%s) failed at %s:%d\n", \ + #p, __FILE__, __LINE__); \ + BUG(); \ + } \ +} while (0) + + +#define BFA_LOG(bfad, level, mask, fmt, arg...) \ +do { \ + if (((mask) & (((struct bfad_s *)(bfad))-> \ + cfg_data[cfg_log_mask])) || (level[1] <= '3')) \ + dev_printk(level, &(((struct bfad_s *) \ + (bfad))->pcidev->dev), fmt, ##arg); \ +} while (0) + +#ifndef BFA_DEV_PRINTF +#define BFA_DEV_PRINTF(bfad, level, fmt, arg...) \ + dev_printk(level, &(((struct bfad_s *) \ + (bfad))->pcidev->dev), fmt, ##arg); +#endif + +#define BFA_PRINTF(level, fmt, arg...) \ + printk(level fmt, ##arg); + +int bfa_os_MWB(void *); + +#define bfa_os_mmiowb() mmiowb() + +#define bfa_swap_3b(_x) \ + ((((_x) & 0xff) << 16) | \ + ((_x) & 0x00ff00) | \ + (((_x) & 0xff0000) >> 16)) + +#define bfa_swap_8b(_x) \ + ((((_x) & 0xff00000000000000ull) >> 56) \ + | (((_x) & 0x00ff000000000000ull) >> 40) \ + | (((_x) & 0x0000ff0000000000ull) >> 24) \ + | (((_x) & 0x000000ff00000000ull) >> 8) \ + | (((_x) & 0x00000000ff000000ull) << 8) \ + | (((_x) & 0x0000000000ff0000ull) << 24) \ + | (((_x) & 0x000000000000ff00ull) << 40) \ + | (((_x) & 0x00000000000000ffull) << 56)) + +#define bfa_os_swap32(_x) \ + ((((_x) & 0xff) << 24) | \ + (((_x) & 0x0000ff00) << 8) | \ + (((_x) & 0x00ff0000) >> 8) | \ + (((_x) & 0xff000000) >> 24)) + + +#ifndef __BIGENDIAN +#define bfa_os_htons(_x) ((u16)((((_x) & 0xff00) >> 8) | \ + (((_x) & 0x00ff) << 8))) + +#define bfa_os_htonl(_x) bfa_os_swap32(_x) +#define bfa_os_htonll(_x) bfa_swap_8b(_x) +#define bfa_os_hton3b(_x) bfa_swap_3b(_x) + +#define bfa_os_wtole(_x) (_x) + +#else + +#define bfa_os_htons(_x) (_x) +#define bfa_os_htonl(_x) (_x) +#define bfa_os_hton3b(_x) (_x) +#define bfa_os_htonll(_x) (_x) +#define bfa_os_wtole(_x) bfa_os_swap32(_x) + +#endif + +#define bfa_os_ntohs(_x) bfa_os_htons(_x) +#define bfa_os_ntohl(_x) bfa_os_htonl(_x) +#define bfa_os_ntohll(_x) bfa_os_htonll(_x) +#define bfa_os_ntoh3b(_x) bfa_os_hton3b(_x) + +#define bfa_os_u32(__pa64) ((__pa64) >> 32) + +#define bfa_os_memset memset +#define bfa_os_memcpy memcpy +#define bfa_os_udelay udelay +#define bfa_os_vsprintf vsprintf + +#define bfa_os_assign(__t, __s) __t = __s + +#define bfa_os_addr_t char __iomem * +#define bfa_os_panic() + +#define bfa_os_reg_read(_raddr) bfa_os_wtole(readl(_raddr)) +#define bfa_os_reg_write(_raddr, _val) writel(bfa_os_wtole((_val)), (_raddr)) +#define bfa_os_mem_read(_raddr, _off) \ + bfa_os_ntohl(readl(((_raddr) + (_off)))) +#define bfa_os_mem_write(_raddr, _off, _val) \ + writel(bfa_os_htonl((_val)), ((_raddr) + (_off))) + +#define BFA_TRC_TS(_trcm) \ + ({ \ + struct timeval tv; \ + \ + do_gettimeofday(&tv); \ + (tv.tv_sec*1000000+tv.tv_usec); \ + }) + +struct bfa_log_mod_s; +void bfa_os_printf(struct bfa_log_mod_s *log_mod, u32 msg_id, + const char *fmt, ...); +#endif + +#define boolean_t int + +/** + * For current time stamp, OS API will fill-in + */ +struct bfa_timeval_s { + u32 tv_sec; /* seconds */ + u32 tv_usec; /* microseconds */ +}; + +void bfa_os_gettimeofday(struct bfa_timeval_s *tv); + +static inline void +wwn2str(char *wwn_str, u64 wwn) +{ + union { + u64 wwn; + u8 byte[8]; + } w; + + w.wwn = wwn; + sprintf(wwn_str, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", w.byte[0], + w.byte[1], w.byte[2], w.byte[3], w.byte[4], w.byte[5], + w.byte[6], w.byte[7]); +} + +static inline void +fcid2str(char *fcid_str, u32 fcid) +{ + union { + u32 fcid; + u8 byte[4]; + } f; + + f.fcid = fcid; + sprintf(fcid_str, "%02x:%02x:%02x", f.byte[1], f.byte[2], f.byte[3]); +} + +#endif /* __BFA_OS_INC_H__ */ diff --git a/drivers/scsi/bfa/bfa_port.c b/drivers/scsi/bfa/bfa_port.c new file mode 100644 index 000000000000..cab19028361a --- /dev/null +++ b/drivers/scsi/bfa/bfa_port.c @@ -0,0 +1,460 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +BFA_TRC_FILE(CNA, PORT); + +#define bfa_ioc_portid(__ioc) ((__ioc)->port_id) +#define bfa_lpuid(__arg) bfa_ioc_portid(&(__arg)->ioc) + +static void +bfa_port_stats_swap(struct bfa_port_s *port, union bfa_pport_stats_u *stats) +{ + u32 *dip = (u32 *) stats; + u32 t0, t1; + int i; + + for (i = 0; i < sizeof(union bfa_pport_stats_u) / sizeof(u32); + i += 2) { + t0 = dip[i]; + t1 = dip[i + 1]; +#ifdef __BIGENDIAN + dip[i] = bfa_os_ntohl(t0); + dip[i + 1] = bfa_os_ntohl(t1); +#else + dip[i] = bfa_os_ntohl(t1); + dip[i + 1] = bfa_os_ntohl(t0); +#endif + } + + /** todo + * QoS stats r also swapped as 64bit; that structure also + * has to use 64 bit counters + */ +} + +/** + * bfa_port_enable_isr() + * + * + * @param[in] port - Pointer to the port module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_port_enable_isr(struct bfa_port_s *port, bfa_status_t status) +{ + bfa_assert(0); +} + +/** + * bfa_port_disable_isr() + * + * + * @param[in] port - Pointer to the port module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_port_disable_isr(struct bfa_port_s *port, bfa_status_t status) +{ + bfa_assert(0); +} + +/** + * bfa_port_get_stats_isr() + * + * + * @param[in] port - Pointer to the Port module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_port_get_stats_isr(struct bfa_port_s *port, bfa_status_t status) +{ + port->stats_status = status; + port->stats_busy = BFA_FALSE; + + if (status == BFA_STATUS_OK) { + memcpy(port->stats, port->stats_dma.kva, + sizeof(union bfa_pport_stats_u)); + bfa_port_stats_swap(port, port->stats); + } + + if (port->stats_cbfn) { + port->stats_cbfn(port->stats_cbarg, status); + port->stats_cbfn = NULL; + } +} + +/** + * bfa_port_clear_stats_isr() + * + * + * @param[in] port - Pointer to the Port module + * status - Return status from the f/w + * + * @return void + */ +static void +bfa_port_clear_stats_isr(struct bfa_port_s *port, bfa_status_t status) +{ + port->stats_status = status; + port->stats_busy = BFA_FALSE; + + if (port->stats_cbfn) { + port->stats_cbfn(port->stats_cbarg, status); + port->stats_cbfn = NULL; + } +} + +/** + * bfa_port_isr() + * + * + * @param[in] Pointer to the Port module data structure. + * + * @return void + */ +static void +bfa_port_isr(void *cbarg, struct bfi_mbmsg_s *m) +{ + struct bfa_port_s *port = (struct bfa_port_s *)cbarg; + union bfi_port_i2h_msg_u *i2hmsg; + + i2hmsg = (union bfi_port_i2h_msg_u *)m; + bfa_trc(port, m->mh.msg_id); + + switch (m->mh.msg_id) { + case BFI_PORT_I2H_ENABLE_RSP: + if (port->endis_pending == BFA_FALSE) + break; + bfa_port_enable_isr(port, i2hmsg->enable_rsp.status); + break; + + case BFI_PORT_I2H_DISABLE_RSP: + if (port->endis_pending == BFA_FALSE) + break; + bfa_port_disable_isr(port, i2hmsg->disable_rsp.status); + break; + + case BFI_PORT_I2H_GET_STATS_RSP: + /* + * Stats busy flag is still set? (may be cmd timed out) + */ + if (port->stats_busy == BFA_FALSE) + break; + bfa_port_get_stats_isr(port, i2hmsg->getstats_rsp.status); + break; + + case BFI_PORT_I2H_CLEAR_STATS_RSP: + if (port->stats_busy == BFA_FALSE) + break; + bfa_port_clear_stats_isr(port, i2hmsg->clearstats_rsp.status); + break; + + default: + bfa_assert(0); + } +} + +/** + * bfa_port_meminfo() + * + * + * @param[in] void + * + * @return Size of DMA region + */ +u32 +bfa_port_meminfo(void) +{ + return BFA_ROUNDUP(sizeof(union bfa_pport_stats_u), BFA_DMA_ALIGN_SZ); +} + +/** + * bfa_port_mem_claim() + * + * + * @param[in] port Port module pointer + * dma_kva Kernel Virtual Address of Port DMA Memory + * dma_pa Physical Address of Port DMA Memory + * + * @return void + */ +void +bfa_port_mem_claim(struct bfa_port_s *port, u8 *dma_kva, u64 dma_pa) +{ + port->stats_dma.kva = dma_kva; + port->stats_dma.pa = dma_pa; +} + +/** + * bfa_port_enable() + * + * Send the Port enable request to the f/w + * + * @param[in] Pointer to the Port module data structure. + * + * @return Status + */ +bfa_status_t +bfa_port_enable(struct bfa_port_s *port, bfa_port_endis_cbfn_t cbfn, + void *cbarg) +{ + struct bfi_port_generic_req_s *m; + + /** todo Not implemented */ + bfa_assert(0); + + if (!bfa_ioc_is_operational(port->ioc)) { + bfa_trc(port, BFA_STATUS_IOC_FAILURE); + return BFA_STATUS_IOC_FAILURE; + } + + if (port->endis_pending) { + bfa_trc(port, BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; + } + + m = (struct bfi_port_generic_req_s *)port->endis_mb.msg; + + port->msgtag++; + port->endis_cbfn = cbfn; + port->endis_cbarg = cbarg; + port->endis_pending = BFA_TRUE; + + bfi_h2i_set(m->mh, BFI_MC_PORT, BFI_PORT_H2I_ENABLE_REQ, + bfa_ioc_portid(port->ioc)); + bfa_ioc_mbox_queue(port->ioc, &port->endis_mb); + + return BFA_STATUS_OK; +} + +/** + * bfa_port_disable() + * + * Send the Port disable request to the f/w + * + * @param[in] Pointer to the Port module data structure. + * + * @return Status + */ +bfa_status_t +bfa_port_disable(struct bfa_port_s *port, bfa_port_endis_cbfn_t cbfn, + void *cbarg) +{ + struct bfi_port_generic_req_s *m; + + /** todo Not implemented */ + bfa_assert(0); + + if (!bfa_ioc_is_operational(port->ioc)) { + bfa_trc(port, BFA_STATUS_IOC_FAILURE); + return BFA_STATUS_IOC_FAILURE; + } + + if (port->endis_pending) { + bfa_trc(port, BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; + } + + m = (struct bfi_port_generic_req_s *)port->endis_mb.msg; + + port->msgtag++; + port->endis_cbfn = cbfn; + port->endis_cbarg = cbarg; + port->endis_pending = BFA_TRUE; + + bfi_h2i_set(m->mh, BFI_MC_PORT, BFI_PORT_H2I_DISABLE_REQ, + bfa_ioc_portid(port->ioc)); + bfa_ioc_mbox_queue(port->ioc, &port->endis_mb); + + return BFA_STATUS_OK; +} + +/** + * bfa_port_get_stats() + * + * Send the request to the f/w to fetch Port statistics. + * + * @param[in] Pointer to the Port module data structure. + * + * @return Status + */ +bfa_status_t +bfa_port_get_stats(struct bfa_port_s *port, union bfa_pport_stats_u *stats, + bfa_port_stats_cbfn_t cbfn, void *cbarg) +{ + struct bfi_port_get_stats_req_s *m; + + if (!bfa_ioc_is_operational(port->ioc)) { + bfa_trc(port, BFA_STATUS_IOC_FAILURE); + return BFA_STATUS_IOC_FAILURE; + } + + if (port->stats_busy) { + bfa_trc(port, BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; + } + + m = (struct bfi_port_get_stats_req_s *)port->stats_mb.msg; + + port->stats = stats; + port->stats_cbfn = cbfn; + port->stats_cbarg = cbarg; + port->stats_busy = BFA_TRUE; + bfa_dma_be_addr_set(m->dma_addr, port->stats_dma.pa); + + bfi_h2i_set(m->mh, BFI_MC_PORT, BFI_PORT_H2I_GET_STATS_REQ, + bfa_ioc_portid(port->ioc)); + bfa_ioc_mbox_queue(port->ioc, &port->stats_mb); + + return BFA_STATUS_OK; +} + +/** + * bfa_port_clear_stats() + * + * + * @param[in] Pointer to the Port module data structure. + * + * @return Status + */ +bfa_status_t +bfa_port_clear_stats(struct bfa_port_s *port, bfa_port_stats_cbfn_t cbfn, + void *cbarg) +{ + struct bfi_port_generic_req_s *m; + + if (!bfa_ioc_is_operational(port->ioc)) { + bfa_trc(port, BFA_STATUS_IOC_FAILURE); + return BFA_STATUS_IOC_FAILURE; + } + + if (port->stats_busy) { + bfa_trc(port, BFA_STATUS_DEVBUSY); + return BFA_STATUS_DEVBUSY; + } + + m = (struct bfi_port_generic_req_s *)port->stats_mb.msg; + + port->stats_cbfn = cbfn; + port->stats_cbarg = cbarg; + port->stats_busy = BFA_TRUE; + + bfi_h2i_set(m->mh, BFI_MC_PORT, BFI_PORT_H2I_CLEAR_STATS_REQ, + bfa_ioc_portid(port->ioc)); + bfa_ioc_mbox_queue(port->ioc, &port->stats_mb); + + return BFA_STATUS_OK; +} + +/** + * bfa_port_hbfail() + * + * + * @param[in] Pointer to the Port module data structure. + * + * @return void + */ +void +bfa_port_hbfail(void *arg) +{ + struct bfa_port_s *port = (struct bfa_port_s *)arg; + + /* + * Fail any pending get_stats/clear_stats requests + */ + if (port->stats_busy) { + if (port->stats_cbfn) + port->stats_cbfn(port->dev, BFA_STATUS_FAILED); + port->stats_cbfn = NULL; + port->stats_busy = BFA_FALSE; + } + + /* + * Clear any enable/disable is pending + */ + if (port->endis_pending) { + if (port->endis_cbfn) + port->endis_cbfn(port->dev, BFA_STATUS_FAILED); + port->endis_cbfn = NULL; + port->endis_pending = BFA_FALSE; + } +} + +/** + * bfa_port_attach() + * + * + * @param[in] port - Pointer to the Port module data structure + * ioc - Pointer to the ioc module data structure + * dev - Pointer to the device driver module data structure + * The device driver specific mbox ISR functions have + * this pointer as one of the parameters. + * trcmod - + * logmod - + * + * @return void + */ +void +bfa_port_attach(struct bfa_port_s *port, struct bfa_ioc_s *ioc, void *dev, + struct bfa_trc_mod_s *trcmod, struct bfa_log_mod_s *logmod) +{ + bfa_assert(port); + + port->dev = dev; + port->ioc = ioc; + port->trcmod = trcmod; + port->logmod = logmod; + + port->stats_busy = port->endis_pending = BFA_FALSE; + port->stats_cbfn = port->endis_cbfn = NULL; + + bfa_ioc_mbox_regisr(port->ioc, BFI_MC_PORT, bfa_port_isr, port); + bfa_ioc_hbfail_init(&port->hbfail, bfa_port_hbfail, port); + bfa_ioc_hbfail_register(port->ioc, &port->hbfail); + + bfa_trc(port, 0); +} + +/** + * bfa_port_detach() + * + * + * @param[in] port - Pointer to the Port module data structure + * + * @return void + */ +void +bfa_port_detach(struct bfa_port_s *port) +{ + bfa_trc(port, 0); +} diff --git a/drivers/scsi/bfa/bfa_port_priv.h b/drivers/scsi/bfa/bfa_port_priv.h new file mode 100644 index 000000000000..4b97e2759908 --- /dev/null +++ b/drivers/scsi/bfa/bfa_port_priv.h @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#ifndef __BFA_PORT_PRIV_H__ +#define __BFA_PORT_PRIV_H__ + +#include +#include +#include "bfa_intr_priv.h" + +/** + * BFA physical port data structure + */ +struct bfa_pport_s { + struct bfa_s *bfa; /* parent BFA instance */ + bfa_sm_t sm; /* port state machine */ + wwn_t nwwn; /* node wwn of physical port */ + wwn_t pwwn; /* port wwn of physical oprt */ + enum bfa_pport_speed speed_sup; + /* supported speeds */ + enum bfa_pport_speed speed; /* current speed */ + enum bfa_pport_topology topology; /* current topology */ + u8 myalpa; /* my ALPA in LOOP topology */ + u8 rsvd[3]; + struct bfa_pport_cfg_s cfg; /* current port configuration */ + struct bfa_qos_attr_s qos_attr; /* QoS Attributes */ + struct bfa_qos_vc_attr_s qos_vc_attr; /* VC info from ELP */ + struct bfa_reqq_wait_s reqq_wait; + /* to wait for room in reqq */ + struct bfa_reqq_wait_s svcreq_wait; + /* to wait for room in reqq */ + struct bfa_reqq_wait_s stats_reqq_wait; + /* to wait for room in reqq (stats) */ + void *event_cbarg; + void (*event_cbfn) (void *cbarg, + bfa_pport_event_t event); + union { + union bfi_pport_i2h_msg_u i2hmsg; + } event_arg; + void *bfad; /* BFA driver handle */ + struct bfa_cb_qe_s hcb_qe; /* BFA callback queue elem */ + enum bfa_pport_linkstate hcb_event; + /* link event for callback */ + u32 msgtag; /* fimrware msg tag for reply */ + u8 *stats_kva; + u64 stats_pa; + union bfa_pport_stats_u *stats; /* pport stats */ + u32 mypid : 24; + u32 rsvd_b : 8; + struct bfa_timer_s timer; /* timer */ + union bfa_pport_stats_u *stats_ret; + /* driver stats location */ + bfa_status_t stats_status; + /* stats/statsclr status */ + bfa_boolean_t stats_busy; + /* outstanding stats/statsclr */ + bfa_boolean_t stats_qfull; + bfa_boolean_t diag_busy; + /* diag busy status */ + bfa_boolean_t beacon; + /* port beacon status */ + bfa_boolean_t link_e2e_beacon; + /* link beacon status */ + bfa_cb_pport_t stats_cbfn; + /* driver callback function */ + void *stats_cbarg; + /* *!< user callback arg */ +}; + +#define BFA_PORT_MOD(__bfa) (&(__bfa)->modules.pport) + +/* + * public functions + */ +void bfa_pport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +#endif /* __BFA_PORT_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_priv.h b/drivers/scsi/bfa/bfa_priv.h new file mode 100644 index 000000000000..0747a6b26f7b --- /dev/null +++ b/drivers/scsi/bfa/bfa_priv.h @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#ifndef __BFA_PRIV_H__ +#define __BFA_PRIV_H__ + +#include "bfa_iocfc.h" +#include "bfa_intr_priv.h" +#include "bfa_trcmod_priv.h" +#include "bfa_modules_priv.h" +#include "bfa_fwimg_priv.h" +#include +#include + +/** + * Macro to define a new BFA module + */ +#define BFA_MODULE(__mod) \ + static void bfa_ ## __mod ## _meminfo( \ + struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, \ + u32 *dm_len); \ + static void bfa_ ## __mod ## _attach(struct bfa_s *bfa, \ + void *bfad, struct bfa_iocfc_cfg_s *cfg, \ + struct bfa_meminfo_s *meminfo, \ + struct bfa_pcidev_s *pcidev); \ + static void bfa_ ## __mod ## _initdone(struct bfa_s *bfa); \ + static void bfa_ ## __mod ## _detach(struct bfa_s *bfa); \ + static void bfa_ ## __mod ## _start(struct bfa_s *bfa); \ + static void bfa_ ## __mod ## _stop(struct bfa_s *bfa); \ + static void bfa_ ## __mod ## _iocdisable(struct bfa_s *bfa); \ + \ + extern struct bfa_module_s hal_mod_ ## __mod; \ + struct bfa_module_s hal_mod_ ## __mod = { \ + bfa_ ## __mod ## _meminfo, \ + bfa_ ## __mod ## _attach, \ + bfa_ ## __mod ## _initdone, \ + bfa_ ## __mod ## _detach, \ + bfa_ ## __mod ## _start, \ + bfa_ ## __mod ## _stop, \ + bfa_ ## __mod ## _iocdisable, \ + } + +#define BFA_CACHELINE_SZ (256) + +/** + * Structure used to interact between different BFA sub modules + * + * Each sub module needs to implement only the entry points relevant to it (and + * can leave entry points as NULL) + */ +struct bfa_module_s { + void (*meminfo) (struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len); + void (*attach) (struct bfa_s *bfa, void *bfad, + struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, + struct bfa_pcidev_s *pcidev); + void (*initdone) (struct bfa_s *bfa); + void (*detach) (struct bfa_s *bfa); + void (*start) (struct bfa_s *bfa); + void (*stop) (struct bfa_s *bfa); + void (*iocdisable) (struct bfa_s *bfa); +}; + +extern struct bfa_module_s *hal_mods[]; + +struct bfa_s { + void *bfad; /* BFA driver instance */ + struct bfa_aen_s *aen; /* AEN module */ + struct bfa_plog_s *plog; /* portlog buffer */ + struct bfa_log_mod_s *logm; /* driver logging modulen */ + struct bfa_trc_mod_s *trcmod; /* driver tracing */ + struct bfa_ioc_s ioc; /* IOC module */ + struct bfa_iocfc_s iocfc; /* IOCFC module */ + struct bfa_timer_mod_s timer_mod; /* timer module */ + struct bfa_modules_s modules; /* BFA modules */ + struct list_head comp_q; /* pending completions */ + bfa_boolean_t rme_process; /* RME processing enabled */ + struct list_head reqq_waitq[BFI_IOC_MAX_CQS]; + bfa_boolean_t fcs; /* FCS is attached to BFA */ + struct bfa_msix_s msix; +}; + +extern bfa_isr_func_t bfa_isrs[BFI_MC_MAX]; +extern bfa_ioc_mbox_mcfunc_t bfa_mbox_isrs[]; +extern bfa_boolean_t bfa_auto_recover; +extern struct bfa_module_s hal_mod_flash; +extern struct bfa_module_s hal_mod_fcdiag; +extern struct bfa_module_s hal_mod_sgpg; +extern struct bfa_module_s hal_mod_pport; +extern struct bfa_module_s hal_mod_fcxp; +extern struct bfa_module_s hal_mod_lps; +extern struct bfa_module_s hal_mod_uf; +extern struct bfa_module_s hal_mod_rport; +extern struct bfa_module_s hal_mod_fcpim; +extern struct bfa_module_s hal_mod_pbind; + +#endif /* __BFA_PRIV_H__ */ + diff --git a/drivers/scsi/bfa/bfa_rport.c b/drivers/scsi/bfa/bfa_rport.c new file mode 100644 index 000000000000..16da77a8db28 --- /dev/null +++ b/drivers/scsi/bfa/bfa_rport.c @@ -0,0 +1,911 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include "bfa_intr_priv.h" + +BFA_TRC_FILE(HAL, RPORT); +BFA_MODULE(rport); + +#define bfa_rport_offline_cb(__rp) do { \ + if ((__rp)->bfa->fcs) \ + bfa_cb_rport_offline((__rp)->rport_drv); \ + else { \ + bfa_cb_queue((__rp)->bfa, &(__rp)->hcb_qe, \ + __bfa_cb_rport_offline, (__rp)); \ + } \ +} while (0) + +#define bfa_rport_online_cb(__rp) do { \ + if ((__rp)->bfa->fcs) \ + bfa_cb_rport_online((__rp)->rport_drv); \ + else { \ + bfa_cb_queue((__rp)->bfa, &(__rp)->hcb_qe, \ + __bfa_cb_rport_online, (__rp)); \ + } \ +} while (0) + +/* + * forward declarations + */ +static struct bfa_rport_s *bfa_rport_alloc(struct bfa_rport_mod_s *rp_mod); +static void bfa_rport_free(struct bfa_rport_s *rport); +static bfa_boolean_t bfa_rport_send_fwcreate(struct bfa_rport_s *rp); +static bfa_boolean_t bfa_rport_send_fwdelete(struct bfa_rport_s *rp); +static bfa_boolean_t bfa_rport_send_fwspeed(struct bfa_rport_s *rp); +static void __bfa_cb_rport_online(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_rport_offline(void *cbarg, bfa_boolean_t complete); + +/** + * bfa_rport_sm BFA rport state machine + */ + + +enum bfa_rport_event { + BFA_RPORT_SM_CREATE = 1, /* rport create event */ + BFA_RPORT_SM_DELETE = 2, /* deleting an existing rport */ + BFA_RPORT_SM_ONLINE = 3, /* rport is online */ + BFA_RPORT_SM_OFFLINE = 4, /* rport is offline */ + BFA_RPORT_SM_FWRSP = 5, /* firmware response */ + BFA_RPORT_SM_HWFAIL = 6, /* IOC h/w failure */ + BFA_RPORT_SM_QOS_SCN = 7, /* QoS SCN from firmware */ + BFA_RPORT_SM_SET_SPEED = 8, /* Set Rport Speed */ + BFA_RPORT_SM_QRESUME = 9, /* space in requeue queue */ +}; + +static void bfa_rport_sm_uninit(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_created(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_fwcreate(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_online(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_fwdelete(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_offline(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_deleting(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_offline_pending(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_delete_pending(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_iocdisable(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_fwcreate_qfull(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_fwdelete_qfull(struct bfa_rport_s *rp, + enum bfa_rport_event event); +static void bfa_rport_sm_deleting_qfull(struct bfa_rport_s *rp, + enum bfa_rport_event event); + +/** + * Beginning state, only online event expected. + */ +static void +bfa_rport_sm_uninit(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_CREATE: + bfa_stats(rp, sm_un_cr); + bfa_sm_set_state(rp, bfa_rport_sm_created); + break; + + default: + bfa_stats(rp, sm_un_unexp); + bfa_assert(0); + } +} + +static void +bfa_rport_sm_created(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_ONLINE: + bfa_stats(rp, sm_cr_on); + if (bfa_rport_send_fwcreate(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate); + else + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate_qfull); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_cr_del); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_cr_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + break; + + default: + bfa_stats(rp, sm_cr_unexp); + bfa_assert(0); + } +} + +/** + * Waiting for rport create response from firmware. + */ +static void +bfa_rport_sm_fwcreate(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_FWRSP: + bfa_stats(rp, sm_fwc_rsp); + bfa_sm_set_state(rp, bfa_rport_sm_online); + bfa_rport_online_cb(rp); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_fwc_del); + bfa_sm_set_state(rp, bfa_rport_sm_delete_pending); + break; + + case BFA_RPORT_SM_OFFLINE: + bfa_stats(rp, sm_fwc_off); + bfa_sm_set_state(rp, bfa_rport_sm_offline_pending); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_fwc_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + break; + + default: + bfa_stats(rp, sm_fwc_unexp); + bfa_assert(0); + } +} + +/** + * Request queue is full, awaiting queue resume to send create request. + */ +static void +bfa_rport_sm_fwcreate_qfull(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_QRESUME: + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate); + bfa_rport_send_fwcreate(rp); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_fwc_del); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_reqq_wcancel(&rp->reqq_wait); + bfa_rport_free(rp); + break; + + case BFA_RPORT_SM_OFFLINE: + bfa_stats(rp, sm_fwc_off); + bfa_sm_set_state(rp, bfa_rport_sm_offline); + bfa_reqq_wcancel(&rp->reqq_wait); + bfa_rport_offline_cb(rp); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_fwc_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + bfa_reqq_wcancel(&rp->reqq_wait); + break; + + default: + bfa_stats(rp, sm_fwc_unexp); + bfa_assert(0); + } +} + +/** + * Online state - normal parking state. + */ +static void +bfa_rport_sm_online(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + struct bfi_rport_qos_scn_s *qos_scn; + + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_OFFLINE: + bfa_stats(rp, sm_on_off); + if (bfa_rport_send_fwdelete(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_fwdelete); + else + bfa_sm_set_state(rp, bfa_rport_sm_fwdelete_qfull); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_on_del); + if (bfa_rport_send_fwdelete(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_deleting); + else + bfa_sm_set_state(rp, bfa_rport_sm_deleting_qfull); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_on_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + break; + + case BFA_RPORT_SM_SET_SPEED: + bfa_rport_send_fwspeed(rp); + break; + + case BFA_RPORT_SM_QOS_SCN: + qos_scn = (struct bfi_rport_qos_scn_s *) rp->event_arg.fw_msg; + rp->qos_attr = qos_scn->new_qos_attr; + bfa_trc(rp->bfa, qos_scn->old_qos_attr.qos_flow_id); + bfa_trc(rp->bfa, qos_scn->new_qos_attr.qos_flow_id); + bfa_trc(rp->bfa, qos_scn->old_qos_attr.qos_priority); + bfa_trc(rp->bfa, qos_scn->new_qos_attr.qos_priority); + + qos_scn->old_qos_attr.qos_flow_id = + bfa_os_ntohl(qos_scn->old_qos_attr.qos_flow_id); + qos_scn->new_qos_attr.qos_flow_id = + bfa_os_ntohl(qos_scn->new_qos_attr.qos_flow_id); + qos_scn->old_qos_attr.qos_priority = + bfa_os_ntohl(qos_scn->old_qos_attr.qos_priority); + qos_scn->new_qos_attr.qos_priority = + bfa_os_ntohl(qos_scn->new_qos_attr.qos_priority); + + if (qos_scn->old_qos_attr.qos_flow_id != + qos_scn->new_qos_attr.qos_flow_id) + bfa_cb_rport_qos_scn_flowid(rp->rport_drv, + qos_scn->old_qos_attr, + qos_scn->new_qos_attr); + if (qos_scn->old_qos_attr.qos_priority != + qos_scn->new_qos_attr.qos_priority) + bfa_cb_rport_qos_scn_prio(rp->rport_drv, + qos_scn->old_qos_attr, + qos_scn->new_qos_attr); + break; + + default: + bfa_stats(rp, sm_on_unexp); + bfa_assert(0); + } +} + +/** + * Firmware rport is being deleted - awaiting f/w response. + */ +static void +bfa_rport_sm_fwdelete(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_FWRSP: + bfa_stats(rp, sm_fwd_rsp); + bfa_sm_set_state(rp, bfa_rport_sm_offline); + bfa_rport_offline_cb(rp); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_fwd_del); + bfa_sm_set_state(rp, bfa_rport_sm_deleting); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_fwd_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + bfa_rport_offline_cb(rp); + break; + + default: + bfa_stats(rp, sm_fwd_unexp); + bfa_assert(0); + } +} + +static void +bfa_rport_sm_fwdelete_qfull(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_QRESUME: + bfa_sm_set_state(rp, bfa_rport_sm_fwdelete); + bfa_rport_send_fwdelete(rp); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_fwd_del); + bfa_sm_set_state(rp, bfa_rport_sm_deleting_qfull); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_fwd_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + bfa_reqq_wcancel(&rp->reqq_wait); + bfa_rport_offline_cb(rp); + break; + + default: + bfa_stats(rp, sm_fwd_unexp); + bfa_assert(0); + } +} + +/** + * Offline state. + */ +static void +bfa_rport_sm_offline(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_off_del); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + case BFA_RPORT_SM_ONLINE: + bfa_stats(rp, sm_off_on); + if (bfa_rport_send_fwcreate(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate); + else + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate_qfull); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_off_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + break; + + default: + bfa_stats(rp, sm_off_unexp); + bfa_assert(0); + } +} + +/** + * Rport is deleted, waiting for firmware response to delete. + */ +static void +bfa_rport_sm_deleting(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_FWRSP: + bfa_stats(rp, sm_del_fwrsp); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_del_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_rport_sm_deleting_qfull(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_QRESUME: + bfa_stats(rp, sm_del_fwrsp); + bfa_sm_set_state(rp, bfa_rport_sm_deleting); + bfa_rport_send_fwdelete(rp); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_del_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_reqq_wcancel(&rp->reqq_wait); + bfa_rport_free(rp); + break; + + default: + bfa_assert(0); + } +} + +/** + * Waiting for rport create response from firmware. A delete is pending. + */ +static void +bfa_rport_sm_delete_pending(struct bfa_rport_s *rp, + enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_FWRSP: + bfa_stats(rp, sm_delp_fwrsp); + if (bfa_rport_send_fwdelete(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_deleting); + else + bfa_sm_set_state(rp, bfa_rport_sm_deleting_qfull); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_delp_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + default: + bfa_stats(rp, sm_delp_unexp); + bfa_assert(0); + } +} + +/** + * Waiting for rport create response from firmware. Rport offline is pending. + */ +static void +bfa_rport_sm_offline_pending(struct bfa_rport_s *rp, + enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_FWRSP: + bfa_stats(rp, sm_offp_fwrsp); + if (bfa_rport_send_fwdelete(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_fwdelete); + else + bfa_sm_set_state(rp, bfa_rport_sm_fwdelete_qfull); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_offp_del); + bfa_sm_set_state(rp, bfa_rport_sm_delete_pending); + break; + + case BFA_RPORT_SM_HWFAIL: + bfa_stats(rp, sm_offp_hwf); + bfa_sm_set_state(rp, bfa_rport_sm_iocdisable); + break; + + default: + bfa_stats(rp, sm_offp_unexp); + bfa_assert(0); + } +} + +/** + * IOC h/w failed. + */ +static void +bfa_rport_sm_iocdisable(struct bfa_rport_s *rp, enum bfa_rport_event event) +{ + bfa_trc(rp->bfa, rp->rport_tag); + bfa_trc(rp->bfa, event); + + switch (event) { + case BFA_RPORT_SM_OFFLINE: + bfa_stats(rp, sm_iocd_off); + bfa_rport_offline_cb(rp); + break; + + case BFA_RPORT_SM_DELETE: + bfa_stats(rp, sm_iocd_del); + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + bfa_rport_free(rp); + break; + + case BFA_RPORT_SM_ONLINE: + bfa_stats(rp, sm_iocd_on); + if (bfa_rport_send_fwcreate(rp)) + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate); + else + bfa_sm_set_state(rp, bfa_rport_sm_fwcreate_qfull); + break; + + case BFA_RPORT_SM_HWFAIL: + break; + + default: + bfa_stats(rp, sm_iocd_unexp); + bfa_assert(0); + } +} + + + +/** + * bfa_rport_private BFA rport private functions + */ + +static void +__bfa_cb_rport_online(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_rport_s *rp = cbarg; + + if (complete) + bfa_cb_rport_online(rp->rport_drv); +} + +static void +__bfa_cb_rport_offline(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_rport_s *rp = cbarg; + + if (complete) + bfa_cb_rport_offline(rp->rport_drv); +} + +static void +bfa_rport_qresume(void *cbarg) +{ + struct bfa_rport_s *rp = cbarg; + + bfa_sm_send_event(rp, BFA_RPORT_SM_QRESUME); +} + +static void +bfa_rport_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len) +{ + if (cfg->fwcfg.num_rports < BFA_RPORT_MIN) + cfg->fwcfg.num_rports = BFA_RPORT_MIN; + + *km_len += cfg->fwcfg.num_rports * sizeof(struct bfa_rport_s); +} + +static void +bfa_rport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_rport_mod_s *mod = BFA_RPORT_MOD(bfa); + struct bfa_rport_s *rp; + u16 i; + + INIT_LIST_HEAD(&mod->rp_free_q); + INIT_LIST_HEAD(&mod->rp_active_q); + + rp = (struct bfa_rport_s *) bfa_meminfo_kva(meminfo); + mod->rps_list = rp; + mod->num_rports = cfg->fwcfg.num_rports; + + bfa_assert(mod->num_rports + && !(mod->num_rports & (mod->num_rports - 1))); + + for (i = 0; i < mod->num_rports; i++, rp++) { + bfa_os_memset(rp, 0, sizeof(struct bfa_rport_s)); + rp->bfa = bfa; + rp->rport_tag = i; + bfa_sm_set_state(rp, bfa_rport_sm_uninit); + + /** + * - is unused + */ + if (i) + list_add_tail(&rp->qe, &mod->rp_free_q); + + bfa_reqq_winit(&rp->reqq_wait, bfa_rport_qresume, rp); + } + + /** + * consume memory + */ + bfa_meminfo_kva(meminfo) = (u8 *) rp; +} + +static void +bfa_rport_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_rport_detach(struct bfa_s *bfa) +{ +} + +static void +bfa_rport_start(struct bfa_s *bfa) +{ +} + +static void +bfa_rport_stop(struct bfa_s *bfa) +{ +} + +static void +bfa_rport_iocdisable(struct bfa_s *bfa) +{ + struct bfa_rport_mod_s *mod = BFA_RPORT_MOD(bfa); + struct bfa_rport_s *rport; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &mod->rp_active_q) { + rport = (struct bfa_rport_s *) qe; + bfa_sm_send_event(rport, BFA_RPORT_SM_HWFAIL); + } +} + +static struct bfa_rport_s * +bfa_rport_alloc(struct bfa_rport_mod_s *mod) +{ + struct bfa_rport_s *rport; + + bfa_q_deq(&mod->rp_free_q, &rport); + if (rport) + list_add_tail(&rport->qe, &mod->rp_active_q); + + return (rport); +} + +static void +bfa_rport_free(struct bfa_rport_s *rport) +{ + struct bfa_rport_mod_s *mod = BFA_RPORT_MOD(rport->bfa); + + bfa_assert(bfa_q_is_on_q(&mod->rp_active_q, rport)); + list_del(&rport->qe); + list_add_tail(&rport->qe, &mod->rp_free_q); +} + +static bfa_boolean_t +bfa_rport_send_fwcreate(struct bfa_rport_s *rp) +{ + struct bfi_rport_create_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(rp->bfa, BFA_REQQ_RPORT); + if (!m) { + bfa_reqq_wait(rp->bfa, BFA_REQQ_RPORT, &rp->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_RPORT, BFI_RPORT_H2I_CREATE_REQ, + bfa_lpuid(rp->bfa)); + m->bfa_handle = rp->rport_tag; + m->max_frmsz = bfa_os_htons(rp->rport_info.max_frmsz); + m->pid = rp->rport_info.pid; + m->lp_tag = rp->rport_info.lp_tag; + m->local_pid = rp->rport_info.local_pid; + m->fc_class = rp->rport_info.fc_class; + m->vf_en = rp->rport_info.vf_en; + m->vf_id = rp->rport_info.vf_id; + m->cisc = rp->rport_info.cisc; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT); + return BFA_TRUE; +} + +static bfa_boolean_t +bfa_rport_send_fwdelete(struct bfa_rport_s *rp) +{ + struct bfi_rport_delete_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(rp->bfa, BFA_REQQ_RPORT); + if (!m) { + bfa_reqq_wait(rp->bfa, BFA_REQQ_RPORT, &rp->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_RPORT, BFI_RPORT_H2I_DELETE_REQ, + bfa_lpuid(rp->bfa)); + m->fw_handle = rp->fw_handle; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT); + return BFA_TRUE; +} + +static bfa_boolean_t +bfa_rport_send_fwspeed(struct bfa_rport_s *rp) +{ + struct bfa_rport_speed_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(rp->bfa, BFA_REQQ_RPORT); + if (!m) { + bfa_trc(rp->bfa, rp->rport_info.speed); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_RPORT, BFI_RPORT_H2I_SET_SPEED_REQ, + bfa_lpuid(rp->bfa)); + m->fw_handle = rp->fw_handle; + m->speed = (u8)rp->rport_info.speed; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(rp->bfa, BFA_REQQ_RPORT); + return BFA_TRUE; +} + + + +/** + * bfa_rport_public + */ + +/** + * Rport interrupt processing. + */ +void +bfa_rport_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + union bfi_rport_i2h_msg_u msg; + struct bfa_rport_s *rp; + + bfa_trc(bfa, m->mhdr.msg_id); + + msg.msg = m; + + switch (m->mhdr.msg_id) { + case BFI_RPORT_I2H_CREATE_RSP: + rp = BFA_RPORT_FROM_TAG(bfa, msg.create_rsp->bfa_handle); + rp->fw_handle = msg.create_rsp->fw_handle; + rp->qos_attr = msg.create_rsp->qos_attr; + bfa_assert(msg.create_rsp->status == BFA_STATUS_OK); + bfa_sm_send_event(rp, BFA_RPORT_SM_FWRSP); + break; + + case BFI_RPORT_I2H_DELETE_RSP: + rp = BFA_RPORT_FROM_TAG(bfa, msg.delete_rsp->bfa_handle); + bfa_assert(msg.delete_rsp->status == BFA_STATUS_OK); + bfa_sm_send_event(rp, BFA_RPORT_SM_FWRSP); + break; + + case BFI_RPORT_I2H_QOS_SCN: + rp = BFA_RPORT_FROM_TAG(bfa, msg.qos_scn_evt->bfa_handle); + rp->event_arg.fw_msg = msg.qos_scn_evt; + bfa_sm_send_event(rp, BFA_RPORT_SM_QOS_SCN); + break; + + default: + bfa_trc(bfa, m->mhdr.msg_id); + bfa_assert(0); + } +} + + + +/** + * bfa_rport_api + */ + +struct bfa_rport_s * +bfa_rport_create(struct bfa_s *bfa, void *rport_drv) +{ + struct bfa_rport_s *rp; + + rp = bfa_rport_alloc(BFA_RPORT_MOD(bfa)); + + if (rp == NULL) + return (NULL); + + rp->bfa = bfa; + rp->rport_drv = rport_drv; + bfa_rport_clear_stats(rp); + + bfa_assert(bfa_sm_cmp_state(rp, bfa_rport_sm_uninit)); + bfa_sm_send_event(rp, BFA_RPORT_SM_CREATE); + + return (rp); +} + +void +bfa_rport_delete(struct bfa_rport_s *rport) +{ + bfa_sm_send_event(rport, BFA_RPORT_SM_DELETE); +} + +void +bfa_rport_online(struct bfa_rport_s *rport, struct bfa_rport_info_s *rport_info) +{ + bfa_assert(rport_info->max_frmsz != 0); + + /** + * Some JBODs are seen to be not setting PDU size correctly in PLOGI + * responses. Default to minimum size. + */ + if (rport_info->max_frmsz == 0) { + bfa_trc(rport->bfa, rport->rport_tag); + rport_info->max_frmsz = FC_MIN_PDUSZ; + } + + bfa_os_assign(rport->rport_info, *rport_info); + bfa_sm_send_event(rport, BFA_RPORT_SM_ONLINE); +} + +void +bfa_rport_offline(struct bfa_rport_s *rport) +{ + bfa_sm_send_event(rport, BFA_RPORT_SM_OFFLINE); +} + +void +bfa_rport_speed(struct bfa_rport_s *rport, enum bfa_pport_speed speed) +{ + bfa_assert(speed != 0); + bfa_assert(speed != BFA_PPORT_SPEED_AUTO); + + rport->rport_info.speed = speed; + bfa_sm_send_event(rport, BFA_RPORT_SM_SET_SPEED); +} + +void +bfa_rport_get_stats(struct bfa_rport_s *rport, + struct bfa_rport_hal_stats_s *stats) +{ + *stats = rport->stats; +} + +void +bfa_rport_get_qos_attr(struct bfa_rport_s *rport, + struct bfa_rport_qos_attr_s *qos_attr) +{ + qos_attr->qos_priority = bfa_os_ntohl(rport->qos_attr.qos_priority); + qos_attr->qos_flow_id = bfa_os_ntohl(rport->qos_attr.qos_flow_id); + +} + +void +bfa_rport_clear_stats(struct bfa_rport_s *rport) +{ + bfa_os_memset(&rport->stats, 0, sizeof(rport->stats)); +} + + diff --git a/drivers/scsi/bfa/bfa_rport_priv.h b/drivers/scsi/bfa/bfa_rport_priv.h new file mode 100644 index 000000000000..6490ce2e990d --- /dev/null +++ b/drivers/scsi/bfa/bfa_rport_priv.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef __BFA_RPORT_PRIV_H__ +#define __BFA_RPORT_PRIV_H__ + +#include + +#define BFA_RPORT_MIN 4 + +struct bfa_rport_mod_s { + struct bfa_rport_s *rps_list; /* list of rports */ + struct list_head rp_free_q; /* free bfa_rports */ + struct list_head rp_active_q; /* free bfa_rports */ + u16 num_rports; /* number of rports */ +}; + +#define BFA_RPORT_MOD(__bfa) (&(__bfa)->modules.rport_mod) + +/** + * Convert rport tag to RPORT + */ +#define BFA_RPORT_FROM_TAG(__bfa, _tag) \ + (BFA_RPORT_MOD(__bfa)->rps_list + \ + ((_tag) & (BFA_RPORT_MOD(__bfa)->num_rports - 1))) + +/* + * external functions + */ +void bfa_rport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); +#endif /* __BFA_RPORT_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_sgpg.c b/drivers/scsi/bfa/bfa_sgpg.c new file mode 100644 index 000000000000..279d8f9b8907 --- /dev/null +++ b/drivers/scsi/bfa/bfa_sgpg.c @@ -0,0 +1,231 @@ +/* + * 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. + */ + +#include + +BFA_TRC_FILE(HAL, SGPG); +BFA_MODULE(sgpg); + +/** + * bfa_sgpg_mod BFA SGPG Mode module + */ + +/** + * Compute and return memory needed by FCP(im) module. + */ +static void +bfa_sgpg_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, + u32 *dm_len) +{ + if (cfg->drvcfg.num_sgpgs < BFA_SGPG_MIN) + cfg->drvcfg.num_sgpgs = BFA_SGPG_MIN; + + *km_len += (cfg->drvcfg.num_sgpgs + 1) * sizeof(struct bfa_sgpg_s); + *dm_len += (cfg->drvcfg.num_sgpgs + 1) * sizeof(struct bfi_sgpg_s); +} + + +static void +bfa_sgpg_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *minfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); + int i; + struct bfa_sgpg_s *hsgpg; + struct bfi_sgpg_s *sgpg; + u64 align_len; + + union { + u64 pa; + union bfi_addr_u addr; + } sgpg_pa; + + INIT_LIST_HEAD(&mod->sgpg_q); + INIT_LIST_HEAD(&mod->sgpg_wait_q); + + bfa_trc(bfa, cfg->drvcfg.num_sgpgs); + + mod->num_sgpgs = cfg->drvcfg.num_sgpgs; + mod->sgpg_arr_pa = bfa_meminfo_dma_phys(minfo); + align_len = (BFA_SGPG_ROUNDUP(mod->sgpg_arr_pa) - mod->sgpg_arr_pa); + mod->sgpg_arr_pa += align_len; + mod->hsgpg_arr = (struct bfa_sgpg_s *) (bfa_meminfo_kva(minfo) + + align_len); + mod->sgpg_arr = (struct bfi_sgpg_s *) (bfa_meminfo_dma_virt(minfo) + + align_len); + + hsgpg = mod->hsgpg_arr; + sgpg = mod->sgpg_arr; + sgpg_pa.pa = mod->sgpg_arr_pa; + mod->free_sgpgs = mod->num_sgpgs; + + bfa_assert(!(sgpg_pa.pa & (sizeof(struct bfi_sgpg_s) - 1))); + + for (i = 0; i < mod->num_sgpgs; i++) { + bfa_os_memset(hsgpg, 0, sizeof(*hsgpg)); + bfa_os_memset(sgpg, 0, sizeof(*sgpg)); + + hsgpg->sgpg = sgpg; + hsgpg->sgpg_pa = sgpg_pa.addr; + list_add_tail(&hsgpg->qe, &mod->sgpg_q); + + hsgpg++; + sgpg++; + sgpg_pa.pa += sizeof(struct bfi_sgpg_s); + } + + bfa_meminfo_kva(minfo) = (u8 *) hsgpg; + bfa_meminfo_dma_virt(minfo) = (u8 *) sgpg; + bfa_meminfo_dma_phys(minfo) = sgpg_pa.pa; +} + +static void +bfa_sgpg_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_sgpg_detach(struct bfa_s *bfa) +{ +} + +static void +bfa_sgpg_start(struct bfa_s *bfa) +{ +} + +static void +bfa_sgpg_stop(struct bfa_s *bfa) +{ +} + +static void +bfa_sgpg_iocdisable(struct bfa_s *bfa) +{ +} + + + +/** + * bfa_sgpg_public BFA SGPG public functions + */ + +bfa_status_t +bfa_sgpg_malloc(struct bfa_s *bfa, struct list_head *sgpg_q, int nsgpgs) +{ + struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); + struct bfa_sgpg_s *hsgpg; + int i; + + bfa_trc_fp(bfa, nsgpgs); + + if (mod->free_sgpgs < nsgpgs) + return BFA_STATUS_ENOMEM; + + for (i = 0; i < nsgpgs; i++) { + bfa_q_deq(&mod->sgpg_q, &hsgpg); + bfa_assert(hsgpg); + list_add_tail(&hsgpg->qe, sgpg_q); + } + + mod->free_sgpgs -= nsgpgs; + return BFA_STATUS_OK; +} + +void +bfa_sgpg_mfree(struct bfa_s *bfa, struct list_head *sgpg_q, int nsgpg) +{ + struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); + struct bfa_sgpg_wqe_s *wqe; + + bfa_trc_fp(bfa, nsgpg); + + mod->free_sgpgs += nsgpg; + bfa_assert(mod->free_sgpgs <= mod->num_sgpgs); + + list_splice_tail_init(sgpg_q, &mod->sgpg_q); + + if (list_empty(&mod->sgpg_wait_q)) + return; + + /** + * satisfy as many waiting requests as possible + */ + do { + wqe = bfa_q_first(&mod->sgpg_wait_q); + if (mod->free_sgpgs < wqe->nsgpg) + nsgpg = mod->free_sgpgs; + else + nsgpg = wqe->nsgpg; + bfa_sgpg_malloc(bfa, &wqe->sgpg_q, nsgpg); + wqe->nsgpg -= nsgpg; + if (wqe->nsgpg == 0) { + list_del(&wqe->qe); + wqe->cbfn(wqe->cbarg); + } + } while (mod->free_sgpgs && !list_empty(&mod->sgpg_wait_q)); +} + +void +bfa_sgpg_wait(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe, int nsgpg) +{ + struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); + + bfa_assert(nsgpg > 0); + bfa_assert(nsgpg > mod->free_sgpgs); + + wqe->nsgpg_total = wqe->nsgpg = nsgpg; + + /** + * allocate any left to this one first + */ + if (mod->free_sgpgs) { + /** + * no one else is waiting for SGPG + */ + bfa_assert(list_empty(&mod->sgpg_wait_q)); + list_splice_tail_init(&mod->sgpg_q, &wqe->sgpg_q); + wqe->nsgpg -= mod->free_sgpgs; + mod->free_sgpgs = 0; + } + + list_add_tail(&wqe->qe, &mod->sgpg_wait_q); +} + +void +bfa_sgpg_wcancel(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe) +{ + struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); + + bfa_assert(bfa_q_is_on_q(&mod->sgpg_wait_q, wqe)); + list_del(&wqe->qe); + + if (wqe->nsgpg_total != wqe->nsgpg) + bfa_sgpg_mfree(bfa, &wqe->sgpg_q, + wqe->nsgpg_total - wqe->nsgpg); +} + +void +bfa_sgpg_winit(struct bfa_sgpg_wqe_s *wqe, void (*cbfn) (void *cbarg), + void *cbarg) +{ + INIT_LIST_HEAD(&wqe->sgpg_q); + wqe->cbfn = cbfn; + wqe->cbarg = cbarg; +} + + diff --git a/drivers/scsi/bfa/bfa_sgpg_priv.h b/drivers/scsi/bfa/bfa_sgpg_priv.h new file mode 100644 index 000000000000..9c2a8cbe7522 --- /dev/null +++ b/drivers/scsi/bfa/bfa_sgpg_priv.h @@ -0,0 +1,79 @@ +/* + * 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. + */ + +/** + * hal_sgpg.h BFA SG page module + */ + +#ifndef __BFA_SGPG_PRIV_H__ +#define __BFA_SGPG_PRIV_H__ + +#include + +#define BFA_SGPG_MIN (16) + +/** + * Alignment macro for SG page allocation + */ +#define BFA_SGPG_ROUNDUP(_l) (((_l) + (sizeof(struct bfi_sgpg_s) - 1)) \ + & ~(sizeof(struct bfi_sgpg_s) - 1)) + +struct bfa_sgpg_wqe_s { + struct list_head qe; /* queue sg page element */ + int nsgpg; /* pages to be allocated */ + int nsgpg_total; /* total pages required */ + void (*cbfn) (void *cbarg); + /* callback function */ + void *cbarg; /* callback arg */ + struct list_head sgpg_q; /* queue of alloced sgpgs */ +}; + +struct bfa_sgpg_s { + struct list_head qe; /* queue sg page element */ + struct bfi_sgpg_s *sgpg; /* va of SG page */ + union bfi_addr_u sgpg_pa;/* pa of SG page */ +}; + +/** + * Given number of SG elements, BFA_SGPG_NPAGE() returns the number of + * SG pages required. + */ +#define BFA_SGPG_NPAGE(_nsges) (((_nsges) / BFI_SGPG_DATA_SGES) + 1) + +struct bfa_sgpg_mod_s { + struct bfa_s *bfa; + int num_sgpgs; /* number of SG pages */ + int free_sgpgs; /* number of free SG pages */ + struct bfa_sgpg_s *hsgpg_arr; /* BFA SG page array */ + struct bfi_sgpg_s *sgpg_arr; /* actual SG page array */ + u64 sgpg_arr_pa; /* SG page array DMA addr */ + struct list_head sgpg_q; /* queue of free SG pages */ + struct list_head sgpg_wait_q; /* wait queue for SG pages */ +}; +#define BFA_SGPG_MOD(__bfa) (&(__bfa)->modules.sgpg_mod) + +bfa_status_t bfa_sgpg_malloc(struct bfa_s *bfa, struct list_head *sgpg_q, + int nsgpgs); +void bfa_sgpg_mfree(struct bfa_s *bfa, struct list_head *sgpg_q, + int nsgpgs); +void bfa_sgpg_winit(struct bfa_sgpg_wqe_s *wqe, + void (*cbfn) (void *cbarg), void *cbarg); +void bfa_sgpg_wait(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe, + int nsgpgs); +void bfa_sgpg_wcancel(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe); + +#endif /* __BFA_SGPG_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_sm.c b/drivers/scsi/bfa/bfa_sm.c new file mode 100644 index 000000000000..5420f4f45e58 --- /dev/null +++ b/drivers/scsi/bfa/bfa_sm.c @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/** + * bfasm.c BFA State machine utility functions + */ + +#include + +/** + * cs_sm_api + */ + +int +bfa_sm_to_state(struct bfa_sm_table_s *smt, bfa_sm_t sm) +{ + int i = 0; + + while (smt[i].sm && smt[i].sm != sm) + i++; + return smt[i].state; +} + + diff --git a/drivers/scsi/bfa/bfa_timer.c b/drivers/scsi/bfa/bfa_timer.c new file mode 100644 index 000000000000..cb76481f5cb1 --- /dev/null +++ b/drivers/scsi/bfa/bfa_timer.c @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#include +#include + +void +bfa_timer_init(struct bfa_timer_mod_s *mod) +{ + INIT_LIST_HEAD(&mod->timer_q); +} + +void +bfa_timer_beat(struct bfa_timer_mod_s *mod) +{ + struct list_head *qh = &mod->timer_q; + struct list_head *qe, *qe_next; + struct bfa_timer_s *elem; + struct list_head timedout_q; + + INIT_LIST_HEAD(&timedout_q); + + qe = bfa_q_next(qh); + + while (qe != qh) { + qe_next = bfa_q_next(qe); + + elem = (struct bfa_timer_s *) qe; + if (elem->timeout <= BFA_TIMER_FREQ) { + elem->timeout = 0; + list_del(&elem->qe); + list_add_tail(&elem->qe, &timedout_q); + } else { + elem->timeout -= BFA_TIMER_FREQ; + } + + qe = qe_next; /* go to next elem */ + } + + /* + * Pop all the timeout entries + */ + while (!list_empty(&timedout_q)) { + bfa_q_deq(&timedout_q, &elem); + elem->timercb(elem->arg); + } +} + +/** + * Should be called with lock protection + */ +void +bfa_timer_begin(struct bfa_timer_mod_s *mod, struct bfa_timer_s *timer, + void (*timercb) (void *), void *arg, unsigned int timeout) +{ + + bfa_assert(timercb != NULL); + bfa_assert(!bfa_q_is_on_q(&mod->timer_q, timer)); + + timer->timeout = timeout; + timer->timercb = timercb; + timer->arg = arg; + + list_add_tail(&timer->qe, &mod->timer_q); +} + +/** + * Should be called with lock protection + */ +void +bfa_timer_stop(struct bfa_timer_s *timer) +{ + bfa_assert(!list_empty(&timer->qe)); + + list_del(&timer->qe); +} diff --git a/drivers/scsi/bfa/bfa_trcmod_priv.h b/drivers/scsi/bfa/bfa_trcmod_priv.h new file mode 100644 index 000000000000..b3562dce7e9f --- /dev/null +++ b/drivers/scsi/bfa/bfa_trcmod_priv.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +/** + * hal_trcmod.h BFA trace modules + */ + +#ifndef __BFA_TRCMOD_PRIV_H__ +#define __BFA_TRCMOD_PRIV_H__ + +#include + +/* + * !!! Only append to the enums defined here to avoid any versioning + * !!! needed between trace utility and driver version + */ +enum { + BFA_TRC_HAL_IOC = 1, + BFA_TRC_HAL_INTR = 2, + BFA_TRC_HAL_FCXP = 3, + BFA_TRC_HAL_UF = 4, + BFA_TRC_HAL_DIAG = 5, + BFA_TRC_HAL_RPORT = 6, + BFA_TRC_HAL_FCPIM = 7, + BFA_TRC_HAL_IOIM = 8, + BFA_TRC_HAL_TSKIM = 9, + BFA_TRC_HAL_ITNIM = 10, + BFA_TRC_HAL_PPORT = 11, + BFA_TRC_HAL_SGPG = 12, + BFA_TRC_HAL_FLASH = 13, + BFA_TRC_HAL_DEBUG = 14, + BFA_TRC_HAL_WWN = 15, + BFA_TRC_HAL_FLASH_RAW = 16, + BFA_TRC_HAL_SBOOT = 17, + BFA_TRC_HAL_SBOOT_IO = 18, + BFA_TRC_HAL_SBOOT_INTR = 19, + BFA_TRC_HAL_SBTEST = 20, + BFA_TRC_HAL_IPFC = 21, + BFA_TRC_HAL_IOCFC = 22, + BFA_TRC_HAL_FCPTM = 23, + BFA_TRC_HAL_IOTM = 24, + BFA_TRC_HAL_TSKTM = 25, + BFA_TRC_HAL_TIN = 26, + BFA_TRC_HAL_LPS = 27, + BFA_TRC_HAL_FCDIAG = 28, + BFA_TRC_HAL_PBIND = 29, + BFA_TRC_HAL_IOCFC_CT = 30, + BFA_TRC_HAL_IOCFC_CB = 31, + BFA_TRC_HAL_IOCFC_Q = 32, +}; + +#endif /* __BFA_TRCMOD_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfa_tskim.c b/drivers/scsi/bfa/bfa_tskim.c new file mode 100644 index 000000000000..010d40d1e5d3 --- /dev/null +++ b/drivers/scsi/bfa/bfa_tskim.c @@ -0,0 +1,689 @@ +/* + * 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. + */ + +#include +#include + +BFA_TRC_FILE(HAL, TSKIM); + +/** + * task management completion handling + */ +#define bfa_tskim_qcomp(__tskim, __cbfn) do { \ + bfa_cb_queue((__tskim)->bfa, &(__tskim)->hcb_qe, __cbfn, (__tskim)); \ + bfa_tskim_notify_comp(__tskim); \ +} while (0) + +#define bfa_tskim_notify_comp(__tskim) do { \ + if ((__tskim)->notify) \ + bfa_itnim_tskdone((__tskim)->itnim); \ +} while (0) + +/* + * forward declarations + */ +static void __bfa_cb_tskim_done(void *cbarg, bfa_boolean_t complete); +static void __bfa_cb_tskim_failed(void *cbarg, bfa_boolean_t complete); +static bfa_boolean_t bfa_tskim_match_scope(struct bfa_tskim_s *tskim, + lun_t lun); +static void bfa_tskim_gather_ios(struct bfa_tskim_s *tskim); +static void bfa_tskim_cleanp_comp(void *tskim_cbarg); +static void bfa_tskim_cleanup_ios(struct bfa_tskim_s *tskim); +static bfa_boolean_t bfa_tskim_send(struct bfa_tskim_s *tskim); +static bfa_boolean_t bfa_tskim_send_abort(struct bfa_tskim_s *tskim); +static void bfa_tskim_iocdisable_ios(struct bfa_tskim_s *tskim); + +/** + * bfa_tskim_sm + */ + +enum bfa_tskim_event { + BFA_TSKIM_SM_START = 1, /* TM command start */ + BFA_TSKIM_SM_DONE = 2, /* TM completion */ + BFA_TSKIM_SM_QRESUME = 3, /* resume after qfull */ + BFA_TSKIM_SM_HWFAIL = 5, /* IOC h/w failure event */ + BFA_TSKIM_SM_HCB = 6, /* BFA callback completion */ + BFA_TSKIM_SM_IOS_DONE = 7, /* IO and sub TM completions */ + BFA_TSKIM_SM_CLEANUP = 8, /* TM cleanup on ITN offline */ + BFA_TSKIM_SM_CLEANUP_DONE = 9, /* TM abort completion */ +}; + +static void bfa_tskim_sm_uninit(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_active(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_cleanup(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_iocleanup(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_qfull(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_cleanup_qfull(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); +static void bfa_tskim_sm_hcb(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event); + +/** + * Task management command beginning state. + */ +static void +bfa_tskim_sm_uninit(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_START: + bfa_sm_set_state(tskim, bfa_tskim_sm_active); + bfa_tskim_gather_ios(tskim); + + /** + * If device is offline, do not send TM on wire. Just cleanup + * any pending IO requests and complete TM request. + */ + if (!bfa_itnim_is_online(tskim->itnim)) { + bfa_sm_set_state(tskim, bfa_tskim_sm_iocleanup); + tskim->tsk_status = BFI_TSKIM_STS_OK; + bfa_tskim_cleanup_ios(tskim); + return; + } + + if (!bfa_tskim_send(tskim)) { + bfa_sm_set_state(tskim, bfa_tskim_sm_qfull); + bfa_reqq_wait(tskim->bfa, tskim->itnim->reqq, + &tskim->reqq_wait); + } + break; + + default: + bfa_assert(0); + } +} + +/** + * brief + * TM command is active, awaiting completion from firmware to + * cleanup IO requests in TM scope. + */ +static void +bfa_tskim_sm_active(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_DONE: + bfa_sm_set_state(tskim, bfa_tskim_sm_iocleanup); + bfa_tskim_cleanup_ios(tskim); + break; + + case BFA_TSKIM_SM_CLEANUP: + bfa_sm_set_state(tskim, bfa_tskim_sm_cleanup); + if (!bfa_tskim_send_abort(tskim)) { + bfa_sm_set_state(tskim, bfa_tskim_sm_cleanup_qfull); + bfa_reqq_wait(tskim->bfa, tskim->itnim->reqq, + &tskim->reqq_wait); + } + break; + + case BFA_TSKIM_SM_HWFAIL: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_tskim_iocdisable_ios(tskim); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_failed); + break; + + default: + bfa_assert(0); + } +} + +/** + * An active TM is being cleaned up since ITN is offline. Awaiting cleanup + * completion event from firmware. + */ +static void +bfa_tskim_sm_cleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_DONE: + /** + * Ignore and wait for ABORT completion from firmware. + */ + break; + + case BFA_TSKIM_SM_CLEANUP_DONE: + bfa_sm_set_state(tskim, bfa_tskim_sm_iocleanup); + bfa_tskim_cleanup_ios(tskim); + break; + + case BFA_TSKIM_SM_HWFAIL: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_tskim_iocdisable_ios(tskim); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_failed); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_tskim_sm_iocleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_IOS_DONE: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_done); + break; + + case BFA_TSKIM_SM_CLEANUP: + /** + * Ignore, TM command completed on wire. + * Notify TM conmpletion on IO cleanup completion. + */ + break; + + case BFA_TSKIM_SM_HWFAIL: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_tskim_iocdisable_ios(tskim); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_failed); + break; + + default: + bfa_assert(0); + } +} + +/** + * Task management command is waiting for room in request CQ + */ +static void +bfa_tskim_sm_qfull(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_QRESUME: + bfa_sm_set_state(tskim, bfa_tskim_sm_active); + bfa_tskim_send(tskim); + break; + + case BFA_TSKIM_SM_CLEANUP: + /** + * No need to send TM on wire since ITN is offline. + */ + bfa_sm_set_state(tskim, bfa_tskim_sm_iocleanup); + bfa_reqq_wcancel(&tskim->reqq_wait); + bfa_tskim_cleanup_ios(tskim); + break; + + case BFA_TSKIM_SM_HWFAIL: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_reqq_wcancel(&tskim->reqq_wait); + bfa_tskim_iocdisable_ios(tskim); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_failed); + break; + + default: + bfa_assert(0); + } +} + +/** + * Task management command is active, awaiting for room in request CQ + * to send clean up request. + */ +static void +bfa_tskim_sm_cleanup_qfull(struct bfa_tskim_s *tskim, + enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_DONE: + bfa_reqq_wcancel(&tskim->reqq_wait); + /** + * + * Fall through !!! + */ + + case BFA_TSKIM_SM_QRESUME: + bfa_sm_set_state(tskim, bfa_tskim_sm_cleanup); + bfa_tskim_send_abort(tskim); + break; + + case BFA_TSKIM_SM_HWFAIL: + bfa_sm_set_state(tskim, bfa_tskim_sm_hcb); + bfa_reqq_wcancel(&tskim->reqq_wait); + bfa_tskim_iocdisable_ios(tskim); + bfa_tskim_qcomp(tskim, __bfa_cb_tskim_failed); + break; + + default: + bfa_assert(0); + } +} + +/** + * BFA callback is pending + */ +static void +bfa_tskim_sm_hcb(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) +{ + bfa_trc(tskim->bfa, event); + + switch (event) { + case BFA_TSKIM_SM_HCB: + bfa_sm_set_state(tskim, bfa_tskim_sm_uninit); + bfa_tskim_free(tskim); + break; + + case BFA_TSKIM_SM_CLEANUP: + bfa_tskim_notify_comp(tskim); + break; + + case BFA_TSKIM_SM_HWFAIL: + break; + + default: + bfa_assert(0); + } +} + + + +/** + * bfa_tskim_private + */ + +static void +__bfa_cb_tskim_done(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_tskim_s *tskim = cbarg; + + if (!complete) { + bfa_sm_send_event(tskim, BFA_TSKIM_SM_HCB); + return; + } + + bfa_stats(tskim->itnim, tm_success); + bfa_cb_tskim_done(tskim->bfa->bfad, tskim->dtsk, tskim->tsk_status); +} + +static void +__bfa_cb_tskim_failed(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_tskim_s *tskim = cbarg; + + if (!complete) { + bfa_sm_send_event(tskim, BFA_TSKIM_SM_HCB); + return; + } + + bfa_stats(tskim->itnim, tm_failures); + bfa_cb_tskim_done(tskim->bfa->bfad, tskim->dtsk, + BFI_TSKIM_STS_FAILED); +} + +static bfa_boolean_t +bfa_tskim_match_scope(struct bfa_tskim_s *tskim, lun_t lun) +{ + switch (tskim->tm_cmnd) { + case FCP_TM_TARGET_RESET: + return BFA_TRUE; + + case FCP_TM_ABORT_TASK_SET: + case FCP_TM_CLEAR_TASK_SET: + case FCP_TM_LUN_RESET: + case FCP_TM_CLEAR_ACA: + return (tskim->lun == lun); + + default: + bfa_assert(0); + } + + return BFA_FALSE; +} + +/** + * Gather affected IO requests and task management commands. + */ +static void +bfa_tskim_gather_ios(struct bfa_tskim_s *tskim) +{ + struct bfa_itnim_s *itnim = tskim->itnim; + struct bfa_ioim_s *ioim; + struct list_head *qe, *qen; + + INIT_LIST_HEAD(&tskim->io_q); + + /** + * Gather any active IO requests first. + */ + list_for_each_safe(qe, qen, &itnim->io_q) { + ioim = (struct bfa_ioim_s *) qe; + if (bfa_tskim_match_scope + (tskim, bfa_cb_ioim_get_lun(ioim->dio))) { + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &tskim->io_q); + } + } + + /** + * Failback any pending IO requests immediately. + */ + list_for_each_safe(qe, qen, &itnim->pending_q) { + ioim = (struct bfa_ioim_s *) qe; + if (bfa_tskim_match_scope + (tskim, bfa_cb_ioim_get_lun(ioim->dio))) { + list_del(&ioim->qe); + list_add_tail(&ioim->qe, &ioim->fcpim->ioim_comp_q); + bfa_ioim_tov(ioim); + } + } +} + +/** + * IO cleanup completion + */ +static void +bfa_tskim_cleanp_comp(void *tskim_cbarg) +{ + struct bfa_tskim_s *tskim = tskim_cbarg; + + bfa_stats(tskim->itnim, tm_io_comps); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_IOS_DONE); +} + +/** + * Gather affected IO requests and task management commands. + */ +static void +bfa_tskim_cleanup_ios(struct bfa_tskim_s *tskim) +{ + struct bfa_ioim_s *ioim; + struct list_head *qe, *qen; + + bfa_wc_init(&tskim->wc, bfa_tskim_cleanp_comp, tskim); + + list_for_each_safe(qe, qen, &tskim->io_q) { + ioim = (struct bfa_ioim_s *) qe; + bfa_wc_up(&tskim->wc); + bfa_ioim_cleanup_tm(ioim, tskim); + } + + bfa_wc_wait(&tskim->wc); +} + +/** + * Send task management request to firmware. + */ +static bfa_boolean_t +bfa_tskim_send(struct bfa_tskim_s *tskim) +{ + struct bfa_itnim_s *itnim = tskim->itnim; + struct bfi_tskim_req_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(tskim->bfa, itnim->reqq); + if (!m) + return BFA_FALSE; + + /** + * build i/o request message next + */ + bfi_h2i_set(m->mh, BFI_MC_TSKIM, BFI_TSKIM_H2I_TM_REQ, + bfa_lpuid(tskim->bfa)); + + m->tsk_tag = bfa_os_htons(tskim->tsk_tag); + m->itn_fhdl = tskim->itnim->rport->fw_handle; + m->t_secs = tskim->tsecs; + m->lun = tskim->lun; + m->tm_flags = tskim->tm_cmnd; + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(tskim->bfa, itnim->reqq); + return BFA_TRUE; +} + +/** + * Send abort request to cleanup an active TM to firmware. + */ +static bfa_boolean_t +bfa_tskim_send_abort(struct bfa_tskim_s *tskim) +{ + struct bfa_itnim_s *itnim = tskim->itnim; + struct bfi_tskim_abortreq_s *m; + + /** + * check for room in queue to send request now + */ + m = bfa_reqq_next(tskim->bfa, itnim->reqq); + if (!m) + return BFA_FALSE; + + /** + * build i/o request message next + */ + bfi_h2i_set(m->mh, BFI_MC_TSKIM, BFI_TSKIM_H2I_ABORT_REQ, + bfa_lpuid(tskim->bfa)); + + m->tsk_tag = bfa_os_htons(tskim->tsk_tag); + + /** + * queue I/O message to firmware + */ + bfa_reqq_produce(tskim->bfa, itnim->reqq); + return BFA_TRUE; +} + +/** + * Call to resume task management cmnd waiting for room in request queue. + */ +static void +bfa_tskim_qresume(void *cbarg) +{ + struct bfa_tskim_s *tskim = cbarg; + + bfa_fcpim_stats(tskim->fcpim, qresumes); + bfa_stats(tskim->itnim, tm_qresumes); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_QRESUME); +} + +/** + * Cleanup IOs associated with a task mangement command on IOC failures. + */ +static void +bfa_tskim_iocdisable_ios(struct bfa_tskim_s *tskim) +{ + struct bfa_ioim_s *ioim; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &tskim->io_q) { + ioim = (struct bfa_ioim_s *) qe; + bfa_ioim_iocdisable(ioim); + } +} + + + +/** + * bfa_tskim_friend + */ + +/** + * Notification on completions from related ioim. + */ +void +bfa_tskim_iodone(struct bfa_tskim_s *tskim) +{ + bfa_wc_down(&tskim->wc); +} + +/** + * Handle IOC h/w failure notification from itnim. + */ +void +bfa_tskim_iocdisable(struct bfa_tskim_s *tskim) +{ + tskim->notify = BFA_FALSE; + bfa_stats(tskim->itnim, tm_iocdowns); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_HWFAIL); +} + +/** + * Cleanup TM command and associated IOs as part of ITNIM offline. + */ +void +bfa_tskim_cleanup(struct bfa_tskim_s *tskim) +{ + tskim->notify = BFA_TRUE; + bfa_stats(tskim->itnim, tm_cleanups); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_CLEANUP); +} + +/** + * Memory allocation and initialization. + */ +void +bfa_tskim_attach(struct bfa_fcpim_mod_s *fcpim, struct bfa_meminfo_s *minfo) +{ + struct bfa_tskim_s *tskim; + u16 i; + + INIT_LIST_HEAD(&fcpim->tskim_free_q); + + tskim = (struct bfa_tskim_s *) bfa_meminfo_kva(minfo); + fcpim->tskim_arr = tskim; + + for (i = 0; i < fcpim->num_tskim_reqs; i++, tskim++) { + /* + * initialize TSKIM + */ + bfa_os_memset(tskim, 0, sizeof(struct bfa_tskim_s)); + tskim->tsk_tag = i; + tskim->bfa = fcpim->bfa; + tskim->fcpim = fcpim; + tskim->notify = BFA_FALSE; + bfa_reqq_winit(&tskim->reqq_wait, bfa_tskim_qresume, + tskim); + bfa_sm_set_state(tskim, bfa_tskim_sm_uninit); + + list_add_tail(&tskim->qe, &fcpim->tskim_free_q); + } + + bfa_meminfo_kva(minfo) = (u8 *) tskim; +} + +void +bfa_tskim_detach(struct bfa_fcpim_mod_s *fcpim) +{ + /** + * @todo + */ +} + +void +bfa_tskim_isr(struct bfa_s *bfa, struct bfi_msg_s *m) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfi_tskim_rsp_s *rsp = (struct bfi_tskim_rsp_s *) m; + struct bfa_tskim_s *tskim; + u16 tsk_tag = bfa_os_ntohs(rsp->tsk_tag); + + tskim = BFA_TSKIM_FROM_TAG(fcpim, tsk_tag); + bfa_assert(tskim->tsk_tag == tsk_tag); + + tskim->tsk_status = rsp->tsk_status; + + /** + * Firmware sends BFI_TSKIM_STS_ABORTED status for abort + * requests. All other statuses are for normal completions. + */ + if (rsp->tsk_status == BFI_TSKIM_STS_ABORTED) { + bfa_stats(tskim->itnim, tm_cleanup_comps); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_CLEANUP_DONE); + } else { + bfa_stats(tskim->itnim, tm_fw_rsps); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_DONE); + } +} + + + +/** + * bfa_tskim_api + */ + + +struct bfa_tskim_s * +bfa_tskim_alloc(struct bfa_s *bfa, struct bfad_tskim_s *dtsk) +{ + struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa); + struct bfa_tskim_s *tskim; + + bfa_q_deq(&fcpim->tskim_free_q, &tskim); + + if (!tskim) + bfa_fcpim_stats(fcpim, no_tskims); + else + tskim->dtsk = dtsk; + + return tskim; +} + +void +bfa_tskim_free(struct bfa_tskim_s *tskim) +{ + bfa_assert(bfa_q_is_on_q_func(&tskim->itnim->tsk_q, &tskim->qe)); + list_del(&tskim->qe); + list_add_tail(&tskim->qe, &tskim->fcpim->tskim_free_q); +} + +/** + * Start a task management command. + * + * @param[in] tskim BFA task management command instance + * @param[in] itnim i-t nexus for the task management command + * @param[in] lun lun, if applicable + * @param[in] tm_cmnd Task management command code. + * @param[in] t_secs Timeout in seconds + * + * @return None. + */ +void +bfa_tskim_start(struct bfa_tskim_s *tskim, struct bfa_itnim_s *itnim, lun_t lun, + enum fcp_tm_cmnd tm_cmnd, u8 tsecs) +{ + tskim->itnim = itnim; + tskim->lun = lun; + tskim->tm_cmnd = tm_cmnd; + tskim->tsecs = tsecs; + tskim->notify = BFA_FALSE; + bfa_stats(itnim, tm_cmnds); + + list_add_tail(&tskim->qe, &itnim->tsk_q); + bfa_sm_send_event(tskim, BFA_TSKIM_SM_START); +} + + diff --git a/drivers/scsi/bfa/bfa_uf.c b/drivers/scsi/bfa/bfa_uf.c new file mode 100644 index 000000000000..ff5f9deb1b22 --- /dev/null +++ b/drivers/scsi/bfa/bfa_uf.c @@ -0,0 +1,345 @@ +/* + * 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. + */ + +/** + * bfa_uf.c BFA unsolicited frame receive implementation + */ + +#include +#include +#include +#include + +BFA_TRC_FILE(HAL, UF); +BFA_MODULE(uf); + +/* + ***************************************************************************** + * Internal functions + ***************************************************************************** + */ +static void +__bfa_cb_uf_recv(void *cbarg, bfa_boolean_t complete) +{ + struct bfa_uf_s *uf = cbarg; + struct bfa_uf_mod_s *ufm = BFA_UF_MOD(uf->bfa); + + if (complete) + ufm->ufrecv(ufm->cbarg, uf); +} + +static void +claim_uf_pbs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) +{ + u32 uf_pb_tot_sz; + + ufm->uf_pbs_kva = (struct bfa_uf_buf_s *) bfa_meminfo_dma_virt(mi); + ufm->uf_pbs_pa = bfa_meminfo_dma_phys(mi); + uf_pb_tot_sz = BFA_ROUNDUP((sizeof(struct bfa_uf_buf_s) * ufm->num_ufs), + BFA_DMA_ALIGN_SZ); + + bfa_meminfo_dma_virt(mi) += uf_pb_tot_sz; + bfa_meminfo_dma_phys(mi) += uf_pb_tot_sz; + + bfa_os_memset((void *)ufm->uf_pbs_kva, 0, uf_pb_tot_sz); +} + +static void +claim_uf_post_msgs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) +{ + struct bfi_uf_buf_post_s *uf_bp_msg; + struct bfi_sge_s *sge; + union bfi_addr_u sga_zero = { {0} }; + u16 i; + u16 buf_len; + + ufm->uf_buf_posts = (struct bfi_uf_buf_post_s *) bfa_meminfo_kva(mi); + uf_bp_msg = ufm->uf_buf_posts; + + for (i = 0, uf_bp_msg = ufm->uf_buf_posts; i < ufm->num_ufs; + i++, uf_bp_msg++) { + bfa_os_memset(uf_bp_msg, 0, sizeof(struct bfi_uf_buf_post_s)); + + uf_bp_msg->buf_tag = i; + buf_len = sizeof(struct bfa_uf_buf_s); + uf_bp_msg->buf_len = bfa_os_htons(buf_len); + bfi_h2i_set(uf_bp_msg->mh, BFI_MC_UF, BFI_UF_H2I_BUF_POST, + bfa_lpuid(ufm->bfa)); + + sge = uf_bp_msg->sge; + sge[0].sg_len = buf_len; + sge[0].flags = BFI_SGE_DATA_LAST; + bfa_dma_addr_set(sge[0].sga, ufm_pbs_pa(ufm, i)); + bfa_sge_to_be(sge); + + sge[1].sg_len = buf_len; + sge[1].flags = BFI_SGE_PGDLEN; + sge[1].sga = sga_zero; + bfa_sge_to_be(&sge[1]); + } + + /** + * advance pointer beyond consumed memory + */ + bfa_meminfo_kva(mi) = (u8 *) uf_bp_msg; +} + +static void +claim_ufs(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) +{ + u16 i; + struct bfa_uf_s *uf; + + /* + * Claim block of memory for UF list + */ + ufm->uf_list = (struct bfa_uf_s *) bfa_meminfo_kva(mi); + + /* + * Initialize UFs and queue it in UF free queue + */ + for (i = 0, uf = ufm->uf_list; i < ufm->num_ufs; i++, uf++) { + bfa_os_memset(uf, 0, sizeof(struct bfa_uf_s)); + uf->bfa = ufm->bfa; + uf->uf_tag = i; + uf->pb_len = sizeof(struct bfa_uf_buf_s); + uf->buf_kva = (void *)&ufm->uf_pbs_kva[i]; + uf->buf_pa = ufm_pbs_pa(ufm, i); + list_add_tail(&uf->qe, &ufm->uf_free_q); + } + + /** + * advance memory pointer + */ + bfa_meminfo_kva(mi) = (u8 *) uf; +} + +static void +uf_mem_claim(struct bfa_uf_mod_s *ufm, struct bfa_meminfo_s *mi) +{ + claim_uf_pbs(ufm, mi); + claim_ufs(ufm, mi); + claim_uf_post_msgs(ufm, mi); +} + +static void +bfa_uf_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *ndm_len, u32 *dm_len) +{ + u32 num_ufs = cfg->fwcfg.num_uf_bufs; + + /* + * dma-able memory for UF posted bufs + */ + *dm_len += BFA_ROUNDUP((sizeof(struct bfa_uf_buf_s) * num_ufs), + BFA_DMA_ALIGN_SZ); + + /* + * kernel Virtual memory for UFs and UF buf post msg copies + */ + *ndm_len += sizeof(struct bfa_uf_s) * num_ufs; + *ndm_len += sizeof(struct bfi_uf_buf_post_s) * num_ufs; +} + +static void +bfa_uf_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, struct bfa_pcidev_s *pcidev) +{ + struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); + + bfa_os_memset(ufm, 0, sizeof(struct bfa_uf_mod_s)); + ufm->bfa = bfa; + ufm->num_ufs = cfg->fwcfg.num_uf_bufs; + INIT_LIST_HEAD(&ufm->uf_free_q); + INIT_LIST_HEAD(&ufm->uf_posted_q); + + uf_mem_claim(ufm, meminfo); +} + +static void +bfa_uf_initdone(struct bfa_s *bfa) +{ +} + +static void +bfa_uf_detach(struct bfa_s *bfa) +{ +} + +static struct bfa_uf_s * +bfa_uf_get(struct bfa_uf_mod_s *uf_mod) +{ + struct bfa_uf_s *uf; + + bfa_q_deq(&uf_mod->uf_free_q, &uf); + return (uf); +} + +static void +bfa_uf_put(struct bfa_uf_mod_s *uf_mod, struct bfa_uf_s *uf) +{ + list_add_tail(&uf->qe, &uf_mod->uf_free_q); +} + +static bfa_status_t +bfa_uf_post(struct bfa_uf_mod_s *ufm, struct bfa_uf_s *uf) +{ + struct bfi_uf_buf_post_s *uf_post_msg; + + uf_post_msg = bfa_reqq_next(ufm->bfa, BFA_REQQ_FCXP); + if (!uf_post_msg) + return BFA_STATUS_FAILED; + + bfa_os_memcpy(uf_post_msg, &ufm->uf_buf_posts[uf->uf_tag], + sizeof(struct bfi_uf_buf_post_s)); + bfa_reqq_produce(ufm->bfa, BFA_REQQ_FCXP); + + bfa_trc(ufm->bfa, uf->uf_tag); + + list_add_tail(&uf->qe, &ufm->uf_posted_q); + return BFA_STATUS_OK; +} + +static void +bfa_uf_post_all(struct bfa_uf_mod_s *uf_mod) +{ + struct bfa_uf_s *uf; + + while ((uf = bfa_uf_get(uf_mod)) != NULL) { + if (bfa_uf_post(uf_mod, uf) != BFA_STATUS_OK) + break; + } +} + +static void +uf_recv(struct bfa_s *bfa, struct bfi_uf_frm_rcvd_s *m) +{ + struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); + u16 uf_tag = m->buf_tag; + struct bfa_uf_buf_s *uf_buf = &ufm->uf_pbs_kva[uf_tag]; + struct bfa_uf_s *uf = &ufm->uf_list[uf_tag]; + u8 *buf = &uf_buf->d[0]; + struct fchs_s *fchs; + + m->frm_len = bfa_os_ntohs(m->frm_len); + m->xfr_len = bfa_os_ntohs(m->xfr_len); + + fchs = (struct fchs_s *) uf_buf; + + list_del(&uf->qe); /* dequeue from posted queue */ + + uf->data_ptr = buf; + uf->data_len = m->xfr_len; + + bfa_assert(uf->data_len >= sizeof(struct fchs_s)); + + if (uf->data_len == sizeof(struct fchs_s)) { + bfa_plog_fchdr(bfa->plog, BFA_PL_MID_HAL_UF, BFA_PL_EID_RX, + uf->data_len, (struct fchs_s *) buf); + } else { + u32 pld_w0 = *((u32 *) (buf + sizeof(struct fchs_s))); + bfa_plog_fchdr_and_pl(bfa->plog, BFA_PL_MID_HAL_UF, + BFA_PL_EID_RX, uf->data_len, + (struct fchs_s *) buf, pld_w0); + } + + bfa_cb_queue(bfa, &uf->hcb_qe, __bfa_cb_uf_recv, uf); +} + +static void +bfa_uf_stop(struct bfa_s *bfa) +{ +} + +static void +bfa_uf_iocdisable(struct bfa_s *bfa) +{ + struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); + struct bfa_uf_s *uf; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &ufm->uf_posted_q) { + uf = (struct bfa_uf_s *) qe; + list_del(&uf->qe); + bfa_uf_put(ufm, uf); + } +} + +static void +bfa_uf_start(struct bfa_s *bfa) +{ + bfa_uf_post_all(BFA_UF_MOD(bfa)); +} + + + +/** + * bfa_uf_api + */ + +/** + * Register handler for all unsolicted recieve frames. + * + * @param[in] bfa BFA instance + * @param[in] ufrecv receive handler function + * @param[in] cbarg receive handler arg + */ +void +bfa_uf_recv_register(struct bfa_s *bfa, bfa_cb_uf_recv_t ufrecv, void *cbarg) +{ + struct bfa_uf_mod_s *ufm = BFA_UF_MOD(bfa); + + ufm->ufrecv = ufrecv; + ufm->cbarg = cbarg; +} + +/** + * Free an unsolicited frame back to BFA. + * + * @param[in] uf unsolicited frame to be freed + * + * @return None + */ +void +bfa_uf_free(struct bfa_uf_s *uf) +{ + bfa_uf_put(BFA_UF_MOD(uf->bfa), uf); + bfa_uf_post_all(BFA_UF_MOD(uf->bfa)); +} + + + +/** + * uf_pub BFA uf module public functions + */ + +void +bfa_uf_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) +{ + bfa_trc(bfa, msg->mhdr.msg_id); + + switch (msg->mhdr.msg_id) { + case BFI_UF_I2H_FRM_RCVD: + uf_recv(bfa, (struct bfi_uf_frm_rcvd_s *) msg); + break; + + default: + bfa_trc(bfa, msg->mhdr.msg_id); + bfa_assert(0); + } +} + + diff --git a/drivers/scsi/bfa/bfa_uf_priv.h b/drivers/scsi/bfa/bfa_uf_priv.h new file mode 100644 index 000000000000..bcb490f834f3 --- /dev/null +++ b/drivers/scsi/bfa/bfa_uf_priv.h @@ -0,0 +1,47 @@ +/* + * 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. + */ +#ifndef __BFA_UF_PRIV_H__ +#define __BFA_UF_PRIV_H__ + +#include +#include +#include + +#define BFA_UF_MIN (4) + +struct bfa_uf_mod_s { + struct bfa_s *bfa; /* back pointer to BFA */ + struct bfa_uf_s *uf_list; /* array of UFs */ + u16 num_ufs; /* num unsolicited rx frames */ + struct list_head uf_free_q; /* free UFs */ + struct list_head uf_posted_q; /* UFs posted to IOC */ + struct bfa_uf_buf_s *uf_pbs_kva; /* list UF bufs request pld */ + u64 uf_pbs_pa; /* phy addr for UF bufs */ + struct bfi_uf_buf_post_s *uf_buf_posts; + /* pre-built UF post msgs */ + bfa_cb_uf_recv_t ufrecv; /* uf recv handler function */ + void *cbarg; /* uf receive handler arg */ +}; + +#define BFA_UF_MOD(__bfa) (&(__bfa)->modules.uf_mod) + +#define ufm_pbs_pa(_ufmod, _uftag) \ + ((_ufmod)->uf_pbs_pa + sizeof(struct bfa_uf_buf_s) * (_uftag)) + +void bfa_uf_isr(struct bfa_s *bfa, struct bfi_msg_s *msg); + +#endif /* __BFA_UF_PRIV_H__ */ diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c new file mode 100644 index 000000000000..6f2be5abf561 --- /dev/null +++ b/drivers/scsi/bfa/bfad.c @@ -0,0 +1,1182 @@ +/* + * 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 +#include "bfad_drv.h" +#include "bfad_im.h" +#include "bfad_tm.h" +#include "bfad_ipfc.h" +#include "bfad_trcmod.h" +#include +#include +#include +#include + +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; +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(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); +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); +} + +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; + + 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_pport_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; + int i; + + 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); + INIT_LIST_HEAD(&bfad->file_q); + INIT_LIST_HEAD(&bfad->file_free_q); + for (i = 0; i < BFAD_AEN_MAX_APPS; i++) { + bfa_q_qe_init(&bfad->file_buf[i].qe); + list_add_tail(&bfad->file_buf[i].qe, &bfad->file_free_q); + } + 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", + __FUNCTION__, 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_init(&bfad->bfa_fcs, &bfad->bfa, bfad, BFA_FALSE); + bfa_fcs_driver_info_init(&bfad->bfa_fcs, &driver_info); + 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) +{ + del_timer_sync(&bfad->hal_tmo); + bfa_isr_disable(&bfad->bfa); + bfa_detach(&bfad->bfa); + bfad_remove_intr(bfad); + bfa_assert(list_empty(&bfad->file_q)); + bfad_hal_mem_release(bfad); +} + +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); +} + + /* + * 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; + char buf[16]; + + /* + * 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; + sprintf(buf, "%d", bfad_inst); + bfa_log_init(bfad->logmod, buf, 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; + + retval = bfad_drv_init(bfad); + if (retval != BFA_STATUS_OK) + goto out_drv_init_failure; + if (!(bfad->bfad_flags & BFAD_HAL_INIT_DONE)) { + printk(KERN_WARNING "bfad%d: hal init failed\n", bfad->inst_no); + goto ok; + } + + /* + * 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); +ok: + return 0; + +out_fc4_probe_failure: + bfad_fc4_probe_undo(bfad); + bfad_uncfg_pport(bfad); +out_cfg_pport_failure: + bfad_drv_uninit(bfad); +out_drv_init_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); + + 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); + + bfad_remove_intr(bfad); + + del_timer_sync(&bfad->hal_tmo); + 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); + + diff --git a/drivers/scsi/bfa/bfad_attr.c b/drivers/scsi/bfa/bfad_attr.c new file mode 100644 index 000000000000..9129ae3040ff --- /dev/null +++ b/drivers/scsi/bfa/bfad_attr.c @@ -0,0 +1,649 @@ +/* + * 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. + */ + +/** + * bfa_attr.c Linux driver configuration interface module. + */ + +#include "bfad_drv.h" +#include "bfad_im.h" +#include "bfad_trcmod.h" +#include "bfad_attr.h" + +/** + * FC_transport_template FC transport template + */ + +/** + * FC transport template entry, get SCSI target port ID. + */ +void +bfad_im_get_starget_port_id(struct scsi_target *starget) +{ + struct Scsi_Host *shost; + struct bfad_im_port_s *im_port; + struct bfad_s *bfad; + struct bfad_itnim_s *itnim = NULL; + u32 fc_id = -1; + unsigned long flags; + + shost = bfad_os_starget_to_shost(starget); + im_port = (struct bfad_im_port_s *) shost->hostdata[0]; + bfad = im_port->bfad; + spin_lock_irqsave(&bfad->bfad_lock, flags); + + itnim = bfad_os_get_itnim(im_port, starget->id); + if (itnim) + fc_id = bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim); + + fc_starget_port_id(starget) = fc_id; + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +/** + * FC transport template entry, get SCSI target nwwn. + */ +void +bfad_im_get_starget_node_name(struct scsi_target *starget) +{ + struct Scsi_Host *shost; + struct bfad_im_port_s *im_port; + struct bfad_s *bfad; + struct bfad_itnim_s *itnim = NULL; + u64 node_name = 0; + unsigned long flags; + + shost = bfad_os_starget_to_shost(starget); + im_port = (struct bfad_im_port_s *) shost->hostdata[0]; + bfad = im_port->bfad; + spin_lock_irqsave(&bfad->bfad_lock, flags); + + itnim = bfad_os_get_itnim(im_port, starget->id); + if (itnim) + node_name = bfa_fcs_itnim_get_nwwn(&itnim->fcs_itnim); + + fc_starget_node_name(starget) = bfa_os_htonll(node_name); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +/** + * FC transport template entry, get SCSI target pwwn. + */ +void +bfad_im_get_starget_port_name(struct scsi_target *starget) +{ + struct Scsi_Host *shost; + struct bfad_im_port_s *im_port; + struct bfad_s *bfad; + struct bfad_itnim_s *itnim = NULL; + u64 port_name = 0; + unsigned long flags; + + shost = bfad_os_starget_to_shost(starget); + im_port = (struct bfad_im_port_s *) shost->hostdata[0]; + bfad = im_port->bfad; + spin_lock_irqsave(&bfad->bfad_lock, flags); + + itnim = bfad_os_get_itnim(im_port, starget->id); + if (itnim) + port_name = bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim); + + fc_starget_port_name(starget) = bfa_os_htonll(port_name); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +/** + * FC transport template entry, get SCSI host port ID. + */ +void +bfad_im_get_host_port_id(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_port_s *port = im_port->port; + + fc_host_port_id(shost) = + bfa_os_hton3b(bfa_fcs_port_get_fcid(port->fcs_port)); +} + + + + + +struct Scsi_Host * +bfad_os_starget_to_shost(struct scsi_target *starget) +{ + return dev_to_shost(starget->dev.parent); +} + +/** + * FC transport template entry, get SCSI host port type. + */ +static void +bfad_im_get_host_port_type(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_pport_attr_s attr; + + bfa_pport_get_attr(&bfad->bfa, &attr); + + switch (attr.port_type) { + case BFA_PPORT_TYPE_NPORT: + fc_host_port_type(shost) = FC_PORTTYPE_NPORT; + break; + case BFA_PPORT_TYPE_NLPORT: + fc_host_port_type(shost) = FC_PORTTYPE_NLPORT; + break; + case BFA_PPORT_TYPE_P2P: + fc_host_port_type(shost) = FC_PORTTYPE_PTP; + break; + case BFA_PPORT_TYPE_LPORT: + fc_host_port_type(shost) = FC_PORTTYPE_LPORT; + break; + default: + fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN; + break; + } +} + +/** + * FC transport template entry, get SCSI host port state. + */ +static void +bfad_im_get_host_port_state(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_pport_attr_s attr; + + bfa_pport_get_attr(&bfad->bfa, &attr); + + switch (attr.port_state) { + case BFA_PPORT_ST_LINKDOWN: + fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN; + break; + case BFA_PPORT_ST_LINKUP: + fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; + break; + case BFA_PPORT_ST_UNINIT: + case BFA_PPORT_ST_ENABLING_QWAIT: + case BFA_PPORT_ST_ENABLING: + case BFA_PPORT_ST_DISABLING_QWAIT: + case BFA_PPORT_ST_DISABLING: + case BFA_PPORT_ST_DISABLED: + case BFA_PPORT_ST_STOPPED: + case BFA_PPORT_ST_IOCDOWN: + default: + fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; + break; + } +} + +/** + * FC transport template entry, get SCSI host active fc4s. + */ +static void +bfad_im_get_host_active_fc4s(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_port_s *port = im_port->port; + + memset(fc_host_active_fc4s(shost), 0, + sizeof(fc_host_active_fc4s(shost))); + + if (port->supported_fc4s & + (BFA_PORT_ROLE_FCP_IM | BFA_PORT_ROLE_FCP_TM)) + fc_host_active_fc4s(shost)[2] = 1; + + if (port->supported_fc4s & BFA_PORT_ROLE_FCP_IPFC) + fc_host_active_fc4s(shost)[3] = 0x20; + + fc_host_active_fc4s(shost)[7] = 1; +} + +/** + * FC transport template entry, get SCSI host link speed. + */ +static void +bfad_im_get_host_speed(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_pport_attr_s attr; + + bfa_pport_get_attr(&bfad->bfa, &attr); + switch (attr.speed) { + case BFA_PPORT_SPEED_8GBPS: + fc_host_speed(shost) = FC_PORTSPEED_8GBIT; + break; + case BFA_PPORT_SPEED_4GBPS: + fc_host_speed(shost) = FC_PORTSPEED_4GBIT; + break; + case BFA_PPORT_SPEED_2GBPS: + fc_host_speed(shost) = FC_PORTSPEED_2GBIT; + break; + case BFA_PPORT_SPEED_1GBPS: + fc_host_speed(shost) = FC_PORTSPEED_1GBIT; + break; + default: + fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; + break; + } +} + +/** + * FC transport template entry, get SCSI host port type. + */ +static void +bfad_im_get_host_fabric_name(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_port_s *port = im_port->port; + wwn_t fabric_nwwn = 0; + + fabric_nwwn = bfa_fcs_port_get_fabric_name(port->fcs_port); + + fc_host_fabric_name(shost) = bfa_os_htonll(fabric_nwwn); + +} + +/** + * FC transport template entry, get BFAD statistics. + */ +static struct fc_host_statistics * +bfad_im_get_stats(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfad_hal_comp fcomp; + struct fc_host_statistics *hstats; + bfa_status_t rc; + unsigned long flags; + + hstats = &bfad->link_stats; + init_completion(&fcomp.comp); + spin_lock_irqsave(&bfad->bfad_lock, flags); + memset(hstats, 0, sizeof(struct fc_host_statistics)); + rc = bfa_pport_get_stats(&bfad->bfa, + (union bfa_pport_stats_u *) hstats, + bfad_hcb_comp, &fcomp); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + if (rc != BFA_STATUS_OK) + return NULL; + + wait_for_completion(&fcomp.comp); + + return hstats; +} + +/** + * FC transport template entry, reset BFAD statistics. + */ +static void +bfad_im_reset_stats(struct Scsi_Host *shost) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfad_hal_comp fcomp; + unsigned long flags; + bfa_status_t rc; + + init_completion(&fcomp.comp); + spin_lock_irqsave(&bfad->bfad_lock, flags); + rc = bfa_pport_clear_stats(&bfad->bfa, bfad_hcb_comp, &fcomp); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + if (rc != BFA_STATUS_OK) + return; + + wait_for_completion(&fcomp.comp); + + return; +} + +/** + * FC transport template entry, get rport loss timeout. + */ +static void +bfad_im_get_rport_loss_tmo(struct fc_rport *rport) +{ + struct bfad_itnim_data_s *itnim_data = rport->dd_data; + struct bfad_itnim_s *itnim = itnim_data->itnim; + struct bfad_s *bfad = itnim->im->bfad; + unsigned long flags; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + rport->dev_loss_tmo = bfa_fcpim_path_tov_get(&bfad->bfa); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +/** + * FC transport template entry, set rport loss timeout. + */ +static void +bfad_im_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout) +{ + struct bfad_itnim_data_s *itnim_data = rport->dd_data; + struct bfad_itnim_s *itnim = itnim_data->itnim; + struct bfad_s *bfad = itnim->im->bfad; + unsigned long flags; + + if (timeout > 0) { + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_fcpim_path_tov_set(&bfad->bfa, timeout); + rport->dev_loss_tmo = bfa_fcpim_path_tov_get(&bfad->bfa); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + } + +} + +struct fc_function_template bfad_im_fc_function_template = { + + /* Target dynamic attributes */ + .get_starget_port_id = bfad_im_get_starget_port_id, + .show_starget_port_id = 1, + .get_starget_node_name = bfad_im_get_starget_node_name, + .show_starget_node_name = 1, + .get_starget_port_name = bfad_im_get_starget_port_name, + .show_starget_port_name = 1, + + /* Host dynamic attribute */ + .get_host_port_id = bfad_im_get_host_port_id, + .show_host_port_id = 1, + + /* Host fixed attributes */ + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, + .show_host_supported_speeds = 1, + .show_host_maxframe_size = 1, + + /* More host dynamic attributes */ + .show_host_port_type = 1, + .get_host_port_type = bfad_im_get_host_port_type, + .show_host_port_state = 1, + .get_host_port_state = bfad_im_get_host_port_state, + .show_host_active_fc4s = 1, + .get_host_active_fc4s = bfad_im_get_host_active_fc4s, + .show_host_speed = 1, + .get_host_speed = bfad_im_get_host_speed, + .show_host_fabric_name = 1, + .get_host_fabric_name = bfad_im_get_host_fabric_name, + + .show_host_symbolic_name = 1, + + /* Statistics */ + .get_fc_host_stats = bfad_im_get_stats, + .reset_fc_host_stats = bfad_im_reset_stats, + + /* Allocation length for host specific data */ + .dd_fcrport_size = sizeof(struct bfad_itnim_data_s *), + + /* Remote port fixed attributes */ + .show_rport_maxframe_size = 1, + .show_rport_supported_classes = 1, + .show_rport_dev_loss_tmo = 1, + .get_rport_dev_loss_tmo = bfad_im_get_rport_loss_tmo, + .set_rport_dev_loss_tmo = bfad_im_set_rport_loss_tmo, +}; + +/** + * Scsi_Host_attrs SCSI host attributes + */ +static ssize_t +bfad_im_serial_num_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", + ioc_attr.adapter_attr.serial_num); +} + +static ssize_t +bfad_im_model_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", ioc_attr.adapter_attr.model); +} + +static ssize_t +bfad_im_model_desc_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", + ioc_attr.adapter_attr.model_descr); +} + +static ssize_t +bfad_im_node_name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_port_s *port = im_port->port; + u64 nwwn; + + nwwn = bfa_fcs_port_get_nwwn(port->fcs_port); + return snprintf(buf, PAGE_SIZE, "0x%llx\n", bfa_os_htonll(nwwn)); +} + +static ssize_t +bfad_im_symbolic_name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + + return snprintf(buf, PAGE_SIZE, "Brocade %s FV%s DV%s\n", + ioc_attr.adapter_attr.model, + ioc_attr.adapter_attr.fw_ver, BFAD_DRIVER_VERSION); +} + +static ssize_t +bfad_im_hw_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", ioc_attr.adapter_attr.hw_ver); +} + +static ssize_t +bfad_im_drv_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", BFAD_DRIVER_VERSION); +} + +static ssize_t +bfad_im_optionrom_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", + ioc_attr.adapter_attr.optrom_ver); +} + +static ssize_t +bfad_im_fw_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%s\n", ioc_attr.adapter_attr.fw_ver); +} + +static ssize_t +bfad_im_num_of_ports_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioc_attr_s ioc_attr; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + return snprintf(buf, PAGE_SIZE, "%d\n", ioc_attr.adapter_attr.nports); +} + +static ssize_t +bfad_im_drv_name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", BFAD_DRIVER_NAME); +} + +static ssize_t +bfad_im_num_of_discovered_ports_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_port_s *port = im_port->port; + struct bfad_s *bfad = im_port->bfad; + int nrports = 2048; + wwn_t *rports = NULL; + unsigned long flags; + + rports = kzalloc(sizeof(wwn_t) * nrports , GFP_ATOMIC); + if (rports == NULL) + return -ENOMEM; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + bfa_fcs_port_get_rports(port->fcs_port, rports, &nrports); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + kfree(rports); + + return snprintf(buf, PAGE_SIZE, "%d\n", nrports); +} + +static DEVICE_ATTR(serial_number, S_IRUGO, + bfad_im_serial_num_show, NULL); +static DEVICE_ATTR(model, S_IRUGO, bfad_im_model_show, NULL); +static DEVICE_ATTR(model_description, S_IRUGO, + bfad_im_model_desc_show, NULL); +static DEVICE_ATTR(node_name, S_IRUGO, bfad_im_node_name_show, NULL); +static DEVICE_ATTR(symbolic_name, S_IRUGO, + bfad_im_symbolic_name_show, NULL); +static DEVICE_ATTR(hardware_version, S_IRUGO, + bfad_im_hw_version_show, NULL); +static DEVICE_ATTR(driver_version, S_IRUGO, + bfad_im_drv_version_show, NULL); +static DEVICE_ATTR(option_rom_version, S_IRUGO, + bfad_im_optionrom_version_show, NULL); +static DEVICE_ATTR(firmware_version, S_IRUGO, + bfad_im_fw_version_show, NULL); +static DEVICE_ATTR(number_of_ports, S_IRUGO, + bfad_im_num_of_ports_show, NULL); +static DEVICE_ATTR(driver_name, S_IRUGO, bfad_im_drv_name_show, NULL); +static DEVICE_ATTR(number_of_discovered_ports, S_IRUGO, + bfad_im_num_of_discovered_ports_show, NULL); + +struct device_attribute *bfad_im_host_attrs[] = { + &dev_attr_serial_number, + &dev_attr_model, + &dev_attr_model_description, + &dev_attr_node_name, + &dev_attr_symbolic_name, + &dev_attr_hardware_version, + &dev_attr_driver_version, + &dev_attr_option_rom_version, + &dev_attr_firmware_version, + &dev_attr_number_of_ports, + &dev_attr_driver_name, + &dev_attr_number_of_discovered_ports, + NULL, +}; + +struct device_attribute *bfad_im_vport_attrs[] = { + &dev_attr_serial_number, + &dev_attr_model, + &dev_attr_model_description, + &dev_attr_node_name, + &dev_attr_symbolic_name, + &dev_attr_hardware_version, + &dev_attr_driver_version, + &dev_attr_option_rom_version, + &dev_attr_firmware_version, + &dev_attr_number_of_ports, + &dev_attr_driver_name, + &dev_attr_number_of_discovered_ports, + NULL, +}; + + diff --git a/drivers/scsi/bfa/bfad_attr.h b/drivers/scsi/bfa/bfad_attr.h new file mode 100644 index 000000000000..4d3312da6a81 --- /dev/null +++ b/drivers/scsi/bfa/bfad_attr.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef __BFAD_ATTR_H__ +#define __BFAD_ATTR_H__ +/** + * bfad_attr.h VMware driver configuration interface module. + */ + +/** + * FC_transport_template FC transport template + */ + +struct Scsi_Host* +bfad_os_dev_to_shost(struct scsi_target *starget); + +/** + * FC transport template entry, get SCSI target port ID. + */ +void +bfad_im_get_starget_port_id(struct scsi_target *starget); + +/** + * FC transport template entry, get SCSI target nwwn. + */ +void +bfad_im_get_starget_node_name(struct scsi_target *starget); + +/** + * FC transport template entry, get SCSI target pwwn. + */ +void +bfad_im_get_starget_port_name(struct scsi_target *starget); + +/** + * FC transport template entry, get SCSI host port ID. + */ +void +bfad_im_get_host_port_id(struct Scsi_Host *shost); + +/** + * FC transport template entry, issue a LIP. + */ +int +bfad_im_issue_fc_host_lip(struct Scsi_Host *shost); + +struct Scsi_Host* +bfad_os_starget_to_shost(struct scsi_target *starget); + + +#endif /* __BFAD_ATTR_H__ */ diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h new file mode 100644 index 000000000000..172c81e25c1c --- /dev/null +++ b/drivers/scsi/bfa/bfad_drv.h @@ -0,0 +1,295 @@ +/* + * 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. + */ + +/** + * Contains base driver definitions. + */ + +/** + * bfa_drv.h Linux driver data structures. + */ + +#ifndef __BFAD_DRV_H__ +#define __BFAD_DRV_H__ + +#include "bfa_os_inc.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "aen/bfa_aen.h" +#include + +#define BFAD_DRIVER_NAME "bfa" +#ifdef BFA_DRIVER_VERSION +#define BFAD_DRIVER_VERSION BFA_DRIVER_VERSION +#else +#define BFAD_DRIVER_VERSION "2.0.0.0" +#endif + + +#define BFAD_IRQ_FLAGS IRQF_SHARED + +/* + * BFAD flags + */ +#define BFAD_MSIX_ON 0x00000001 +#define BFAD_HAL_INIT_DONE 0x00000002 +#define BFAD_DRV_INIT_DONE 0x00000004 +#define BFAD_CFG_PPORT_DONE 0x00000008 +#define BFAD_HAL_START_DONE 0x00000010 +#define BFAD_PORT_ONLINE 0x00000020 +#define BFAD_RPORT_ONLINE 0x00000040 + +#define BFAD_PORT_DELETE 0x00000001 + +/* + * BFAD related definition + */ +#define SCSI_SCAN_DELAY HZ +#define BFAD_STOP_TIMEOUT 30 +#define BFAD_SUSPEND_TIMEOUT BFAD_STOP_TIMEOUT + +/* + * BFAD configuration parameter default values + */ +#define BFAD_LUN_QUEUE_DEPTH 32 +#define BFAD_IO_MAX_SGE SG_ALL + +#define bfad_isr_t irq_handler_t + +#define MAX_MSIX_ENTRY 22 + +struct bfad_msix_s { + struct bfad_s *bfad; + struct msix_entry msix; +}; + +enum bfad_port_pvb_type { + BFAD_PORT_PHYS_BASE = 0, + BFAD_PORT_PHYS_VPORT = 1, + BFAD_PORT_VF_BASE = 2, + BFAD_PORT_VF_VPORT = 3, +}; + +/* + * PORT data structure + */ +struct bfad_port_s { + struct list_head list_entry; + struct bfad_s *bfad; + struct bfa_fcs_port_s *fcs_port; + u32 roles; + s32 flags; + u32 supported_fc4s; + u8 ipfc_flags; + enum bfad_port_pvb_type pvb_type; + struct bfad_im_port_s *im_port; /* IM specific data */ + struct bfad_tm_port_s *tm_port; /* TM specific data */ + struct bfad_ipfc_port_s *ipfc_port; /* IPFC specific data */ +}; + +/* + * VPORT data structure + */ +struct bfad_vport_s { + struct bfad_port_s drv_port; + struct bfa_fcs_vport_s fcs_vport; + struct completion *comp_del; +}; + +/* + * VF data structure + */ +struct bfad_vf_s { + bfa_fcs_vf_t fcs_vf; + struct bfad_port_s base_port; /* base port for vf */ + struct bfad_s *bfad; +}; + +struct bfad_cfg_param_s { + u32 rport_del_timeout; + u32 ioc_queue_depth; + u32 lun_queue_depth; + u32 io_max_sge; + u32 binding_method; +}; + +#define BFAD_AEN_MAX_APPS 8 +struct bfad_aen_file_s { + struct list_head qe; + struct bfad_s *bfad; + s32 ri; + s32 app_id; +}; + +/* + * BFAD (PCI function) data structure + */ +struct bfad_s { + struct list_head list_entry; + struct bfa_s bfa; + struct bfa_fcs_s bfa_fcs; + struct pci_dev *pcidev; + const char *pci_name; + struct bfa_pcidev_s hal_pcidev; + struct bfa_ioc_pci_attr_s pci_attr; + unsigned long pci_bar0_map; + void __iomem *pci_bar0_kva; + struct completion comp; + struct completion suspend; + struct completion disable_comp; + bfa_boolean_t disable_active; + struct bfad_port_s pport; /* physical port of the BFAD */ + struct bfa_meminfo_s meminfo; + struct bfa_iocfc_cfg_s ioc_cfg; + u32 inst_no; /* BFAD instance number */ + u32 bfad_flags; + spinlock_t bfad_lock; + struct bfad_cfg_param_s cfg_data; + struct bfad_msix_s msix_tab[MAX_MSIX_ENTRY]; + int nvec; + char adapter_name[BFA_ADAPTER_SYM_NAME_LEN]; + char port_name[BFA_ADAPTER_SYM_NAME_LEN]; + struct timer_list hal_tmo; + unsigned long hs_start; + struct bfad_im_s *im; /* IM specific data */ + struct bfad_tm_s *tm; /* TM specific data */ + struct bfad_ipfc_s *ipfc; /* IPFC specific data */ + struct bfa_log_mod_s log_data; + struct bfa_trc_mod_s *trcmod; + struct bfa_log_mod_s *logmod; + struct bfa_aen_s *aen; + struct bfa_aen_s aen_buf; + struct bfad_aen_file_s file_buf[BFAD_AEN_MAX_APPS]; + struct list_head file_q; + struct list_head file_free_q; + struct bfa_plog_s plog_buf; + int ref_count; + bfa_boolean_t ipfc_enabled; + struct fc_host_statistics link_stats; + + struct kobject *bfa_kobj; + struct kobject *ioc_kobj; + struct kobject *pport_kobj; + struct kobject *lport_kobj; +}; + +/* + * RPORT data structure + */ +struct bfad_rport_s { + struct bfa_fcs_rport_s fcs_rport; +}; + +struct bfad_buf_info { + void *virt; + dma_addr_t phys; + u32 size; +}; + +struct bfad_fcxp { + struct bfad_port_s *port; + struct bfa_rport_s *bfa_rport; + bfa_status_t req_status; + u16 tag; + u16 rsp_len; + u16 rsp_maxlen; + u8 use_ireqbuf; + u8 use_irspbuf; + u32 num_req_sgles; + u32 num_rsp_sgles; + struct fchs_s fchs; + void *reqbuf_info; + void *rspbuf_info; + struct bfa_sge_s *req_sge; + struct bfa_sge_s *rsp_sge; + fcxp_send_cb_t send_cbfn; + void *send_cbarg; + void *bfa_fcxp; + struct completion comp; +}; + +struct bfad_hal_comp { + bfa_status_t status; + struct completion comp; +}; + +/* + * Macro to obtain the immediate lower power + * of two for the integer. + */ +#define nextLowerInt(x) \ +do { \ + int j; \ + (*x)--; \ + for (j = 1; j < (sizeof(int) * 8); j <<= 1) \ + (*x) = (*x) | (*x) >> j; \ + (*x)++; \ + (*x) = (*x) >> 1; \ +} while (0) + + +bfa_status_t bfad_vport_create(struct bfad_s *bfad, u16 vf_id, + struct bfa_port_cfg_s *port_cfg); +bfa_status_t bfad_vf_create(struct bfad_s *bfad, u16 vf_id, + struct bfa_port_cfg_s *port_cfg); +bfa_status_t bfad_cfg_pport(struct bfad_s *bfad, enum bfa_port_role role); +bfa_status_t bfad_drv_init(struct bfad_s *bfad); +void bfad_drv_start(struct bfad_s *bfad); +void bfad_uncfg_pport(struct bfad_s *bfad); +void bfad_drv_stop(struct bfad_s *bfad); +void bfad_remove_intr(struct bfad_s *bfad); +void bfad_hal_mem_release(struct bfad_s *bfad); +void bfad_hcb_comp(void *arg, bfa_status_t status); + +int bfad_setup_intr(struct bfad_s *bfad); +void bfad_remove_intr(struct bfad_s *bfad); + +void bfad_update_hal_cfg(struct bfa_iocfc_cfg_s *bfa_cfg); +bfa_status_t bfad_hal_mem_alloc(struct bfad_s *bfad); +void bfad_bfa_tmo(unsigned long data); +void bfad_init_timer(struct bfad_s *bfad); +int bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad); +void bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad); +void bfad_fcs_port_cfg(struct bfad_s *bfad); +void bfad_drv_uninit(struct bfad_s *bfad); +void bfad_drv_log_level_set(struct bfad_s *bfad); +bfa_status_t bfad_fc4_module_init(void); +void bfad_fc4_module_exit(void); + +void bfad_pci_remove(struct pci_dev *pdev); +int bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid); +void bfad_os_rport_online_wait(struct bfad_s *bfad); +int bfad_os_get_linkup_delay(struct bfad_s *bfad); +int bfad_install_msix_handler(struct bfad_s *bfad); + +extern struct idr bfad_im_port_index; +extern struct list_head bfad_list; +extern int bfa_lun_queue_depth; +extern int bfad_supported_fc4s; +extern int bfa_linkup_delay; + +#endif /* __BFAD_DRV_H__ */ diff --git a/drivers/scsi/bfa/bfad_fwimg.c b/drivers/scsi/bfa/bfad_fwimg.c new file mode 100644 index 000000000000..b2f6949bc8d3 --- /dev/null +++ b/drivers/scsi/bfa/bfad_fwimg.c @@ -0,0 +1,95 @@ +/* + * 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_fwimg.c Linux driver PCI interface module. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +u32 bfi_image_ct_size; +u32 bfi_image_cb_size; +u32 *bfi_image_ct; +u32 *bfi_image_cb; + + +#define BFAD_FW_FILE_CT "ctfw.bin" +#define BFAD_FW_FILE_CB "cbfw.bin" + +u32 * +bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image, + u32 *bfi_image_size, char *fw_name) +{ + const struct firmware *fw; + + if (request_firmware(&fw, fw_name, &pdev->dev)) { + printk(KERN_ALERT "Can't locate firmware %s\n", fw_name); + goto error; + } + + *bfi_image = vmalloc(fw->size); + if (NULL == *bfi_image) { + printk(KERN_ALERT "Fail to allocate buffer for fw image " + "size=%x!\n", (u32) fw->size); + goto error; + } + + memcpy(*bfi_image, fw->data, fw->size); + *bfi_image_size = fw->size/sizeof(u32); + + return(*bfi_image); + +error: + return(NULL); +} + +u32 * +bfad_get_firmware_buf(struct pci_dev *pdev) +{ + if (pdev->device == BFA_PCI_DEVICE_ID_CT) { + if (bfi_image_ct_size == 0) + bfad_read_firmware(pdev, &bfi_image_ct, + &bfi_image_ct_size, BFAD_FW_FILE_CT); + return(bfi_image_ct); + } else { + if (bfi_image_cb_size == 0) + bfad_read_firmware(pdev, &bfi_image_cb, + &bfi_image_cb_size, BFAD_FW_FILE_CB); + return(bfi_image_cb); + } +} + +u32 * +bfi_image_ct_get_chunk(u32 off) +{ return (u32 *)(bfi_image_ct + off); } + +u32 * +bfi_image_cb_get_chunk(u32 off) +{ return (u32 *)(bfi_image_cb + off); } + diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c new file mode 100644 index 000000000000..158c99243c08 --- /dev/null +++ b/drivers/scsi/bfa/bfad_im.c @@ -0,0 +1,1230 @@ +/* + * 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_im.c Linux driver IM module. + */ + +#include "bfad_drv.h" +#include "bfad_im.h" +#include "bfad_trcmod.h" +#include "bfa_cb_ioim_macros.h" +#include + +BFA_TRC_FILE(LDRV, IM); + +DEFINE_IDR(bfad_im_port_index); +struct scsi_transport_template *bfad_im_scsi_transport_template; +static void bfad_im_itnim_work_handler(struct work_struct *work); +static int bfad_im_queuecommand(struct scsi_cmnd *cmnd, + void (*done)(struct scsi_cmnd *)); +static int bfad_im_slave_alloc(struct scsi_device *sdev); + +void +bfa_cb_ioim_done(void *drv, struct bfad_ioim_s *dio, + enum bfi_ioim_status io_status, u8 scsi_status, + int sns_len, u8 *sns_info, s32 residue) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + struct bfad_s *bfad = drv; + struct bfad_itnim_data_s *itnim_data; + struct bfad_itnim_s *itnim; + + switch (io_status) { + case BFI_IOIM_STS_OK: + bfa_trc(bfad, scsi_status); + cmnd->result = ScsiResult(DID_OK, scsi_status); + scsi_set_resid(cmnd, 0); + + if (sns_len > 0) { + bfa_trc(bfad, sns_len); + if (sns_len > SCSI_SENSE_BUFFERSIZE) + sns_len = SCSI_SENSE_BUFFERSIZE; + memcpy(cmnd->sense_buffer, sns_info, sns_len); + } + if (residue > 0) + scsi_set_resid(cmnd, residue); + break; + + case BFI_IOIM_STS_ABORTED: + case BFI_IOIM_STS_TIMEDOUT: + case BFI_IOIM_STS_PATHTOV: + default: + cmnd->result = ScsiResult(DID_ERROR, 0); + } + + /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ + if (cmnd->device->host != NULL) + scsi_dma_unmap(cmnd); + + cmnd->host_scribble = NULL; + bfa_trc(bfad, cmnd->result); + + itnim_data = cmnd->device->hostdata; + if (itnim_data) { + itnim = itnim_data->itnim; + if (!cmnd->result && itnim && + (bfa_lun_queue_depth > cmnd->device->queue_depth)) { + /* Queue depth adjustment for good status completion */ + bfad_os_ramp_up_qdepth(itnim, cmnd->device); + } else if (cmnd->result == SAM_STAT_TASK_SET_FULL && itnim) { + /* qfull handling */ + bfad_os_handle_qfull(itnim, cmnd->device); + } + } + + cmnd->scsi_done(cmnd); +} + +void +bfa_cb_ioim_good_comp(void *drv, struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + struct bfad_itnim_data_s *itnim_data; + struct bfad_itnim_s *itnim; + + cmnd->result = ScsiResult(DID_OK, SCSI_STATUS_GOOD); + + /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ + if (cmnd->device->host != NULL) + scsi_dma_unmap(cmnd); + + cmnd->host_scribble = NULL; + + /* Queue depth adjustment */ + if (bfa_lun_queue_depth > cmnd->device->queue_depth) { + itnim_data = cmnd->device->hostdata; + if (itnim_data) { + itnim = itnim_data->itnim; + if (itnim) + bfad_os_ramp_up_qdepth(itnim, cmnd->device); + } + } + + cmnd->scsi_done(cmnd); +} + +void +bfa_cb_ioim_abort(void *drv, struct bfad_ioim_s *dio) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; + struct bfad_s *bfad = drv; + + cmnd->result = ScsiResult(DID_ERROR, 0); + + /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ + if (cmnd->device->host != NULL) + scsi_dma_unmap(cmnd); + + bfa_trc(bfad, cmnd->result); + cmnd->host_scribble = NULL; +} + +void +bfa_cb_tskim_done(void *bfad, struct bfad_tskim_s *dtsk, + enum bfi_tskim_status tsk_status) +{ + struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dtsk; + wait_queue_head_t *wq; + + cmnd->SCp.Status |= tsk_status << 1; + set_bit(IO_DONE_BIT, (unsigned long *)&cmnd->SCp.Status); + wq = (wait_queue_head_t *) cmnd->SCp.ptr; + cmnd->SCp.ptr = NULL; + + if (wq) + wake_up(wq); +} + +void +bfa_cb_ioim_resfree(void *drv) +{ +} + +/** + * Scsi_Host_template SCSI host template + */ +/** + * Scsi_Host template entry, returns BFAD PCI info. + */ +static const char * +bfad_im_info(struct Scsi_Host *shost) +{ + static char bfa_buf[256]; + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfa_ioc_attr_s ioc_attr; + struct bfad_s *bfad = im_port->bfad; + + memset(&ioc_attr, 0, sizeof(ioc_attr)); + bfa_get_attr(&bfad->bfa, &ioc_attr); + + memset(bfa_buf, 0, sizeof(bfa_buf)); + snprintf(bfa_buf, sizeof(bfa_buf), + "Brocade FC/FCOE Adapter, " "model: %s hwpath: %s driver: %s", + ioc_attr.adapter_attr.model, bfad->pci_name, + BFAD_DRIVER_VERSION); + return bfa_buf; +} + +/** + * Scsi_Host template entry, aborts the specified SCSI command. + * + * Returns: SUCCESS or FAILED. + */ +static int +bfad_im_abort_handler(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_ioim_s *hal_io; + unsigned long flags; + u32 timeout; + int rc = FAILED; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + hal_io = (struct bfa_ioim_s *) cmnd->host_scribble; + if (!hal_io) { + /* IO has been completed, retrun success */ + rc = SUCCESS; + goto out; + } + if (hal_io->dio != (struct bfad_ioim_s *) cmnd) { + rc = FAILED; + goto out; + } + + bfa_trc(bfad, hal_io->iotag); + bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_ABORT, + im_port->shost->host_no, cmnd, hal_io->iotag); + bfa_ioim_abort(hal_io); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + /* Need to wait until the command get aborted */ + timeout = 10; + while ((struct bfa_ioim_s *) cmnd->host_scribble == hal_io) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(timeout); + if (timeout < 4 * HZ) + timeout *= 2; + } + + cmnd->scsi_done(cmnd); + bfa_trc(bfad, hal_io->iotag); + bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_ABORT_COMP, + im_port->shost->host_no, cmnd, hal_io->iotag); + return SUCCESS; +out: + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + return rc; +} + +static bfa_status_t +bfad_im_target_reset_send(struct bfad_s *bfad, struct scsi_cmnd *cmnd, + struct bfad_itnim_s *itnim) +{ + struct bfa_tskim_s *tskim; + struct bfa_itnim_s *bfa_itnim; + bfa_status_t rc = BFA_STATUS_OK; + + bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim); + tskim = bfa_tskim_alloc(&bfad->bfa, (struct bfad_tskim_s *) cmnd); + if (!tskim) { + BFA_DEV_PRINTF(bfad, BFA_ERR, + "target reset, fail to allocate tskim\n"); + rc = BFA_STATUS_FAILED; + goto out; + } + + /* + * Set host_scribble to NULL to avoid aborting a task command if + * happens. + */ + cmnd->host_scribble = NULL; + cmnd->SCp.Status = 0; + bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim); + bfa_tskim_start(tskim, bfa_itnim, (lun_t)0, + FCP_TM_TARGET_RESET, BFAD_TARGET_RESET_TMO); +out: + return rc; +} + +/** + * Scsi_Host template entry, resets a LUN and abort its all commands. + * + * Returns: SUCCESS or FAILED. + * + */ +static int +bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_itnim_data_s *itnim_data = cmnd->device->hostdata; + struct bfad_s *bfad = im_port->bfad; + struct bfa_tskim_s *tskim; + struct bfad_itnim_s *itnim; + struct bfa_itnim_s *bfa_itnim; + DECLARE_WAIT_QUEUE_HEAD(wq); + int rc = SUCCESS; + unsigned long flags; + enum bfi_tskim_status task_status; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + itnim = itnim_data->itnim; + if (!itnim) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + rc = FAILED; + goto out; + } + + tskim = bfa_tskim_alloc(&bfad->bfa, (struct bfad_tskim_s *) cmnd); + if (!tskim) { + BFA_DEV_PRINTF(bfad, BFA_ERR, + "LUN reset, fail to allocate tskim"); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + rc = FAILED; + goto out; + } + + /** + * Set host_scribble to NULL to avoid aborting a task command + * if happens. + */ + cmnd->host_scribble = NULL; + cmnd->SCp.ptr = (char *)&wq; + cmnd->SCp.Status = 0; + bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim); + bfa_tskim_start(tskim, bfa_itnim, + bfad_int_to_lun(cmnd->device->lun), + FCP_TM_LUN_RESET, BFAD_LUN_RESET_TMO); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + wait_event(wq, test_bit(IO_DONE_BIT, + (unsigned long *)&cmnd->SCp.Status)); + + task_status = cmnd->SCp.Status >> 1; + if (task_status != BFI_TSKIM_STS_OK) { + BFA_DEV_PRINTF(bfad, BFA_ERR, "LUN reset failure, status: %d\n", + task_status); + rc = FAILED; + } + +out: + return rc; +} + +/** + * Scsi_Host template entry, resets the bus and abort all commands. + */ +static int +bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfad_itnim_s *itnim; + unsigned long flags; + u32 i, rc, err_cnt = 0; + DECLARE_WAIT_QUEUE_HEAD(wq); + enum bfi_tskim_status task_status; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + for (i = 0; i < MAX_FCP_TARGET; i++) { + itnim = bfad_os_get_itnim(im_port, i); + if (itnim) { + cmnd->SCp.ptr = (char *)&wq; + rc = bfad_im_target_reset_send(bfad, cmnd, itnim); + if (rc != BFA_STATUS_OK) { + err_cnt++; + continue; + } + + /* wait target reset to complete */ + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + wait_event(wq, test_bit(IO_DONE_BIT, + (unsigned long *)&cmnd->SCp.Status)); + spin_lock_irqsave(&bfad->bfad_lock, flags); + + task_status = cmnd->SCp.Status >> 1; + if (task_status != BFI_TSKIM_STS_OK) { + BFA_DEV_PRINTF(bfad, BFA_ERR, + "target reset failure," + " status: %d\n", task_status); + err_cnt++; + } + } + } + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + if (err_cnt) + return FAILED; + + return SUCCESS; +} + +/** + * Scsi_Host template entry slave_destroy. + */ +static void +bfad_im_slave_destroy(struct scsi_device *sdev) +{ + sdev->hostdata = NULL; + return; +} + +/** + * BFA FCS itnim callbacks + */ + +/** + * BFA FCS itnim alloc callback, after successful PRLI + * Context: Interrupt + */ +void +bfa_fcb_itnim_alloc(struct bfad_s *bfad, struct bfa_fcs_itnim_s **itnim, + struct bfad_itnim_s **itnim_drv) +{ + *itnim_drv = kzalloc(sizeof(struct bfad_itnim_s), GFP_ATOMIC); + if (*itnim_drv == NULL) + return; + + (*itnim_drv)->im = bfad->im; + *itnim = &(*itnim_drv)->fcs_itnim; + (*itnim_drv)->state = ITNIM_STATE_NONE; + + /* + * Initiaze the itnim_work + */ + INIT_WORK(&(*itnim_drv)->itnim_work, bfad_im_itnim_work_handler); + bfad->bfad_flags |= BFAD_RPORT_ONLINE; +} + +/** + * BFA FCS itnim free callback. + * Context: Interrupt. bfad_lock is held + */ +void +bfa_fcb_itnim_free(struct bfad_s *bfad, struct bfad_itnim_s *itnim_drv) +{ + struct bfad_port_s *port; + wwn_t wwpn; + u32 fcid; + char wwpn_str[32], fcid_str[16]; + + /* online to free state transtion should not happen */ + bfa_assert(itnim_drv->state != ITNIM_STATE_ONLINE); + + itnim_drv->queue_work = 1; + /* offline request is not yet done, use the same request to free */ + if (itnim_drv->state == ITNIM_STATE_OFFLINE_PENDING) + itnim_drv->queue_work = 0; + + itnim_drv->state = ITNIM_STATE_FREE; + port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim); + itnim_drv->im_port = port->im_port; + wwpn = bfa_fcs_itnim_get_pwwn(&itnim_drv->fcs_itnim); + fcid = bfa_fcs_itnim_get_fcid(&itnim_drv->fcs_itnim); + wwn2str(wwpn_str, wwpn); + fcid2str(fcid_str, fcid); + bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_FREE, + port->im_port->shost->host_no, + fcid_str, wwpn_str); + bfad_os_itnim_process(itnim_drv); +} + +/** + * BFA FCS itnim online callback. + * Context: Interrupt. bfad_lock is held + */ +void +bfa_fcb_itnim_online(struct bfad_itnim_s *itnim_drv) +{ + struct bfad_port_s *port; + + itnim_drv->bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim_drv->fcs_itnim); + port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim); + itnim_drv->state = ITNIM_STATE_ONLINE; + itnim_drv->queue_work = 1; + itnim_drv->im_port = port->im_port; + bfad_os_itnim_process(itnim_drv); +} + +/** + * BFA FCS itnim offline callback. + * Context: Interrupt. bfad_lock is held + */ +void +bfa_fcb_itnim_offline(struct bfad_itnim_s *itnim_drv) +{ + struct bfad_port_s *port; + struct bfad_s *bfad; + + port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim); + bfad = port->bfad; + if ((bfad->pport.flags & BFAD_PORT_DELETE) || + (port->flags & BFAD_PORT_DELETE)) { + itnim_drv->state = ITNIM_STATE_OFFLINE; + return; + } + itnim_drv->im_port = port->im_port; + itnim_drv->state = ITNIM_STATE_OFFLINE_PENDING; + itnim_drv->queue_work = 1; + bfad_os_itnim_process(itnim_drv); +} + +/** + * BFA FCS itnim timeout callback. + * Context: Interrupt. bfad_lock is held + */ +void bfa_fcb_itnim_tov(struct bfad_itnim_s *itnim) +{ + itnim->state = ITNIM_STATE_TIMEOUT; +} + +/** + * Path TOV processing begin notification -- dummy for linux + */ +void +bfa_fcb_itnim_tov_begin(struct bfad_itnim_s *itnim) +{ +} + + + +/** + * Allocate a Scsi_Host for a port. + */ +int +bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port) +{ + int error = 1; + + if (!idr_pre_get(&bfad_im_port_index, GFP_KERNEL)) { + printk(KERN_WARNING "idr_pre_get failure\n"); + goto out; + } + + error = idr_get_new(&bfad_im_port_index, im_port, + &im_port->idr_id); + if (error) { + printk(KERN_WARNING "idr_get_new failure\n"); + goto out; + } + + im_port->shost = bfad_os_scsi_host_alloc(im_port, bfad); + if (!im_port->shost) { + error = 1; + goto out_free_idr; + } + + im_port->shost->hostdata[0] = (unsigned long)im_port; + im_port->shost->unique_id = im_port->idr_id; + im_port->shost->this_id = -1; + im_port->shost->max_id = MAX_FCP_TARGET; + im_port->shost->max_lun = MAX_FCP_LUN; + im_port->shost->max_cmd_len = 16; + im_port->shost->can_queue = bfad->cfg_data.ioc_queue_depth; + im_port->shost->transportt = bfad_im_scsi_transport_template; + + error = bfad_os_scsi_add_host(im_port->shost, im_port, bfad); + if (error) { + printk(KERN_WARNING "bfad_os_scsi_add_host failure %d\n", + error); + goto out_fc_rel; + } + + /* setup host fixed attribute if the lk supports */ + bfad_os_fc_host_init(im_port); + + return 0; + +out_fc_rel: + scsi_host_put(im_port->shost); +out_free_idr: + idr_remove(&bfad_im_port_index, im_port->idr_id); +out: + return error; +} + +void +bfad_im_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port) +{ + unsigned long flags; + + bfa_trc(bfad, bfad->inst_no); + bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_HOST_FREE, + im_port->shost->host_no); + + fc_remove_host(im_port->shost); + + scsi_remove_host(im_port->shost); + scsi_host_put(im_port->shost); + + spin_lock_irqsave(&bfad->bfad_lock, flags); + idr_remove(&bfad_im_port_index, im_port->idr_id); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +static void +bfad_im_port_delete_handler(struct work_struct *work) +{ + struct bfad_im_port_s *im_port = + container_of(work, struct bfad_im_port_s, port_delete_work); + + bfad_im_scsi_host_free(im_port->bfad, im_port); + bfad_im_port_clean(im_port); + kfree(im_port); +} + +bfa_status_t +bfad_im_port_new(struct bfad_s *bfad, struct bfad_port_s *port) +{ + int rc = BFA_STATUS_OK; + struct bfad_im_port_s *im_port; + + im_port = kzalloc(sizeof(struct bfad_im_port_s), GFP_ATOMIC); + if (im_port == NULL) { + rc = BFA_STATUS_ENOMEM; + goto ext; + } + port->im_port = im_port; + im_port->port = port; + im_port->bfad = bfad; + + INIT_WORK(&im_port->port_delete_work, bfad_im_port_delete_handler); + INIT_LIST_HEAD(&im_port->itnim_mapped_list); + INIT_LIST_HEAD(&im_port->binding_list); + +ext: + return rc; +} + +void +bfad_im_port_delete(struct bfad_s *bfad, struct bfad_port_s *port) +{ + struct bfad_im_port_s *im_port = port->im_port; + + queue_work(bfad->im->drv_workq, + &im_port->port_delete_work); +} + +void +bfad_im_port_clean(struct bfad_im_port_s *im_port) +{ + struct bfad_fcp_binding *bp, *bp_new; + unsigned long flags; + struct bfad_s *bfad = im_port->bfad; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + list_for_each_entry_safe(bp, bp_new, &im_port->binding_list, + list_entry) { + list_del(&bp->list_entry); + kfree(bp); + } + + /* the itnim_mapped_list must be empty at this time */ + bfa_assert(list_empty(&im_port->itnim_mapped_list)); + + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +void +bfad_im_port_online(struct bfad_s *bfad, struct bfad_port_s *port) +{ +} + +void +bfad_im_port_offline(struct bfad_s *bfad, struct bfad_port_s *port) +{ +} + +bfa_status_t +bfad_im_probe(struct bfad_s *bfad) +{ + struct bfad_im_s *im; + bfa_status_t rc = BFA_STATUS_OK; + + im = kzalloc(sizeof(struct bfad_im_s), GFP_KERNEL); + if (im == NULL) { + rc = BFA_STATUS_ENOMEM; + goto ext; + } + + bfad->im = im; + im->bfad = bfad; + + if (bfad_os_thread_workq(bfad) != BFA_STATUS_OK) { + kfree(im); + rc = BFA_STATUS_FAILED; + } + +ext: + return rc; +} + +void +bfad_im_probe_undo(struct bfad_s *bfad) +{ + if (bfad->im) { + bfad_os_destroy_workq(bfad->im); + kfree(bfad->im); + bfad->im = NULL; + } +} + + + + +int +bfad_os_scsi_add_host(struct Scsi_Host *shost, struct bfad_im_port_s *im_port, + struct bfad_s *bfad) +{ + struct device *dev; + + if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE) + dev = &bfad->pcidev->dev; + else + dev = &bfad->pport.im_port->shost->shost_gendev; + + return scsi_add_host(shost, dev); +} + +struct Scsi_Host * +bfad_os_scsi_host_alloc(struct bfad_im_port_s *im_port, struct bfad_s *bfad) +{ + struct scsi_host_template *sht; + + if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE) + sht = &bfad_im_scsi_host_template; + else + sht = &bfad_im_vport_template; + + sht->sg_tablesize = bfad->cfg_data.io_max_sge; + + return scsi_host_alloc(sht, sizeof(unsigned long)); +} + +void +bfad_os_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port) +{ + flush_workqueue(bfad->im->drv_workq); + bfad_im_scsi_host_free(im_port->bfad, im_port); + bfad_im_port_clean(im_port); + kfree(im_port); +} + +void +bfad_os_destroy_workq(struct bfad_im_s *im) +{ + if (im && im->drv_workq) { + destroy_workqueue(im->drv_workq); + im->drv_workq = NULL; + } +} + +bfa_status_t +bfad_os_thread_workq(struct bfad_s *bfad) +{ + struct bfad_im_s *im = bfad->im; + + bfa_trc(bfad, 0); + snprintf(im->drv_workq_name, BFAD_KOBJ_NAME_LEN, "bfad_wq_%d", + bfad->inst_no); + im->drv_workq = create_singlethread_workqueue(im->drv_workq_name); + if (!im->drv_workq) + return BFA_STATUS_FAILED; + + return BFA_STATUS_OK; +} + +/** + * Scsi_Host template entry. + * + * Description: + * OS entry point to adjust the queue_depths on a per-device basis. + * Called once per device during the bus scan. + * Return non-zero if fails. + */ +static int +bfad_im_slave_configure(struct scsi_device *sdev) +{ + if (sdev->tagged_supported) + scsi_activate_tcq(sdev, bfa_lun_queue_depth); + else + scsi_deactivate_tcq(sdev, bfa_lun_queue_depth); + + return 0; +} + +struct scsi_host_template bfad_im_scsi_host_template = { + .module = THIS_MODULE, + .name = BFAD_DRIVER_NAME, + .info = bfad_im_info, + .queuecommand = bfad_im_queuecommand, + .eh_abort_handler = bfad_im_abort_handler, + .eh_device_reset_handler = bfad_im_reset_lun_handler, + .eh_bus_reset_handler = bfad_im_reset_bus_handler, + + .slave_alloc = bfad_im_slave_alloc, + .slave_configure = bfad_im_slave_configure, + .slave_destroy = bfad_im_slave_destroy, + + .this_id = -1, + .sg_tablesize = BFAD_IO_MAX_SGE, + .cmd_per_lun = 3, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = bfad_im_host_attrs, + .max_sectors = 0xFFFF, +}; + +struct scsi_host_template bfad_im_vport_template = { + .module = THIS_MODULE, + .name = BFAD_DRIVER_NAME, + .info = bfad_im_info, + .queuecommand = bfad_im_queuecommand, + .eh_abort_handler = bfad_im_abort_handler, + .eh_device_reset_handler = bfad_im_reset_lun_handler, + .eh_bus_reset_handler = bfad_im_reset_bus_handler, + + .slave_alloc = bfad_im_slave_alloc, + .slave_configure = bfad_im_slave_configure, + .slave_destroy = bfad_im_slave_destroy, + + .this_id = -1, + .sg_tablesize = BFAD_IO_MAX_SGE, + .cmd_per_lun = 3, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = bfad_im_vport_attrs, + .max_sectors = 0xFFFF, +}; + +void +bfad_im_probe_post(struct bfad_im_s *im) +{ + flush_workqueue(im->drv_workq); +} + +bfa_status_t +bfad_im_module_init(void) +{ + bfad_im_scsi_transport_template = + fc_attach_transport(&bfad_im_fc_function_template); + if (!bfad_im_scsi_transport_template) + return BFA_STATUS_ENOMEM; + + return BFA_STATUS_OK; +} + +void +bfad_im_module_exit(void) +{ + if (bfad_im_scsi_transport_template) + fc_release_transport(bfad_im_scsi_transport_template); +} + +void +bfad_os_itnim_process(struct bfad_itnim_s *itnim_drv) +{ + struct bfad_im_s *im = itnim_drv->im; + + if (itnim_drv->queue_work) + queue_work(im->drv_workq, &itnim_drv->itnim_work); +} + +void +bfad_os_ramp_up_qdepth(struct bfad_itnim_s *itnim, struct scsi_device *sdev) +{ + struct scsi_device *tmp_sdev; + + if (((jiffies - itnim->last_ramp_up_time) > + BFA_QUEUE_FULL_RAMP_UP_TIME * HZ) && + ((jiffies - itnim->last_queue_full_time) > + BFA_QUEUE_FULL_RAMP_UP_TIME * HZ)) { + shost_for_each_device(tmp_sdev, sdev->host) { + if (bfa_lun_queue_depth > tmp_sdev->queue_depth) { + if (tmp_sdev->id != sdev->id) + continue; + if (tmp_sdev->ordered_tags) + scsi_adjust_queue_depth(tmp_sdev, + MSG_ORDERED_TAG, + tmp_sdev->queue_depth + 1); + else + scsi_adjust_queue_depth(tmp_sdev, + MSG_SIMPLE_TAG, + tmp_sdev->queue_depth + 1); + + itnim->last_ramp_up_time = jiffies; + } + } + } +} + +void +bfad_os_handle_qfull(struct bfad_itnim_s *itnim, struct scsi_device *sdev) +{ + struct scsi_device *tmp_sdev; + + itnim->last_queue_full_time = jiffies; + + shost_for_each_device(tmp_sdev, sdev->host) { + if (tmp_sdev->id != sdev->id) + continue; + scsi_track_queue_full(tmp_sdev, tmp_sdev->queue_depth - 1); + } +} + + + + +struct bfad_itnim_s * +bfad_os_get_itnim(struct bfad_im_port_s *im_port, int id) +{ + struct bfad_itnim_s *itnim = NULL; + + /* Search the mapped list for this target ID */ + list_for_each_entry(itnim, &im_port->itnim_mapped_list, list_entry) { + if (id == itnim->scsi_tgt_id) + return itnim; + } + + return NULL; +} + +/** + * Scsi_Host template entry slave_alloc + */ +static int +bfad_im_slave_alloc(struct scsi_device *sdev) +{ + struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); + + if (!rport || fc_remote_port_chkready(rport)) + return -ENXIO; + + sdev->hostdata = rport->dd_data; + + return 0; +} + +void +bfad_os_fc_host_init(struct bfad_im_port_s *im_port) +{ + struct Scsi_Host *host = im_port->shost; + struct bfad_s *bfad = im_port->bfad; + struct bfad_port_s *port = im_port->port; + union attr { + struct bfa_pport_attr_s pattr; + struct bfa_ioc_attr_s ioc_attr; + } attr; + + fc_host_node_name(host) = + bfa_os_htonll((bfa_fcs_port_get_nwwn(port->fcs_port))); + fc_host_port_name(host) = + bfa_os_htonll((bfa_fcs_port_get_pwwn(port->fcs_port))); + + fc_host_supported_classes(host) = FC_COS_CLASS3; + + memset(fc_host_supported_fc4s(host), 0, + sizeof(fc_host_supported_fc4s(host))); + if (bfad_supported_fc4s & (BFA_PORT_ROLE_FCP_IM | BFA_PORT_ROLE_FCP_TM)) + /* For FCP type 0x08 */ + fc_host_supported_fc4s(host)[2] = 1; + if (bfad_supported_fc4s | BFA_PORT_ROLE_FCP_IPFC) + /* For LLC/SNAP type 0x05 */ + fc_host_supported_fc4s(host)[3] = 0x20; + /* For fibre channel services type 0x20 */ + fc_host_supported_fc4s(host)[7] = 1; + + memset(&attr.ioc_attr, 0, sizeof(attr.ioc_attr)); + bfa_get_attr(&bfad->bfa, &attr.ioc_attr); + sprintf(fc_host_symbolic_name(host), "Brocade %s FV%s DV%s", + attr.ioc_attr.adapter_attr.model, + attr.ioc_attr.adapter_attr.fw_ver, BFAD_DRIVER_VERSION); + + fc_host_supported_speeds(host) = 0; + fc_host_supported_speeds(host) |= + FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT | FC_PORTSPEED_2GBIT | + FC_PORTSPEED_1GBIT; + + memset(&attr.pattr, 0, sizeof(attr.pattr)); + bfa_pport_get_attr(&bfad->bfa, &attr.pattr); + fc_host_maxframe_size(host) = attr.pattr.pport_cfg.maxfrsize; +} + +static void +bfad_im_fc_rport_add(struct bfad_im_port_s *im_port, struct bfad_itnim_s *itnim) +{ + struct fc_rport_identifiers rport_ids; + struct fc_rport *fc_rport; + struct bfad_itnim_data_s *itnim_data; + + rport_ids.node_name = + bfa_os_htonll(bfa_fcs_itnim_get_nwwn(&itnim->fcs_itnim)); + rport_ids.port_name = + bfa_os_htonll(bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim)); + rport_ids.port_id = + bfa_os_hton3b(bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim)); + rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; + + itnim->fc_rport = fc_rport = + fc_remote_port_add(im_port->shost, 0, &rport_ids); + + if (!fc_rport) + return; + + fc_rport->maxframe_size = + bfa_fcs_itnim_get_maxfrsize(&itnim->fcs_itnim); + fc_rport->supported_classes = bfa_fcs_itnim_get_cos(&itnim->fcs_itnim); + + itnim_data = fc_rport->dd_data; + itnim_data->itnim = itnim; + + rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET; + + if (rport_ids.roles != FC_RPORT_ROLE_UNKNOWN) + fc_remote_port_rolechg(fc_rport, rport_ids.roles); + + if ((fc_rport->scsi_target_id != -1) + && (fc_rport->scsi_target_id < MAX_FCP_TARGET)) + itnim->scsi_tgt_id = fc_rport->scsi_target_id; + + return; +} + +/** + * Work queue handler using FC transport service +* Context: kernel + */ +static void +bfad_im_itnim_work_handler(struct work_struct *work) +{ + struct bfad_itnim_s *itnim = container_of(work, struct bfad_itnim_s, + itnim_work); + struct bfad_im_s *im = itnim->im; + struct bfad_s *bfad = im->bfad; + struct bfad_im_port_s *im_port; + unsigned long flags; + struct fc_rport *fc_rport; + wwn_t wwpn; + u32 fcid; + char wwpn_str[32], fcid_str[16]; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + im_port = itnim->im_port; + bfa_trc(bfad, itnim->state); + switch (itnim->state) { + case ITNIM_STATE_ONLINE: + if (!itnim->fc_rport) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + bfad_im_fc_rport_add(im_port, itnim); + spin_lock_irqsave(&bfad->bfad_lock, flags); + wwpn = bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim); + fcid = bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim); + wwn2str(wwpn_str, wwpn); + fcid2str(fcid_str, fcid); + list_add_tail(&itnim->list_entry, + &im_port->itnim_mapped_list); + bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_ONLINE, + im_port->shost->host_no, + itnim->scsi_tgt_id, + fcid_str, wwpn_str); + } else { + printk(KERN_WARNING + "%s: itnim %llx is already in online state\n", + __FUNCTION__, + bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim)); + } + + break; + case ITNIM_STATE_OFFLINE_PENDING: + itnim->state = ITNIM_STATE_OFFLINE; + if (itnim->fc_rport) { + fc_rport = itnim->fc_rport; + ((struct bfad_itnim_data_s *) + fc_rport->dd_data)->itnim = NULL; + itnim->fc_rport = NULL; + if (!(im_port->port->flags & BFAD_PORT_DELETE)) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + fc_rport->dev_loss_tmo = + bfa_fcpim_path_tov_get(&bfad->bfa) + 1; + fc_remote_port_delete(fc_rport); + spin_lock_irqsave(&bfad->bfad_lock, flags); + } + wwpn = bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim); + fcid = bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim); + wwn2str(wwpn_str, wwpn); + fcid2str(fcid_str, fcid); + list_del(&itnim->list_entry); + bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_OFFLINE, + im_port->shost->host_no, + itnim->scsi_tgt_id, + fcid_str, wwpn_str); + } + break; + case ITNIM_STATE_FREE: + if (itnim->fc_rport) { + fc_rport = itnim->fc_rport; + ((struct bfad_itnim_data_s *) + fc_rport->dd_data)->itnim = NULL; + itnim->fc_rport = NULL; + if (!(im_port->port->flags & BFAD_PORT_DELETE)) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + fc_rport->dev_loss_tmo = + bfa_fcpim_path_tov_get(&bfad->bfa) + 1; + fc_remote_port_delete(fc_rport); + spin_lock_irqsave(&bfad->bfad_lock, flags); + } + list_del(&itnim->list_entry); + } + + kfree(itnim); + break; + default: + bfa_assert(0); + break; + } + + spin_unlock_irqrestore(&bfad->bfad_lock, flags); +} + +/** + * Scsi_Host template entry, queue a SCSI command to the BFAD. + */ +static int +bfad_im_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) +{ + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) cmnd->device->host->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfad_itnim_data_s *itnim_data = cmnd->device->hostdata; + struct bfad_itnim_s *itnim; + struct bfa_ioim_s *hal_io; + unsigned long flags; + int rc; + s16 sg_cnt = 0; + struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); + + rc = fc_remote_port_chkready(rport); + if (rc) { + cmnd->result = rc; + done(cmnd); + return 0; + } + + sg_cnt = scsi_dma_map(cmnd); + + if (sg_cnt < 0) + return SCSI_MLQUEUE_HOST_BUSY; + + cmnd->scsi_done = done; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + if (!(bfad->bfad_flags & BFAD_HAL_START_DONE)) { + printk(KERN_WARNING + "bfad%d, queuecommand %p %x failed, BFA stopped\n", + bfad->inst_no, cmnd, cmnd->cmnd[0]); + cmnd->result = ScsiResult(DID_NO_CONNECT, 0); + goto out_fail_cmd; + } + + itnim = itnim_data->itnim; + if (!itnim) { + cmnd->result = ScsiResult(DID_IMM_RETRY, 0); + goto out_fail_cmd; + } + + hal_io = bfa_ioim_alloc(&bfad->bfa, (struct bfad_ioim_s *) cmnd, + itnim->bfa_itnim, sg_cnt); + if (!hal_io) { + printk(KERN_WARNING "hal_io failure\n"); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + scsi_dma_unmap(cmnd); + return SCSI_MLQUEUE_HOST_BUSY; + } + + cmnd->host_scribble = (char *)hal_io; + bfa_trc_fp(bfad, hal_io->iotag); + bfa_ioim_start(hal_io); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + return 0; + +out_fail_cmd: + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + scsi_dma_unmap(cmnd); + if (done) + done(cmnd); + + return 0; +} + +void +bfad_os_rport_online_wait(struct bfad_s *bfad) +{ + int i; + int rport_delay = 10; + + for (i = 0; !(bfad->bfad_flags & BFAD_PORT_ONLINE) + && i < bfa_linkup_delay; i++) + schedule_timeout_uninterruptible(HZ); + + if (bfad->bfad_flags & BFAD_PORT_ONLINE) { + rport_delay = rport_delay < bfa_linkup_delay ? + rport_delay : bfa_linkup_delay; + for (i = 0; !(bfad->bfad_flags & BFAD_RPORT_ONLINE) + && i < rport_delay; i++) + schedule_timeout_uninterruptible(HZ); + + if (rport_delay > 0 && (bfad->bfad_flags & BFAD_RPORT_ONLINE)) + schedule_timeout_uninterruptible(rport_delay * HZ); + } +} + +int +bfad_os_get_linkup_delay(struct bfad_s *bfad) +{ + + u8 nwwns = 0; + wwn_t *wwns; + int ldelay; + + /* + * Querying for the boot target port wwns + * -- read from boot information in flash. + * If nwwns > 0 => boot over SAN and set bfa_linkup_delay = 30 + * else => local boot machine set bfa_linkup_delay = 10 + */ + + bfa_iocfc_get_bootwwns(&bfad->bfa, &nwwns, &wwns); + + if (nwwns > 0) { + /* If boot over SAN; linkup_delay = 30sec */ + ldelay = 30; + } else { + /* If local boot; linkup_delay = 10sec */ + ldelay = 0; + } + + return ldelay; +} + + diff --git a/drivers/scsi/bfa/bfad_im.h b/drivers/scsi/bfa/bfad_im.h new file mode 100644 index 000000000000..189a5b29e21a --- /dev/null +++ b/drivers/scsi/bfa/bfad_im.h @@ -0,0 +1,150 @@ +/* + * 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. + */ + +#ifndef __BFAD_IM_H__ +#define __BFAD_IM_H__ + +#include "fcs/bfa_fcs_fcpim.h" +#include "bfad_im_compat.h" + +#define FCPI_NAME " fcpim" + +void bfad_flags_set(struct bfad_s *bfad, u32 flags); +bfa_status_t bfad_im_module_init(void); +void bfad_im_module_exit(void); +bfa_status_t bfad_im_probe(struct bfad_s *bfad); +void bfad_im_probe_undo(struct bfad_s *bfad); +void bfad_im_probe_post(struct bfad_im_s *im); +bfa_status_t bfad_im_port_new(struct bfad_s *bfad, struct bfad_port_s *port); +void bfad_im_port_delete(struct bfad_s *bfad, struct bfad_port_s *port); +void bfad_im_port_online(struct bfad_s *bfad, struct bfad_port_s *port); +void bfad_im_port_offline(struct bfad_s *bfad, struct bfad_port_s *port); +void bfad_im_port_clean(struct bfad_im_port_s *im_port); +int bfad_im_scsi_host_alloc(struct bfad_s *bfad, + struct bfad_im_port_s *im_port); +void bfad_im_scsi_host_free(struct bfad_s *bfad, + struct bfad_im_port_s *im_port); + +#define MAX_FCP_TARGET 1024 +#define MAX_FCP_LUN 16384 +#define BFAD_TARGET_RESET_TMO 60 +#define BFAD_LUN_RESET_TMO 60 +#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code) +#define BFA_QUEUE_FULL_RAMP_UP_TIME 120 +#define BFAD_KOBJ_NAME_LEN 20 + +/* + * itnim flags + */ +#define ITNIM_MAPPED 0x00000001 + +#define SCSI_TASK_MGMT 0x00000001 +#define IO_DONE_BIT 0 + +struct bfad_itnim_data_s { + struct bfad_itnim_s *itnim; +}; + +struct bfad_im_port_s { + struct bfad_s *bfad; + struct bfad_port_s *port; + struct work_struct port_delete_work; + int idr_id; + u16 cur_scsi_id; + struct list_head binding_list; + struct Scsi_Host *shost; + struct list_head itnim_mapped_list; +}; + +enum bfad_itnim_state { + ITNIM_STATE_NONE, + ITNIM_STATE_ONLINE, + ITNIM_STATE_OFFLINE_PENDING, + ITNIM_STATE_OFFLINE, + ITNIM_STATE_TIMEOUT, + ITNIM_STATE_FREE, +}; + +/* + * Per itnim data structure + */ +struct bfad_itnim_s { + struct list_head list_entry; + struct bfa_fcs_itnim_s fcs_itnim; + struct work_struct itnim_work; + u32 flags; + enum bfad_itnim_state state; + struct bfad_im_s *im; + struct bfad_im_port_s *im_port; + struct bfad_rport_s *drv_rport; + struct fc_rport *fc_rport; + struct bfa_itnim_s *bfa_itnim; + u16 scsi_tgt_id; + u16 queue_work; + unsigned long last_ramp_up_time; + unsigned long last_queue_full_time; +}; + +enum bfad_binding_type { + FCP_PWWN_BINDING = 0x1, + FCP_NWWN_BINDING = 0x2, + FCP_FCID_BINDING = 0x3, +}; + +struct bfad_fcp_binding { + struct list_head list_entry; + enum bfad_binding_type binding_type; + u16 scsi_target_id; + u32 fc_id; + wwn_t nwwn; + wwn_t pwwn; +}; + +struct bfad_im_s { + struct bfad_s *bfad; + struct workqueue_struct *drv_workq; + char drv_workq_name[BFAD_KOBJ_NAME_LEN]; +}; + +struct Scsi_Host *bfad_os_scsi_host_alloc(struct bfad_im_port_s *im_port, + struct bfad_s *); +bfa_status_t bfad_os_thread_workq(struct bfad_s *bfad); +void bfad_os_destroy_workq(struct bfad_im_s *im); +void bfad_os_itnim_process(struct bfad_itnim_s *itnim_drv); +void bfad_os_fc_host_init(struct bfad_im_port_s *im_port); +void bfad_os_init_work(struct bfad_im_port_s *im_port); +void bfad_os_scsi_host_free(struct bfad_s *bfad, + struct bfad_im_port_s *im_port); +void bfad_os_ramp_up_qdepth(struct bfad_itnim_s *itnim, + struct scsi_device *sdev); +void bfad_os_handle_qfull(struct bfad_itnim_s *itnim, struct scsi_device *sdev); +struct bfad_itnim_s *bfad_os_get_itnim(struct bfad_im_port_s *im_port, int id); +int bfad_os_scsi_add_host(struct Scsi_Host *shost, + struct bfad_im_port_s *im_port, struct bfad_s *bfad); + +/* + * scsi_host_template entries + */ +void bfad_im_itnim_unmap(struct bfad_im_port_s *im_port, + struct bfad_itnim_s *itnim); + +extern struct scsi_host_template bfad_im_scsi_host_template; +extern struct scsi_host_template bfad_im_vport_template; +extern struct fc_function_template bfad_im_fc_function_template; +extern struct scsi_transport_template *bfad_im_scsi_transport_template; + +#endif diff --git a/drivers/scsi/bfa/bfad_im_compat.h b/drivers/scsi/bfa/bfad_im_compat.h new file mode 100644 index 000000000000..1d3e74ec338c --- /dev/null +++ b/drivers/scsi/bfa/bfad_im_compat.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef __BFAD_IM_COMPAT_H__ +#define __BFAD_IM_COMPAT_H__ + +extern u32 *bfi_image_buf; +extern u32 bfi_image_size; + +extern struct device_attribute *bfad_im_host_attrs[]; +extern struct device_attribute *bfad_im_vport_attrs[]; + +u32 *bfad_get_firmware_buf(struct pci_dev *pdev); +u32 *bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image, + u32 *bfi_image_size, char *fw_name); + +static inline u32 * +bfad_load_fwimg(struct pci_dev *pdev) +{ + return(bfad_get_firmware_buf(pdev)); +} + +static inline void +bfad_free_fwimg(void) +{ + if (bfi_image_ct_size && bfi_image_ct) + vfree(bfi_image_ct); + if (bfi_image_cb_size && bfi_image_cb) + vfree(bfi_image_cb); +} + +#endif diff --git a/drivers/scsi/bfa/bfad_intr.c b/drivers/scsi/bfa/bfad_intr.c new file mode 100644 index 000000000000..f104e029cac9 --- /dev/null +++ b/drivers/scsi/bfa/bfad_intr.c @@ -0,0 +1,214 @@ +/* + * 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. + */ + +#include "bfad_drv.h" +#include "bfad_trcmod.h" + +BFA_TRC_FILE(LDRV, INTR); + +/** + * bfa_isr BFA driver interrupt functions + */ +irqreturn_t bfad_intx(int irq, void *dev_id); +static int msix_disable; +module_param(msix_disable, int, S_IRUGO | S_IWUSR); +/** + * Line based interrupt handler. + */ +irqreturn_t +bfad_intx(int irq, void *dev_id) +{ + struct bfad_s *bfad = dev_id; + struct list_head doneq; + unsigned long flags; + bfa_boolean_t rc; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + rc = bfa_intx(&bfad->bfa); + if (!rc) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + return IRQ_NONE; + } + + 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); + bfa_trc_fp(bfad, irq); + } + + return IRQ_HANDLED; + +} + +static irqreturn_t +bfad_msix(int irq, void *dev_id) +{ + struct bfad_msix_s *vec = dev_id; + struct bfad_s *bfad = vec->bfad; + struct list_head doneq; + unsigned long flags; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + + bfa_msix(&bfad->bfa, vec->msix.entry); + 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); + } + + return IRQ_HANDLED; +} + +/** + * Initialize the MSIX entry table. + */ +static void +bfad_init_msix_entry(struct bfad_s *bfad, struct msix_entry *msix_entries, + int mask, int max_bit) +{ + int i; + int match = 0x00000001; + + for (i = 0, bfad->nvec = 0; i < MAX_MSIX_ENTRY; i++) { + if (mask & match) { + bfad->msix_tab[bfad->nvec].msix.entry = i; + bfad->msix_tab[bfad->nvec].bfad = bfad; + msix_entries[bfad->nvec].entry = i; + bfad->nvec++; + } + + match <<= 1; + } + +} + +int +bfad_install_msix_handler(struct bfad_s *bfad) +{ + int i, error = 0; + + for (i = 0; i < bfad->nvec; i++) { + error = request_irq(bfad->msix_tab[i].msix.vector, + (irq_handler_t) bfad_msix, 0, + BFAD_DRIVER_NAME, &bfad->msix_tab[i]); + bfa_trc(bfad, i); + bfa_trc(bfad, bfad->msix_tab[i].msix.vector); + if (error) { + int j; + + for (j = 0; j < i; j++) + free_irq(bfad->msix_tab[j].msix.vector, + &bfad->msix_tab[j]); + + return 1; + } + } + + return 0; +} + +/** + * Setup MSIX based interrupt. + */ +int +bfad_setup_intr(struct bfad_s *bfad) +{ + int error = 0; + u32 mask = 0, i, num_bit = 0, max_bit = 0; + struct msix_entry msix_entries[MAX_MSIX_ENTRY]; + + /* Call BFA to get the msix map for this PCI function. */ + bfa_msix_getvecs(&bfad->bfa, &mask, &num_bit, &max_bit); + + /* Set up the msix entry table */ + bfad_init_msix_entry(bfad, msix_entries, mask, max_bit); + + if (!msix_disable) { + error = pci_enable_msix(bfad->pcidev, msix_entries, bfad->nvec); + if (error) { + /* + * Only error number of vector is available. + * We don't have a mechanism to map multiple + * interrupts into one vector, so even if we + * can try to request less vectors, we don't + * know how to associate interrupt events to + * vectors. Linux doesn't dupicate vectors + * in the MSIX table for this case. + */ + + printk(KERN_WARNING "bfad%d: " + "pci_enable_msix failed (%d)," + " use line based.\n", bfad->inst_no, error); + + goto line_based; + } + + /* Save the vectors */ + for (i = 0; i < bfad->nvec; i++) { + bfa_trc(bfad, msix_entries[i].vector); + bfad->msix_tab[i].msix.vector = msix_entries[i].vector; + } + + bfa_msix_init(&bfad->bfa, bfad->nvec); + + bfad->bfad_flags |= BFAD_MSIX_ON; + + return error; + } + +line_based: + error = 0; + if (request_irq + (bfad->pcidev->irq, (irq_handler_t) bfad_intx, BFAD_IRQ_FLAGS, + BFAD_DRIVER_NAME, bfad) != 0) { + /* Enable interrupt handler failed */ + return 1; + } + + return error; +} + +void +bfad_remove_intr(struct bfad_s *bfad) +{ + int i; + + if (bfad->bfad_flags & BFAD_MSIX_ON) { + for (i = 0; i < bfad->nvec; i++) + free_irq(bfad->msix_tab[i].msix.vector, + &bfad->msix_tab[i]); + + pci_disable_msix(bfad->pcidev); + bfad->bfad_flags &= ~BFAD_MSIX_ON; + } else { + free_irq(bfad->pcidev->irq, bfad); + } +} + + diff --git a/drivers/scsi/bfa/bfad_ipfc.h b/drivers/scsi/bfa/bfad_ipfc.h new file mode 100644 index 000000000000..718bc5227671 --- /dev/null +++ b/drivers/scsi/bfa/bfad_ipfc.h @@ -0,0 +1,42 @@ +/* + * 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. + */ +#ifndef __BFA_DRV_IPFC_H__ +#define __BFA_DRV_IPFC_H__ + + +#define IPFC_NAME "" + +#define bfad_ipfc_module_init(x) do {} while (0) +#define bfad_ipfc_module_exit(x) do {} while (0) +#define bfad_ipfc_probe(x) do {} while (0) +#define bfad_ipfc_probe_undo(x) do {} while (0) +#define bfad_ipfc_port_config(x, y) BFA_STATUS_OK +#define bfad_ipfc_port_unconfig(x, y) do {} while (0) + +#define bfad_ipfc_probe_post(x) do {} while (0) +#define bfad_ipfc_port_new(x, y, z) BFA_STATUS_OK +#define bfad_ipfc_port_delete(x, y) do {} while (0) +#define bfad_ipfc_port_online(x, y) do {} while (0) +#define bfad_ipfc_port_offline(x, y) do {} while (0) + +#define bfad_ip_get_attr(x) BFA_STATUS_FAILED +#define bfad_ip_reset_drv_stats(x) BFA_STATUS_FAILED +#define bfad_ip_get_drv_stats(x, y) BFA_STATUS_FAILED +#define bfad_ip_enable_ipfc(x, y, z) BFA_STATUS_FAILED + + +#endif diff --git a/drivers/scsi/bfa/bfad_os.c b/drivers/scsi/bfa/bfad_os.c new file mode 100644 index 000000000000..faf47b4f1a38 --- /dev/null +++ b/drivers/scsi/bfa/bfad_os.c @@ -0,0 +1,50 @@ +/* + * 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_os.c Linux driver OS specific calls. + */ + +#include "bfa_os_inc.h" +#include "bfad_drv.h" + +void +bfa_os_gettimeofday(struct bfa_timeval_s *tv) +{ + struct timeval tmp_tv; + + do_gettimeofday(&tmp_tv); + tv->tv_sec = (u32) tmp_tv.tv_sec; + tv->tv_usec = (u32) tmp_tv.tv_usec; +} + +void +bfa_os_printf(struct bfa_log_mod_s *log_mod, u32 msg_id, + const char *fmt, ...) +{ + va_list ap; + #define BFA_STRING_256 256 + char tmp[BFA_STRING_256]; + + va_start(ap, fmt); + vsprintf(tmp, fmt, ap); + va_end(ap); + + printk(tmp); +} + + diff --git a/drivers/scsi/bfa/bfad_tm.h b/drivers/scsi/bfa/bfad_tm.h new file mode 100644 index 000000000000..4901b1b7df02 --- /dev/null +++ b/drivers/scsi/bfa/bfad_tm.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +/* + * Brocade Fibre Channel HBA Linux Target Mode Driver + */ + +/** + * tm/dummy/bfad_tm.h BFA callback dummy header file for BFA Linux target mode PCI interface module. + */ + +#ifndef __BFAD_TM_H__ +#define __BFAD_TM_H__ + +#include + +#define FCPT_NAME "" + +/* + * Called from base Linux driver on (De)Init events + */ + +/* attach tgt template with scst */ +#define bfad_tm_module_init() do {} while (0) + +/* detach/release tgt template */ +#define bfad_tm_module_exit() do {} while (0) + +#define bfad_tm_probe(x) do {} while (0) +#define bfad_tm_probe_undo(x) do {} while (0) +#define bfad_tm_probe_post(x) do {} while (0) + +/* + * Called by base Linux driver but triggered by BFA FCS on config events + */ +#define bfad_tm_port_new(x, y) BFA_STATUS_OK +#define bfad_tm_port_delete(x, y) do {} while (0) + +/* + * Called by base Linux driver but triggered by BFA FCS on PLOGI/O events + */ +#define bfad_tm_port_online(x, y) do {} while (0) +#define bfad_tm_port_offline(x, y) do {} while (0) + +#endif diff --git a/drivers/scsi/bfa/bfad_trcmod.h b/drivers/scsi/bfa/bfad_trcmod.h new file mode 100644 index 000000000000..2827b2acd041 --- /dev/null +++ b/drivers/scsi/bfa/bfad_trcmod.h @@ -0,0 +1,52 @@ +/* + * 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_trcmod.h Linux driver trace modules + */ + + +#ifndef __BFAD_TRCMOD_H__ +#define __BFAD_TRCMOD_H__ + +#include + +/* + * !!! Only append to the enums defined here to avoid any versioning + * !!! needed between trace utility and driver version + */ +enum { + /* 2.6 Driver */ + BFA_TRC_LDRV_BFAD = 1, + BFA_TRC_LDRV_BFAD_2_6 = 2, + BFA_TRC_LDRV_BFAD_2_6_9 = 3, + BFA_TRC_LDRV_BFAD_2_6_10 = 4, + BFA_TRC_LDRV_INTR = 5, + BFA_TRC_LDRV_IOCTL = 6, + BFA_TRC_LDRV_OS = 7, + BFA_TRC_LDRV_IM = 8, + BFA_TRC_LDRV_IM_2_6 = 9, + BFA_TRC_LDRV_IM_2_6_9 = 10, + BFA_TRC_LDRV_IM_2_6_10 = 11, + BFA_TRC_LDRV_TM = 12, + BFA_TRC_LDRV_IPFC = 13, + BFA_TRC_LDRV_IM_2_4 = 14, + BFA_TRC_LDRV_IM_VMW = 15, + BFA_TRC_LDRV_IM_LT_2_6_10 = 16, +}; + +#endif /* __BFAD_TRCMOD_H__ */ diff --git a/drivers/scsi/bfa/fab.c b/drivers/scsi/bfa/fab.c new file mode 100644 index 000000000000..7e3a4d5d7bb4 --- /dev/null +++ b/drivers/scsi/bfa/fab.c @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#include +#include +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "lport_priv.h" + +/** + * fab.c port fab implementation. + */ + +/** + * bfa_fcs_port_fab_public port fab public functions + */ + +/** + * Called by port to initialize fabric services of the base port. + */ +void +bfa_fcs_port_fab_init(struct bfa_fcs_port_s *port) +{ + bfa_fcs_port_ns_init(port); + bfa_fcs_port_scn_init(port); + bfa_fcs_port_ms_init(port); +} + +/** + * Called by port to notify transition to online state. + */ +void +bfa_fcs_port_fab_online(struct bfa_fcs_port_s *port) +{ + bfa_fcs_port_ns_online(port); + bfa_fcs_port_scn_online(port); +} + +/** + * Called by port to notify transition to offline state. + */ +void +bfa_fcs_port_fab_offline(struct bfa_fcs_port_s *port) +{ + bfa_fcs_port_ns_offline(port); + bfa_fcs_port_scn_offline(port); + bfa_fcs_port_ms_offline(port); +} diff --git a/drivers/scsi/bfa/fabric.c b/drivers/scsi/bfa/fabric.c new file mode 100644 index 000000000000..a8b14c47b009 --- /dev/null +++ b/drivers/scsi/bfa/fabric.c @@ -0,0 +1,1278 @@ +/* + * 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. + */ + +/** + * fabric.c Fabric module implementation. + */ + +#include "fcs_fabric.h" +#include "fcs_lport.h" +#include "fcs_vport.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs_auth.h" +#include "fcs.h" +#include "fcbuild.h" +#include +#include +#include + +BFA_TRC_FILE(FCS, FABRIC); + +#define BFA_FCS_FABRIC_RETRY_DELAY (2000) /* Milliseconds */ +#define BFA_FCS_FABRIC_CLEANUP_DELAY (10000) /* Milliseconds */ + +#define bfa_fcs_fabric_set_opertype(__fabric) do { \ + if (bfa_pport_get_topology((__fabric)->fcs->bfa) \ + == BFA_PPORT_TOPOLOGY_P2P) \ + (__fabric)->oper_type = BFA_PPORT_TYPE_NPORT; \ + else \ + (__fabric)->oper_type = BFA_PPORT_TYPE_NLPORT; \ +} while (0) + +/* + * forward declarations + */ +static void bfa_fcs_fabric_init(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_login(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_notify_online(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_notify_offline(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_delay(void *cbarg); +static void bfa_fcs_fabric_delete(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_delete_comp(void *cbarg); +static void bfa_fcs_fabric_process_uf(struct bfa_fcs_fabric_s *fabric, + struct fchs_s *fchs, u16 len); +static void bfa_fcs_fabric_process_flogi(struct bfa_fcs_fabric_s *fabric, + struct fchs_s *fchs, u16 len); +static void bfa_fcs_fabric_send_flogi_acc(struct bfa_fcs_fabric_s *fabric); +static void bfa_fcs_fabric_flogiacc_comp(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rspfchs); +/** + * fcs_fabric_sm fabric state machine functions + */ + +/** + * Fabric state machine events + */ +enum bfa_fcs_fabric_event { + BFA_FCS_FABRIC_SM_CREATE = 1, /* fabric create from driver */ + BFA_FCS_FABRIC_SM_DELETE = 2, /* fabric delete from driver */ + BFA_FCS_FABRIC_SM_LINK_DOWN = 3, /* link down from port */ + BFA_FCS_FABRIC_SM_LINK_UP = 4, /* link up from port */ + BFA_FCS_FABRIC_SM_CONT_OP = 5, /* continue op from flogi/auth */ + BFA_FCS_FABRIC_SM_RETRY_OP = 6, /* continue op from flogi/auth */ + BFA_FCS_FABRIC_SM_NO_FABRIC = 7, /* no fabric from flogi/auth + */ + BFA_FCS_FABRIC_SM_PERF_EVFP = 8, /* perform EVFP from + *flogi/auth */ + BFA_FCS_FABRIC_SM_ISOLATE = 9, /* isolate from EVFP processing */ + BFA_FCS_FABRIC_SM_NO_TAGGING = 10,/* no VFT tagging from EVFP */ + BFA_FCS_FABRIC_SM_DELAYED = 11, /* timeout delay event */ + BFA_FCS_FABRIC_SM_AUTH_FAILED = 12, /* authentication failed */ + BFA_FCS_FABRIC_SM_AUTH_SUCCESS = 13, /* authentication successful + */ + BFA_FCS_FABRIC_SM_DELCOMP = 14, /* all vports deleted event */ + BFA_FCS_FABRIC_SM_LOOPBACK = 15, /* Received our own FLOGI */ + BFA_FCS_FABRIC_SM_START = 16, /* fabric delete from driver */ +}; + +static void bfa_fcs_fabric_sm_uninit(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_created(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_linkdown(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_flogi(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_flogi_retry(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_auth(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_auth_failed(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_loopback(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_nofabric(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_online(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_evfp(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_evfp_done(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_isolated(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +static void bfa_fcs_fabric_sm_deleting(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event); +/** + * Beginning state before fabric creation. + */ +static void +bfa_fcs_fabric_sm_uninit(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_CREATE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_created); + bfa_fcs_fabric_init(fabric); + bfa_fcs_lport_init(&fabric->bport, fabric->fcs, FC_VF_ID_NULL, + &fabric->bport.port_cfg, NULL); + break; + + case BFA_FCS_FABRIC_SM_LINK_UP: + case BFA_FCS_FABRIC_SM_LINK_DOWN: + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Beginning state before fabric creation. + */ +static void +bfa_fcs_fabric_sm_created(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_START: + if (bfa_pport_is_linkup(fabric->fcs->bfa)) { + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi); + bfa_fcs_fabric_login(fabric); + } else + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + break; + + case BFA_FCS_FABRIC_SM_LINK_UP: + case BFA_FCS_FABRIC_SM_LINK_DOWN: + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit); + bfa_fcs_modexit_comp(fabric->fcs); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Link is down, awaiting LINK UP event from port. This is also the + * first state at fabric creation. + */ +static void +bfa_fcs_fabric_sm_linkdown(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_LINK_UP: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi); + bfa_fcs_fabric_login(fabric); + break; + + case BFA_FCS_FABRIC_SM_RETRY_OP: + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * FLOGI is in progress, awaiting FLOGI reply. + */ +static void +bfa_fcs_fabric_sm_flogi(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_CONT_OP: + + bfa_pport_set_tx_bbcredit(fabric->fcs->bfa, fabric->bb_credit); + fabric->fab_type = BFA_FCS_FABRIC_SWITCHED; + + if (fabric->auth_reqd && fabric->is_auth) { + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_auth); + bfa_trc(fabric->fcs, event); + } else { + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_online); + bfa_fcs_fabric_notify_online(fabric); + } + break; + + case BFA_FCS_FABRIC_SM_RETRY_OP: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi_retry); + bfa_timer_start(fabric->fcs->bfa, &fabric->delay_timer, + bfa_fcs_fabric_delay, fabric, + BFA_FCS_FABRIC_RETRY_DELAY); + break; + + case BFA_FCS_FABRIC_SM_LOOPBACK: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_loopback); + bfa_lps_discard(fabric->lps); + bfa_fcs_fabric_set_opertype(fabric); + break; + + case BFA_FCS_FABRIC_SM_NO_FABRIC: + fabric->fab_type = BFA_FCS_FABRIC_N2N; + bfa_pport_set_tx_bbcredit(fabric->fcs->bfa, fabric->bb_credit); + bfa_fcs_fabric_notify_online(fabric); + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_nofabric); + break; + + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_lps_discard(fabric->lps); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_lps_discard(fabric->lps); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + + +static void +bfa_fcs_fabric_sm_flogi_retry(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_DELAYED: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi); + bfa_fcs_fabric_login(fabric); + break; + + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_timer_stop(&fabric->delay_timer); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_timer_stop(&fabric->delay_timer); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Authentication is in progress, awaiting authentication results. + */ +static void +bfa_fcs_fabric_sm_auth(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_AUTH_FAILED: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_auth_failed); + bfa_lps_discard(fabric->lps); + break; + + case BFA_FCS_FABRIC_SM_AUTH_SUCCESS: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_online); + bfa_fcs_fabric_notify_online(fabric); + break; + + case BFA_FCS_FABRIC_SM_PERF_EVFP: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_evfp); + break; + + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_lps_discard(fabric->lps); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Authentication failed + */ +static void +bfa_fcs_fabric_sm_auth_failed(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_fcs_fabric_notify_offline(fabric); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Port is in loopback mode. + */ +static void +bfa_fcs_fabric_sm_loopback(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_fcs_fabric_notify_offline(fabric); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * There is no attached fabric - private loop or NPort-to-NPort topology. + */ +static void +bfa_fcs_fabric_sm_nofabric(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_lps_discard(fabric->lps); + bfa_fcs_fabric_notify_offline(fabric); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + case BFA_FCS_FABRIC_SM_NO_FABRIC: + bfa_trc(fabric->fcs, fabric->bb_credit); + bfa_pport_set_tx_bbcredit(fabric->fcs->bfa, fabric->bb_credit); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Fabric is online - normal operating state. + */ +static void +bfa_fcs_fabric_sm_online(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + bfa_lps_discard(fabric->lps); + bfa_fcs_fabric_notify_offline(fabric); + break; + + case BFA_FCS_FABRIC_SM_DELETE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_deleting); + bfa_fcs_fabric_delete(fabric); + break; + + case BFA_FCS_FABRIC_SM_AUTH_FAILED: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_auth_failed); + bfa_lps_discard(fabric->lps); + break; + + case BFA_FCS_FABRIC_SM_AUTH_SUCCESS: + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * Exchanging virtual fabric parameters. + */ +static void +bfa_fcs_fabric_sm_evfp(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_CONT_OP: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_evfp_done); + break; + + case BFA_FCS_FABRIC_SM_ISOLATE: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_isolated); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + +/** + * EVFP exchange complete and VFT tagging is enabled. + */ +static void +bfa_fcs_fabric_sm_evfp_done(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); +} + +/** + * Port is isolated after EVFP exchange due to VF_ID mismatch (N and F). + */ +static void +bfa_fcs_fabric_sm_isolated(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + bfa_log(fabric->fcs->logm, BFA_LOG_FCS_FABRIC_ISOLATED, + fabric->bport.port_cfg.pwwn, fabric->fcs->port_vfid, + fabric->event_arg.swp_vfid); +} + +/** + * Fabric is being deleted, awaiting vport delete completions. + */ +static void +bfa_fcs_fabric_sm_deleting(struct bfa_fcs_fabric_s *fabric, + enum bfa_fcs_fabric_event event) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, event); + + switch (event) { + case BFA_FCS_FABRIC_SM_DELCOMP: + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit); + bfa_fcs_modexit_comp(fabric->fcs); + break; + + case BFA_FCS_FABRIC_SM_LINK_UP: + break; + + case BFA_FCS_FABRIC_SM_LINK_DOWN: + bfa_fcs_fabric_notify_offline(fabric); + break; + + default: + bfa_sm_fault(fabric->fcs, event); + } +} + + + +/** + * fcs_fabric_private fabric private functions + */ + +static void +bfa_fcs_fabric_init(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_port_cfg_s *port_cfg = &fabric->bport.port_cfg; + + port_cfg->roles = BFA_PORT_ROLE_FCP_IM; + port_cfg->nwwn = bfa_ioc_get_nwwn(&fabric->fcs->bfa->ioc); + port_cfg->pwwn = bfa_ioc_get_pwwn(&fabric->fcs->bfa->ioc); +} + +/** + * Port Symbolic Name Creation for base port. + */ +void +bfa_fcs_fabric_psymb_init(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_port_cfg_s *port_cfg = &fabric->bport.port_cfg; + struct bfa_adapter_attr_s adapter_attr; + struct bfa_fcs_driver_info_s *driver_info = &fabric->fcs->driver_info; + + bfa_os_memset((void *)&adapter_attr, 0, + sizeof(struct bfa_adapter_attr_s)); + bfa_ioc_get_adapter_attr(&fabric->fcs->bfa->ioc, &adapter_attr); + + /* + * Model name/number + */ + strncpy((char *)&port_cfg->sym_name, adapter_attr.model, + BFA_FCS_PORT_SYMBNAME_MODEL_SZ); + strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + + /* + * Driver Version + */ + strncat((char *)&port_cfg->sym_name, (char *)driver_info->version, + BFA_FCS_PORT_SYMBNAME_VERSION_SZ); + strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + + /* + * Host machine name + */ + strncat((char *)&port_cfg->sym_name, + (char *)driver_info->host_machine_name, + BFA_FCS_PORT_SYMBNAME_MACHINENAME_SZ); + strncat((char *)&port_cfg->sym_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + + /* + * Host OS Info : + * If OS Patch Info is not there, do not truncate any bytes from the + * OS name string and instead copy the entire OS info string (64 bytes). + */ + if (driver_info->host_os_patch[0] == '\0') { + strncat((char *)&port_cfg->sym_name, + (char *)driver_info->host_os_name, BFA_FCS_OS_STR_LEN); + strncat((char *)&port_cfg->sym_name, + BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + } else { + strncat((char *)&port_cfg->sym_name, + (char *)driver_info->host_os_name, + BFA_FCS_PORT_SYMBNAME_OSINFO_SZ); + strncat((char *)&port_cfg->sym_name, + BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + + /* + * Append host OS Patch Info + */ + strncat((char *)&port_cfg->sym_name, + (char *)driver_info->host_os_patch, + BFA_FCS_PORT_SYMBNAME_OSPATCH_SZ); + } + + /* + * null terminate + */ + port_cfg->sym_name.symname[BFA_SYMNAME_MAXLEN - 1] = 0; +} + +/** + * bfa lps login completion callback + */ +void +bfa_cb_lps_flogi_comp(void *bfad, void *uarg, bfa_status_t status) +{ + struct bfa_fcs_fabric_s *fabric = uarg; + + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_trc(fabric->fcs, status); + + switch (status) { + case BFA_STATUS_OK: + fabric->stats.flogi_accepts++; + break; + + case BFA_STATUS_INVALID_MAC: + /* + * Only for CNA + */ + fabric->stats.flogi_acc_err++; + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP); + + return; + + case BFA_STATUS_EPROTOCOL: + switch (bfa_lps_get_extstatus(fabric->lps)) { + case BFA_EPROTO_BAD_ACCEPT: + fabric->stats.flogi_acc_err++; + break; + + case BFA_EPROTO_UNKNOWN_RSP: + fabric->stats.flogi_unknown_rsp++; + break; + + default: + break; + } + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP); + + return; + + case BFA_STATUS_FABRIC_RJT: + fabric->stats.flogi_rejects++; + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP); + return; + + default: + fabric->stats.flogi_rsp_err++; + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_RETRY_OP); + return; + } + + fabric->bb_credit = bfa_lps_get_peer_bbcredit(fabric->lps); + bfa_trc(fabric->fcs, fabric->bb_credit); + + if (!bfa_lps_is_brcd_fabric(fabric->lps)) + fabric->fabric_name = bfa_lps_get_peer_nwwn(fabric->lps); + + /* + * Check port type. It should be 1 = F-port. + */ + if (bfa_lps_is_fport(fabric->lps)) { + fabric->bport.pid = bfa_lps_get_pid(fabric->lps); + fabric->is_npiv = bfa_lps_is_npiv_en(fabric->lps); + fabric->is_auth = bfa_lps_is_authreq(fabric->lps); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_CONT_OP); + } else { + /* + * Nport-2-Nport direct attached + */ + fabric->bport.port_topo.pn2n.rem_port_wwn = + bfa_lps_get_peer_pwwn(fabric->lps); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_NO_FABRIC); + } + + bfa_trc(fabric->fcs, fabric->bport.pid); + bfa_trc(fabric->fcs, fabric->is_npiv); + bfa_trc(fabric->fcs, fabric->is_auth); +} + +/** + * Allocate and send FLOGI. + */ +static void +bfa_fcs_fabric_login(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_s *bfa = fabric->fcs->bfa; + struct bfa_port_cfg_s *pcfg = &fabric->bport.port_cfg; + u8 alpa = 0; + + if (bfa_pport_get_topology(bfa) == BFA_PPORT_TOPOLOGY_LOOP) + alpa = bfa_pport_get_myalpa(bfa); + + bfa_lps_flogi(fabric->lps, fabric, alpa, bfa_pport_get_maxfrsize(bfa), + pcfg->pwwn, pcfg->nwwn, fabric->auth_reqd); + + fabric->stats.flogi_sent++; +} + +static void +bfa_fcs_fabric_notify_online(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_fcs_vport_s *vport; + struct list_head *qe, *qen; + + bfa_trc(fabric->fcs, fabric->fabric_name); + + bfa_fcs_fabric_set_opertype(fabric); + fabric->stats.fabric_onlines++; + + /** + * notify online event to base and then virtual ports + */ + bfa_fcs_port_online(&fabric->bport); + + list_for_each_safe(qe, qen, &fabric->vport_q) { + vport = (struct bfa_fcs_vport_s *)qe; + bfa_fcs_vport_online(vport); + } +} + +static void +bfa_fcs_fabric_notify_offline(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_fcs_vport_s *vport; + struct list_head *qe, *qen; + + bfa_trc(fabric->fcs, fabric->fabric_name); + fabric->stats.fabric_offlines++; + + /** + * notify offline event first to vports and then base port. + */ + list_for_each_safe(qe, qen, &fabric->vport_q) { + vport = (struct bfa_fcs_vport_s *)qe; + bfa_fcs_vport_offline(vport); + } + + bfa_fcs_port_offline(&fabric->bport); + + fabric->fabric_name = 0; + fabric->fabric_ip_addr[0] = 0; +} + +static void +bfa_fcs_fabric_delay(void *cbarg) +{ + struct bfa_fcs_fabric_s *fabric = cbarg; + + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_DELAYED); +} + +/** + * Delete all vports and wait for vport delete completions. + */ +static void +bfa_fcs_fabric_delete(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_fcs_vport_s *vport; + struct list_head *qe, *qen; + + list_for_each_safe(qe, qen, &fabric->vport_q) { + vport = (struct bfa_fcs_vport_s *)qe; + bfa_fcs_vport_delete(vport); + } + + bfa_fcs_port_delete(&fabric->bport); + bfa_wc_wait(&fabric->wc); +} + +static void +bfa_fcs_fabric_delete_comp(void *cbarg) +{ + struct bfa_fcs_fabric_s *fabric = cbarg; + + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_DELCOMP); +} + + + +/** + * fcs_fabric_public fabric public functions + */ + +/** + * Module initialization + */ +void +bfa_fcs_fabric_modinit(struct bfa_fcs_s *fcs) +{ + struct bfa_fcs_fabric_s *fabric; + + fabric = &fcs->fabric; + bfa_os_memset(fabric, 0, sizeof(struct bfa_fcs_fabric_s)); + + /** + * Initialize base fabric. + */ + fabric->fcs = fcs; + INIT_LIST_HEAD(&fabric->vport_q); + INIT_LIST_HEAD(&fabric->vf_q); + fabric->lps = bfa_lps_alloc(fcs->bfa); + bfa_assert(fabric->lps); + + /** + * Initialize fabric delete completion handler. Fabric deletion is complete + * when the last vport delete is complete. + */ + bfa_wc_init(&fabric->wc, bfa_fcs_fabric_delete_comp, fabric); + bfa_wc_up(&fabric->wc); /* For the base port */ + + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_uninit); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_CREATE); + bfa_trc(fcs, 0); +} + +/** + * Module cleanup + */ +void +bfa_fcs_fabric_modexit(struct bfa_fcs_s *fcs) +{ + struct bfa_fcs_fabric_s *fabric; + + bfa_trc(fcs, 0); + + /** + * Cleanup base fabric. + */ + fabric = &fcs->fabric; + bfa_lps_delete(fabric->lps); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_DELETE); +} + +/** + * Fabric module start -- kick starts FCS actions + */ +void +bfa_fcs_fabric_modstart(struct bfa_fcs_s *fcs) +{ + struct bfa_fcs_fabric_s *fabric; + + bfa_trc(fcs, 0); + fabric = &fcs->fabric; + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_START); +} + +/** + * Suspend fabric activity as part of driver suspend. + */ +void +bfa_fcs_fabric_modsusp(struct bfa_fcs_s *fcs) +{ +} + +bfa_boolean_t +bfa_fcs_fabric_is_loopback(struct bfa_fcs_fabric_s *fabric) +{ + return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_loopback)); +} + +enum bfa_pport_type +bfa_fcs_fabric_port_type(struct bfa_fcs_fabric_s *fabric) +{ + return fabric->oper_type; +} + +/** + * Link up notification from BFA physical port module. + */ +void +bfa_fcs_fabric_link_up(struct bfa_fcs_fabric_s *fabric) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_LINK_UP); +} + +/** + * Link down notification from BFA physical port module. + */ +void +bfa_fcs_fabric_link_down(struct bfa_fcs_fabric_s *fabric) +{ + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_LINK_DOWN); +} + +/** + * A child vport is being created in the fabric. + * + * Call from vport module at vport creation. A list of base port and vports + * belonging to a fabric is maintained to propagate link events. + * + * param[in] fabric - Fabric instance. This can be a base fabric or vf. + * param[in] vport - Vport being created. + * + * @return None (always succeeds) + */ +void +bfa_fcs_fabric_addvport(struct bfa_fcs_fabric_s *fabric, + struct bfa_fcs_vport_s *vport) +{ + /** + * - add vport to fabric's vport_q + */ + bfa_trc(fabric->fcs, fabric->vf_id); + + list_add_tail(&vport->qe, &fabric->vport_q); + fabric->num_vports++; + bfa_wc_up(&fabric->wc); +} + +/** + * A child vport is being deleted from fabric. + * + * Vport is being deleted. + */ +void +bfa_fcs_fabric_delvport(struct bfa_fcs_fabric_s *fabric, + struct bfa_fcs_vport_s *vport) +{ + list_del(&vport->qe); + fabric->num_vports--; + bfa_wc_down(&fabric->wc); +} + +/** + * Base port is deleted. + */ +void +bfa_fcs_fabric_port_delete_comp(struct bfa_fcs_fabric_s *fabric) +{ + bfa_wc_down(&fabric->wc); +} + +/** + * Check if fabric is online. + * + * param[in] fabric - Fabric instance. This can be a base fabric or vf. + * + * @return TRUE/FALSE + */ +int +bfa_fcs_fabric_is_online(struct bfa_fcs_fabric_s *fabric) +{ + return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_online)); +} + + +bfa_status_t +bfa_fcs_fabric_addvf(struct bfa_fcs_fabric_s *vf, struct bfa_fcs_s *fcs, + struct bfa_port_cfg_s *port_cfg, + struct bfad_vf_s *vf_drv) +{ + bfa_sm_set_state(vf, bfa_fcs_fabric_sm_uninit); + return BFA_STATUS_OK; +} + +/** + * Lookup for a vport withing a fabric given its pwwn + */ +struct bfa_fcs_vport_s * +bfa_fcs_fabric_vport_lookup(struct bfa_fcs_fabric_s *fabric, wwn_t pwwn) +{ + struct bfa_fcs_vport_s *vport; + struct list_head *qe; + + list_for_each(qe, &fabric->vport_q) { + vport = (struct bfa_fcs_vport_s *)qe; + if (bfa_fcs_port_get_pwwn(&vport->lport) == pwwn) + return vport; + } + + return NULL; +} + +/** + * In a given fabric, return the number of lports. + * + * param[in] fabric - Fabric instance. This can be a base fabric or vf. + * +* @return : 1 or more. + */ +u16 +bfa_fcs_fabric_vport_count(struct bfa_fcs_fabric_s *fabric) +{ + return (fabric->num_vports); +} + +/** + * Unsolicited frame receive handling. + */ +void +bfa_fcs_fabric_uf_recv(struct bfa_fcs_fabric_s *fabric, struct fchs_s *fchs, + u16 len) +{ + u32 pid = fchs->d_id; + struct bfa_fcs_vport_s *vport; + struct list_head *qe; + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + struct fc_logi_s *flogi = (struct fc_logi_s *) els_cmd; + + bfa_trc(fabric->fcs, len); + bfa_trc(fabric->fcs, pid); + + /** + * Look for our own FLOGI frames being looped back. This means an + * external loopback cable is in place. Our own FLOGI frames are + * sometimes looped back when switch port gets temporarily bypassed. + */ + if ((pid == bfa_os_ntoh3b(FC_FABRIC_PORT)) + && (els_cmd->els_code == FC_ELS_FLOGI) + && (flogi->port_name == bfa_fcs_port_get_pwwn(&fabric->bport))) { + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_LOOPBACK); + return; + } + + /** + * FLOGI/EVFP exchanges should be consumed by base fabric. + */ + if (fchs->d_id == bfa_os_hton3b(FC_FABRIC_PORT)) { + bfa_trc(fabric->fcs, pid); + bfa_fcs_fabric_process_uf(fabric, fchs, len); + return; + } + + if (fabric->bport.pid == pid) { + /** + * All authentication frames should be routed to auth + */ + bfa_trc(fabric->fcs, els_cmd->els_code); + if (els_cmd->els_code == FC_ELS_AUTH) { + bfa_trc(fabric->fcs, els_cmd->els_code); + fabric->auth.response = (u8 *) els_cmd; + return; + } + + bfa_trc(fabric->fcs, *(u8 *) ((u8 *) fchs)); + bfa_fcs_port_uf_recv(&fabric->bport, fchs, len); + return; + } + + /** + * look for a matching local port ID + */ + list_for_each(qe, &fabric->vport_q) { + vport = (struct bfa_fcs_vport_s *)qe; + if (vport->lport.pid == pid) { + bfa_fcs_port_uf_recv(&vport->lport, fchs, len); + return; + } + } + bfa_trc(fabric->fcs, els_cmd->els_code); + bfa_fcs_port_uf_recv(&fabric->bport, fchs, len); +} + +/** + * Unsolicited frames to be processed by fabric. + */ +static void +bfa_fcs_fabric_process_uf(struct bfa_fcs_fabric_s *fabric, struct fchs_s *fchs, + u16 len) +{ + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + bfa_trc(fabric->fcs, els_cmd->els_code); + + switch (els_cmd->els_code) { + case FC_ELS_FLOGI: + bfa_fcs_fabric_process_flogi(fabric, fchs, len); + break; + + default: + /* + * need to generate a LS_RJT + */ + break; + } +} + +/** + * Process incoming FLOGI + */ +static void +bfa_fcs_fabric_process_flogi(struct bfa_fcs_fabric_s *fabric, + struct fchs_s *fchs, u16 len) +{ + struct fc_logi_s *flogi = (struct fc_logi_s *) (fchs + 1); + struct bfa_fcs_port_s *bport = &fabric->bport; + + bfa_trc(fabric->fcs, fchs->s_id); + + fabric->stats.flogi_rcvd++; + /* + * Check port type. It should be 0 = n-port. + */ + if (flogi->csp.port_type) { + /* + * @todo: may need to send a LS_RJT + */ + bfa_trc(fabric->fcs, flogi->port_name); + fabric->stats.flogi_rejected++; + return; + } + + fabric->bb_credit = bfa_os_ntohs(flogi->csp.bbcred); + bport->port_topo.pn2n.rem_port_wwn = flogi->port_name; + bport->port_topo.pn2n.reply_oxid = fchs->ox_id; + + /* + * Send a Flogi Acc + */ + bfa_fcs_fabric_send_flogi_acc(fabric); + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_NO_FABRIC); +} + +static void +bfa_fcs_fabric_send_flogi_acc(struct bfa_fcs_fabric_s *fabric) +{ + struct bfa_port_cfg_s *pcfg = &fabric->bport.port_cfg; + struct bfa_fcs_port_n2n_s *n2n_port = &fabric->bport.port_topo.pn2n; + struct bfa_s *bfa = fabric->fcs->bfa; + struct bfa_fcxp_s *fcxp; + u16 reqlen; + struct fchs_s fchs; + + fcxp = bfa_fcs_fcxp_alloc(fabric->fcs); + /** + * Do not expect this failure -- expect remote node to retry + */ + if (!fcxp) + return; + + reqlen = fc_flogi_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_os_hton3b(FC_FABRIC_PORT), + n2n_port->reply_oxid, pcfg->pwwn, + pcfg->nwwn, bfa_pport_get_maxfrsize(bfa), + bfa_pport_get_rx_bbcredit(bfa)); + + bfa_fcxp_send(fcxp, NULL, fabric->vf_id, bfa_lps_get_tag(fabric->lps), + BFA_FALSE, FC_CLASS_3, reqlen, &fchs, + bfa_fcs_fabric_flogiacc_comp, fabric, + FC_MAX_PDUSZ, 0); /* Timeout 0 indicates no + * response expected + */ +} + +/** + * Flogi Acc completion callback. + */ +static void +bfa_fcs_fabric_flogiacc_comp(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t status, u32 rsp_len, + u32 resid_len, struct fchs_s *rspfchs) +{ + struct bfa_fcs_fabric_s *fabric = cbarg; + + bfa_trc(fabric->fcs, status); +} + +/* + * + * @param[in] fabric - fabric + * @param[in] result - 1 + * + * @return - none + */ +void +bfa_fcs_auth_finished(struct bfa_fcs_fabric_s *fabric, enum auth_status status) +{ + bfa_trc(fabric->fcs, status); + + if (status == FC_AUTH_STATE_SUCCESS) + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_AUTH_SUCCESS); + else + bfa_sm_send_event(fabric, BFA_FCS_FABRIC_SM_AUTH_FAILED); +} + +/** + * Send AEN notification + */ +static void +bfa_fcs_fabric_aen_post(struct bfa_fcs_port_s *port, + enum bfa_port_aen_event event) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = port->fcs->logm; + wwn_t pwwn = bfa_fcs_port_get_pwwn(port); + wwn_t fwwn = bfa_fcs_port_get_fabric_name(port); + char pwwn_ptr[BFA_STRING_32]; + char fwwn_ptr[BFA_STRING_32]; + + wwn2str(pwwn_ptr, pwwn); + wwn2str(fwwn_ptr, fwwn); + + switch (event) { + case BFA_PORT_AEN_FABRIC_NAME_CHANGE: + bfa_log(logmod, BFA_AEN_PORT_FABRIC_NAME_CHANGE, pwwn_ptr, + fwwn_ptr); + break; + default: + break; + } + + aen_data.port.pwwn = pwwn; + aen_data.port.fwwn = fwwn; +} + +/* + * + * @param[in] fabric - fabric + * @param[in] wwn_t - new fabric name + * + * @return - none + */ +void +bfa_fcs_fabric_set_fabric_name(struct bfa_fcs_fabric_s *fabric, + wwn_t fabric_name) +{ + bfa_trc(fabric->fcs, fabric_name); + + if (fabric->fabric_name == 0) { + /* + * With BRCD switches, we don't get Fabric Name in FLOGI. + * Don't generate a fabric name change event in this case. + */ + fabric->fabric_name = fabric_name; + } else { + fabric->fabric_name = fabric_name; + /* + * Generate a Event + */ + bfa_fcs_fabric_aen_post(&fabric->bport, + BFA_PORT_AEN_FABRIC_NAME_CHANGE); + } + +} + +/** + * Not used by FCS. + */ +void +bfa_cb_lps_flogo_comp(void *bfad, void *uarg) +{ +} + + diff --git a/drivers/scsi/bfa/fcbuild.c b/drivers/scsi/bfa/fcbuild.c new file mode 100644 index 000000000000..d174706b9caa --- /dev/null +++ b/drivers/scsi/bfa/fcbuild.c @@ -0,0 +1,1449 @@ +/* + * 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. + */ +/* + * fcbuild.c - FC link service frame building and parsing routines + */ + +#include +#include "fcbuild.h" + +/* + * static build functions + */ +static void fc_els_rsp_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id); +static void fc_bls_rsp_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id); +static struct fchs_s fc_els_req_tmpl; +static struct fchs_s fc_els_rsp_tmpl; +static struct fchs_s fc_bls_req_tmpl; +static struct fchs_s fc_bls_rsp_tmpl; +static struct fc_ba_acc_s ba_acc_tmpl; +static struct fc_logi_s plogi_tmpl; +static struct fc_prli_s prli_tmpl; +static struct fc_rrq_s rrq_tmpl; +static struct fchs_s fcp_fchs_tmpl; + +void +fcbuild_init(void) +{ + /* + * fc_els_req_tmpl + */ + fc_els_req_tmpl.routing = FC_RTG_EXT_LINK; + fc_els_req_tmpl.cat_info = FC_CAT_LD_REQUEST; + fc_els_req_tmpl.type = FC_TYPE_ELS; + fc_els_req_tmpl.f_ctl = + bfa_os_hton3b(FCTL_SEQ_INI | FCTL_FS_EXCH | FCTL_END_SEQ | + FCTL_SI_XFER); + fc_els_req_tmpl.rx_id = FC_RXID_ANY; + + /* + * fc_els_rsp_tmpl + */ + fc_els_rsp_tmpl.routing = FC_RTG_EXT_LINK; + fc_els_rsp_tmpl.cat_info = FC_CAT_LD_REPLY; + fc_els_rsp_tmpl.type = FC_TYPE_ELS; + fc_els_rsp_tmpl.f_ctl = + bfa_os_hton3b(FCTL_EC_RESP | FCTL_SEQ_INI | FCTL_LS_EXCH | + FCTL_END_SEQ | FCTL_SI_XFER); + fc_els_rsp_tmpl.rx_id = FC_RXID_ANY; + + /* + * fc_bls_req_tmpl + */ + fc_bls_req_tmpl.routing = FC_RTG_BASIC_LINK; + fc_bls_req_tmpl.type = FC_TYPE_BLS; + fc_bls_req_tmpl.f_ctl = bfa_os_hton3b(FCTL_END_SEQ | FCTL_SI_XFER); + fc_bls_req_tmpl.rx_id = FC_RXID_ANY; + + /* + * fc_bls_rsp_tmpl + */ + fc_bls_rsp_tmpl.routing = FC_RTG_BASIC_LINK; + fc_bls_rsp_tmpl.cat_info = FC_CAT_BA_ACC; + fc_bls_rsp_tmpl.type = FC_TYPE_BLS; + fc_bls_rsp_tmpl.f_ctl = + bfa_os_hton3b(FCTL_EC_RESP | FCTL_SEQ_INI | FCTL_LS_EXCH | + FCTL_END_SEQ | FCTL_SI_XFER); + fc_bls_rsp_tmpl.rx_id = FC_RXID_ANY; + + /* + * ba_acc_tmpl + */ + ba_acc_tmpl.seq_id_valid = 0; + ba_acc_tmpl.low_seq_cnt = 0; + ba_acc_tmpl.high_seq_cnt = 0xFFFF; + + /* + * plogi_tmpl + */ + plogi_tmpl.csp.verhi = FC_PH_VER_PH_3; + plogi_tmpl.csp.verlo = FC_PH_VER_4_3; + plogi_tmpl.csp.bbcred = bfa_os_htons(0x0004); + plogi_tmpl.csp.ciro = 0x1; + plogi_tmpl.csp.cisc = 0x0; + plogi_tmpl.csp.altbbcred = 0x0; + plogi_tmpl.csp.conseq = bfa_os_htons(0x00FF); + plogi_tmpl.csp.ro_bitmap = bfa_os_htons(0x0002); + plogi_tmpl.csp.e_d_tov = bfa_os_htonl(2000); + + plogi_tmpl.class3.class_valid = 1; + plogi_tmpl.class3.sequential = 1; + plogi_tmpl.class3.conseq = 0xFF; + plogi_tmpl.class3.ospx = 1; + + /* + * prli_tmpl + */ + prli_tmpl.command = FC_ELS_PRLI; + prli_tmpl.pglen = 0x10; + prli_tmpl.pagebytes = bfa_os_htons(0x0014); + prli_tmpl.parampage.type = FC_TYPE_FCP; + prli_tmpl.parampage.imagepair = 1; + prli_tmpl.parampage.servparams.rxrdisab = 1; + + /* + * rrq_tmpl + */ + rrq_tmpl.els_cmd.els_code = FC_ELS_RRQ; + + /* + * fcp_fchs_tmpl + */ + fcp_fchs_tmpl.routing = FC_RTG_FC4_DEV_DATA; + fcp_fchs_tmpl.cat_info = FC_CAT_UNSOLICIT_CMD; + fcp_fchs_tmpl.type = FC_TYPE_FCP; + fcp_fchs_tmpl.f_ctl = + bfa_os_hton3b(FCTL_FS_EXCH | FCTL_END_SEQ | FCTL_SI_XFER); + fcp_fchs_tmpl.seq_id = 1; + fcp_fchs_tmpl.rx_id = FC_RXID_ANY; +} + +static void +fc_gs_fchdr_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u32 ox_id) +{ + bfa_os_memset(fchs, 0, sizeof(struct fchs_s)); + + fchs->routing = FC_RTG_FC4_DEV_DATA; + fchs->cat_info = FC_CAT_UNSOLICIT_CTRL; + fchs->type = FC_TYPE_SERVICES; + fchs->f_ctl = + bfa_os_hton3b(FCTL_SEQ_INI | FCTL_FS_EXCH | FCTL_END_SEQ | + FCTL_SI_XFER); + fchs->rx_id = FC_RXID_ANY; + fchs->d_id = (d_id); + fchs->s_id = (s_id); + fchs->ox_id = bfa_os_htons(ox_id); + + /** + * @todo no need to set ox_id for request + * no need to set rx_id for response + */ +} + +void +fc_els_req_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id) +{ + bfa_os_memcpy(fchs, &fc_els_req_tmpl, sizeof(struct fchs_s)); + fchs->d_id = (d_id); + fchs->s_id = (s_id); + fchs->ox_id = bfa_os_htons(ox_id); +} + +static void +fc_els_rsp_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id) +{ + bfa_os_memcpy(fchs, &fc_els_rsp_tmpl, sizeof(struct fchs_s)); + fchs->d_id = d_id; + fchs->s_id = s_id; + fchs->ox_id = ox_id; +} + +enum fc_parse_status +fc_els_rsp_parse(struct fchs_s *fchs, int len) +{ + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + struct fc_ls_rjt_s *ls_rjt = (struct fc_ls_rjt_s *) els_cmd; + + len = len; + + switch (els_cmd->els_code) { + case FC_ELS_LS_RJT: + if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY) + return (FC_PARSE_BUSY); + else + return (FC_PARSE_FAILURE); + + case FC_ELS_ACC: + return (FC_PARSE_OK); + } + return (FC_PARSE_OK); +} + +static void +fc_bls_rsp_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id) +{ + bfa_os_memcpy(fchs, &fc_bls_rsp_tmpl, sizeof(struct fchs_s)); + fchs->d_id = d_id; + fchs->s_id = s_id; + fchs->ox_id = ox_id; +} + +static u16 +fc_plogi_x_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size, u8 els_code) +{ + struct fc_logi_s *plogi = (struct fc_logi_s *) (pld); + + bfa_os_memcpy(plogi, &plogi_tmpl, sizeof(struct fc_logi_s)); + + plogi->els_cmd.els_code = els_code; + if (els_code == FC_ELS_PLOGI) + fc_els_req_build(fchs, d_id, s_id, ox_id); + else + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + plogi->csp.rxsz = plogi->class3.rxsz = bfa_os_htons(pdu_size); + + bfa_os_memcpy(&plogi->port_name, &port_name, sizeof(wwn_t)); + bfa_os_memcpy(&plogi->node_name, &node_name, sizeof(wwn_t)); + + return (sizeof(struct fc_logi_s)); +} + +u16 +fc_flogi_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size, u8 set_npiv, u8 set_auth, + u16 local_bb_credits) +{ + u32 d_id = bfa_os_hton3b(FC_FABRIC_PORT); + u32 *vvl_info; + + bfa_os_memcpy(flogi, &plogi_tmpl, sizeof(struct fc_logi_s)); + + flogi->els_cmd.els_code = FC_ELS_FLOGI; + fc_els_req_build(fchs, d_id, s_id, ox_id); + + flogi->csp.rxsz = flogi->class3.rxsz = bfa_os_htons(pdu_size); + flogi->port_name = port_name; + flogi->node_name = node_name; + + /* + * Set the NPIV Capability Bit ( word 1, bit 31) of Common + * Service Parameters. + */ + flogi->csp.ciro = set_npiv; + + /* set AUTH capability */ + flogi->csp.security = set_auth; + + flogi->csp.bbcred = bfa_os_htons(local_bb_credits); + + /* Set brcd token in VVL */ + vvl_info = (u32 *)&flogi->vvl[0]; + + /* set the flag to indicate the presence of VVL */ + flogi->csp.npiv_supp = 1; /* @todo. field name is not correct */ + vvl_info[0] = bfa_os_htonl(FLOGI_VVL_BRCD); + + return (sizeof(struct fc_logi_s)); +} + +u16 +fc_flogi_acc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size, u16 local_bb_credits) +{ + u32 d_id = 0; + + bfa_os_memcpy(flogi, &plogi_tmpl, sizeof(struct fc_logi_s)); + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + flogi->els_cmd.els_code = FC_ELS_ACC; + flogi->csp.rxsz = flogi->class3.rxsz = bfa_os_htons(pdu_size); + flogi->port_name = port_name; + flogi->node_name = node_name; + + flogi->csp.bbcred = bfa_os_htons(local_bb_credits); + + return (sizeof(struct fc_logi_s)); +} + +u16 +fc_fdisc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size) +{ + u32 d_id = bfa_os_hton3b(FC_FABRIC_PORT); + + bfa_os_memcpy(flogi, &plogi_tmpl, sizeof(struct fc_logi_s)); + + flogi->els_cmd.els_code = FC_ELS_FDISC; + fc_els_req_build(fchs, d_id, s_id, ox_id); + + flogi->csp.rxsz = flogi->class3.rxsz = bfa_os_htons(pdu_size); + flogi->port_name = port_name; + flogi->node_name = node_name; + + return (sizeof(struct fc_logi_s)); +} + +u16 +fc_plogi_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size) +{ + return fc_plogi_x_build(fchs, pld, d_id, s_id, ox_id, port_name, + node_name, pdu_size, FC_ELS_PLOGI); +} + +u16 +fc_plogi_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size) +{ + return fc_plogi_x_build(fchs, pld, d_id, s_id, ox_id, port_name, + node_name, pdu_size, FC_ELS_ACC); +} + +enum fc_parse_status +fc_plogi_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name) +{ + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + struct fc_logi_s *plogi; + struct fc_ls_rjt_s *ls_rjt; + + switch (els_cmd->els_code) { + case FC_ELS_LS_RJT: + ls_rjt = (struct fc_ls_rjt_s *) (fchs + 1); + if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY) + return (FC_PARSE_BUSY); + else + return (FC_PARSE_FAILURE); + case FC_ELS_ACC: + plogi = (struct fc_logi_s *) (fchs + 1); + if (len < sizeof(struct fc_logi_s)) + return (FC_PARSE_FAILURE); + + if (!wwn_is_equal(plogi->port_name, port_name)) + return (FC_PARSE_FAILURE); + + if (!plogi->class3.class_valid) + return (FC_PARSE_FAILURE); + + if (bfa_os_ntohs(plogi->class3.rxsz) < (FC_MIN_PDUSZ)) + return (FC_PARSE_FAILURE); + + return (FC_PARSE_OK); + default: + return (FC_PARSE_FAILURE); + } +} + +enum fc_parse_status +fc_plogi_parse(struct fchs_s *fchs) +{ + struct fc_logi_s *plogi = (struct fc_logi_s *) (fchs + 1); + + if (plogi->class3.class_valid != 1) + return FC_PARSE_FAILURE; + + if ((bfa_os_ntohs(plogi->class3.rxsz) < FC_MIN_PDUSZ) + || (bfa_os_ntohs(plogi->class3.rxsz) > FC_MAX_PDUSZ) + || (plogi->class3.rxsz == 0)) + return (FC_PARSE_FAILURE); + + return FC_PARSE_OK; +} + +u16 +fc_prli_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id) +{ + struct fc_prli_s *prli = (struct fc_prli_s *) (pld); + + fc_els_req_build(fchs, d_id, s_id, ox_id); + bfa_os_memcpy(prli, &prli_tmpl, sizeof(struct fc_prli_s)); + + prli->command = FC_ELS_PRLI; + prli->parampage.servparams.initiator = 1; + prli->parampage.servparams.retry = 1; + prli->parampage.servparams.rec_support = 1; + prli->parampage.servparams.task_retry_id = 0; + prli->parampage.servparams.confirm = 1; + + return (sizeof(struct fc_prli_s)); +} + +u16 +fc_prli_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id, enum bfa_port_role role) +{ + struct fc_prli_s *prli = (struct fc_prli_s *) (pld); + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + bfa_os_memcpy(prli, &prli_tmpl, sizeof(struct fc_prli_s)); + + prli->command = FC_ELS_ACC; + + if ((role & BFA_PORT_ROLE_FCP_TM) == BFA_PORT_ROLE_FCP_TM) + prli->parampage.servparams.target = 1; + else + prli->parampage.servparams.initiator = 1; + + prli->parampage.rspcode = FC_PRLI_ACC_XQTD; + + return (sizeof(struct fc_prli_s)); +} + +enum fc_parse_status +fc_prli_rsp_parse(struct fc_prli_s *prli, int len) +{ + if (len < sizeof(struct fc_prli_s)) + return (FC_PARSE_FAILURE); + + if (prli->command != FC_ELS_ACC) + return (FC_PARSE_FAILURE); + + if ((prli->parampage.rspcode != FC_PRLI_ACC_XQTD) + && (prli->parampage.rspcode != FC_PRLI_ACC_PREDEF_IMG)) + return (FC_PARSE_FAILURE); + + if (prli->parampage.servparams.target != 1) + return (FC_PARSE_FAILURE); + + return (FC_PARSE_OK); +} + +enum fc_parse_status +fc_prli_parse(struct fc_prli_s *prli) +{ + if (prli->parampage.type != FC_TYPE_FCP) + return (FC_PARSE_FAILURE); + + if (!prli->parampage.imagepair) + return (FC_PARSE_FAILURE); + + if (!prli->parampage.servparams.initiator) + return (FC_PARSE_FAILURE); + + return (FC_PARSE_OK); +} + +u16 +fc_logo_build(struct fchs_s *fchs, struct fc_logo_s *logo, u32 d_id, + u32 s_id, u16 ox_id, wwn_t port_name) +{ + fc_els_req_build(fchs, d_id, s_id, ox_id); + + memset(logo, '\0', sizeof(struct fc_logo_s)); + logo->els_cmd.els_code = FC_ELS_LOGO; + logo->nport_id = (s_id); + logo->orig_port_name = port_name; + + return (sizeof(struct fc_logo_s)); +} + +static u16 +fc_adisc_x_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, u32 d_id, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name, u8 els_code) +{ + memset(adisc, '\0', sizeof(struct fc_adisc_s)); + + adisc->els_cmd.els_code = els_code; + + if (els_code == FC_ELS_ADISC) + fc_els_req_build(fchs, d_id, s_id, ox_id); + else + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + adisc->orig_HA = 0; + adisc->orig_port_name = port_name; + adisc->orig_node_name = node_name; + adisc->nport_id = (s_id); + + return (sizeof(struct fc_adisc_s)); +} + +u16 +fc_adisc_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, u32 d_id, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name) +{ + return fc_adisc_x_build(fchs, adisc, d_id, s_id, ox_id, port_name, + node_name, FC_ELS_ADISC); +} + +u16 +fc_adisc_acc_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, u32 d_id, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name) +{ + return fc_adisc_x_build(fchs, adisc, d_id, s_id, ox_id, port_name, + node_name, FC_ELS_ACC); +} + +enum fc_parse_status +fc_adisc_rsp_parse(struct fc_adisc_s *adisc, int len, wwn_t port_name, + wwn_t node_name) +{ + + if (len < sizeof(struct fc_adisc_s)) + return (FC_PARSE_FAILURE); + + if (adisc->els_cmd.els_code != FC_ELS_ACC) + return (FC_PARSE_FAILURE); + + if (!wwn_is_equal(adisc->orig_port_name, port_name)) + return (FC_PARSE_FAILURE); + + return (FC_PARSE_OK); +} + +enum fc_parse_status +fc_adisc_parse(struct fchs_s *fchs, void *pld, u32 host_dap, + wwn_t node_name, wwn_t port_name) +{ + struct fc_adisc_s *adisc = (struct fc_adisc_s *) pld; + + if (adisc->els_cmd.els_code != FC_ELS_ACC) + return (FC_PARSE_FAILURE); + + if ((adisc->nport_id == (host_dap)) + && wwn_is_equal(adisc->orig_port_name, port_name) + && wwn_is_equal(adisc->orig_node_name, node_name)) + return (FC_PARSE_OK); + + return (FC_PARSE_FAILURE); +} + +enum fc_parse_status +fc_pdisc_parse(struct fchs_s *fchs, wwn_t node_name, wwn_t port_name) +{ + struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1); + + if (pdisc->class3.class_valid != 1) + return FC_PARSE_FAILURE; + + if ((bfa_os_ntohs(pdisc->class3.rxsz) < + (FC_MIN_PDUSZ - sizeof(struct fchs_s))) + || (pdisc->class3.rxsz == 0)) + return (FC_PARSE_FAILURE); + + if (!wwn_is_equal(pdisc->port_name, port_name)) + return (FC_PARSE_FAILURE); + + if (!wwn_is_equal(pdisc->node_name, node_name)) + return (FC_PARSE_FAILURE); + + return FC_PARSE_OK; +} + +u16 +fc_abts_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id) +{ + bfa_os_memcpy(fchs, &fc_bls_req_tmpl, sizeof(struct fchs_s)); + fchs->cat_info = FC_CAT_ABTS; + fchs->d_id = (d_id); + fchs->s_id = (s_id); + fchs->ox_id = bfa_os_htons(ox_id); + + return (sizeof(struct fchs_s)); +} + +enum fc_parse_status +fc_abts_rsp_parse(struct fchs_s *fchs, int len) +{ + if ((fchs->cat_info == FC_CAT_BA_ACC) + || (fchs->cat_info == FC_CAT_BA_RJT)) + return (FC_PARSE_OK); + + return (FC_PARSE_FAILURE); +} + +u16 +fc_rrq_build(struct fchs_s *fchs, struct fc_rrq_s *rrq, u32 d_id, + u32 s_id, u16 ox_id, u16 rrq_oxid) +{ + fc_els_req_build(fchs, d_id, s_id, ox_id); + + /* + * build rrq payload + */ + bfa_os_memcpy(rrq, &rrq_tmpl, sizeof(struct fc_rrq_s)); + rrq->s_id = (s_id); + rrq->ox_id = bfa_os_htons(rrq_oxid); + rrq->rx_id = FC_RXID_ANY; + + return (sizeof(struct fc_rrq_s)); +} + +u16 +fc_logo_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, + u16 ox_id) +{ + struct fc_els_cmd_s *acc = pld; + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + memset(acc, 0, sizeof(struct fc_els_cmd_s)); + acc->els_code = FC_ELS_ACC; + + return (sizeof(struct fc_els_cmd_s)); +} + +u16 +fc_ls_rjt_build(struct fchs_s *fchs, struct fc_ls_rjt_s *ls_rjt, u32 d_id, + u32 s_id, u16 ox_id, u8 reason_code, + u8 reason_code_expl) +{ + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + memset(ls_rjt, 0, sizeof(struct fc_ls_rjt_s)); + + ls_rjt->els_cmd.els_code = FC_ELS_LS_RJT; + ls_rjt->reason_code = reason_code; + ls_rjt->reason_code_expl = reason_code_expl; + ls_rjt->vendor_unique = 0x00; + + return (sizeof(struct fc_ls_rjt_s)); +} + +u16 +fc_ba_acc_build(struct fchs_s *fchs, struct fc_ba_acc_s *ba_acc, u32 d_id, + u32 s_id, u16 ox_id, u16 rx_id) +{ + fc_bls_rsp_build(fchs, d_id, s_id, ox_id); + + bfa_os_memcpy(ba_acc, &ba_acc_tmpl, sizeof(struct fc_ba_acc_s)); + + fchs->rx_id = rx_id; + + ba_acc->ox_id = fchs->ox_id; + ba_acc->rx_id = fchs->rx_id; + + return (sizeof(struct fc_ba_acc_s)); +} + +u16 +fc_ls_acc_build(struct fchs_s *fchs, struct fc_els_cmd_s *els_cmd, + u32 d_id, u32 s_id, u16 ox_id) +{ + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + memset(els_cmd, 0, sizeof(struct fc_els_cmd_s)); + els_cmd->els_code = FC_ELS_ACC; + + return (sizeof(struct fc_els_cmd_s)); +} + +int +fc_logout_params_pages(struct fchs_s *fc_frame, u8 els_code) +{ + int num_pages = 0; + struct fc_prlo_s *prlo; + struct fc_tprlo_s *tprlo; + + if (els_code == FC_ELS_PRLO) { + prlo = (struct fc_prlo_s *) (fc_frame + 1); + num_pages = (bfa_os_ntohs(prlo->payload_len) - 4) / 16; + } else { + tprlo = (struct fc_tprlo_s *) (fc_frame + 1); + num_pages = (bfa_os_ntohs(tprlo->payload_len) - 4) / 16; + } + return num_pages; +} + +u16 +fc_tprlo_acc_build(struct fchs_s *fchs, struct fc_tprlo_acc_s *tprlo_acc, + u32 d_id, u32 s_id, u16 ox_id, + int num_pages) +{ + int page; + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + memset(tprlo_acc, 0, (num_pages * 16) + 4); + tprlo_acc->command = FC_ELS_ACC; + + tprlo_acc->page_len = 0x10; + tprlo_acc->payload_len = bfa_os_htons((num_pages * 16) + 4); + + for (page = 0; page < num_pages; page++) { + tprlo_acc->tprlo_acc_params[page].opa_valid = 0; + tprlo_acc->tprlo_acc_params[page].rpa_valid = 0; + tprlo_acc->tprlo_acc_params[page].fc4type_csp = FC_TYPE_FCP; + tprlo_acc->tprlo_acc_params[page].orig_process_assc = 0; + tprlo_acc->tprlo_acc_params[page].resp_process_assc = 0; + } + return (bfa_os_ntohs(tprlo_acc->payload_len)); +} + +u16 +fc_prlo_acc_build(struct fchs_s *fchs, struct fc_prlo_acc_s *prlo_acc, + u32 d_id, u32 s_id, u16 ox_id, + int num_pages) +{ + int page; + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + memset(prlo_acc, 0, (num_pages * 16) + 4); + prlo_acc->command = FC_ELS_ACC; + prlo_acc->page_len = 0x10; + prlo_acc->payload_len = bfa_os_htons((num_pages * 16) + 4); + + for (page = 0; page < num_pages; page++) { + prlo_acc->prlo_acc_params[page].opa_valid = 0; + prlo_acc->prlo_acc_params[page].rpa_valid = 0; + prlo_acc->prlo_acc_params[page].fc4type_csp = FC_TYPE_FCP; + prlo_acc->prlo_acc_params[page].orig_process_assc = 0; + prlo_acc->prlo_acc_params[page].resp_process_assc = 0; + } + + return (bfa_os_ntohs(prlo_acc->payload_len)); +} + +u16 +fc_rnid_build(struct fchs_s *fchs, struct fc_rnid_cmd_s *rnid, u32 d_id, + u32 s_id, u16 ox_id, u32 data_format) +{ + fc_els_req_build(fchs, d_id, s_id, ox_id); + + memset(rnid, 0, sizeof(struct fc_rnid_cmd_s)); + + rnid->els_cmd.els_code = FC_ELS_RNID; + rnid->node_id_data_format = data_format; + + return (sizeof(struct fc_rnid_cmd_s)); +} + +u16 +fc_rnid_acc_build(struct fchs_s *fchs, struct fc_rnid_acc_s *rnid_acc, + u32 d_id, u32 s_id, u16 ox_id, + u32 data_format, + struct fc_rnid_common_id_data_s *common_id_data, + struct fc_rnid_general_topology_data_s *gen_topo_data) +{ + memset(rnid_acc, 0, sizeof(struct fc_rnid_acc_s)); + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + rnid_acc->els_cmd.els_code = FC_ELS_ACC; + rnid_acc->node_id_data_format = data_format; + rnid_acc->common_id_data_length = + sizeof(struct fc_rnid_common_id_data_s); + rnid_acc->common_id_data = *common_id_data; + + if (data_format == RNID_NODEID_DATA_FORMAT_DISCOVERY) { + rnid_acc->specific_id_data_length = + sizeof(struct fc_rnid_general_topology_data_s); + bfa_os_assign(rnid_acc->gen_topology_data, *gen_topo_data); + return (sizeof(struct fc_rnid_acc_s)); + } else { + return (sizeof(struct fc_rnid_acc_s) - + sizeof(struct fc_rnid_general_topology_data_s)); + } + +} + +u16 +fc_rpsc_build(struct fchs_s *fchs, struct fc_rpsc_cmd_s *rpsc, u32 d_id, + u32 s_id, u16 ox_id) +{ + fc_els_req_build(fchs, d_id, s_id, ox_id); + + memset(rpsc, 0, sizeof(struct fc_rpsc_cmd_s)); + + rpsc->els_cmd.els_code = FC_ELS_RPSC; + return (sizeof(struct fc_rpsc_cmd_s)); +} + +u16 +fc_rpsc2_build(struct fchs_s *fchs, struct fc_rpsc2_cmd_s *rpsc2, + u32 d_id, u32 s_id, u32 *pid_list, + u16 npids) +{ + u32 dctlr_id = FC_DOMAIN_CTRLR(bfa_os_hton3b(d_id)); + int i = 0; + + fc_els_req_build(fchs, bfa_os_hton3b(dctlr_id), s_id, 0); + + memset(rpsc2, 0, sizeof(struct fc_rpsc2_cmd_s)); + + rpsc2->els_cmd.els_code = FC_ELS_RPSC; + rpsc2->token = bfa_os_htonl(FC_BRCD_TOKEN); + rpsc2->num_pids = bfa_os_htons(npids); + for (i = 0; i < npids; i++) + rpsc2->pid_list[i].pid = pid_list[i]; + + return (sizeof(struct fc_rpsc2_cmd_s) + ((npids - 1) * + (sizeof(u32)))); +} + +u16 +fc_rpsc_acc_build(struct fchs_s *fchs, struct fc_rpsc_acc_s *rpsc_acc, + u32 d_id, u32 s_id, u16 ox_id, + struct fc_rpsc_speed_info_s *oper_speed) +{ + memset(rpsc_acc, 0, sizeof(struct fc_rpsc_acc_s)); + + fc_els_rsp_build(fchs, d_id, s_id, ox_id); + + rpsc_acc->command = FC_ELS_ACC; + rpsc_acc->num_entries = bfa_os_htons(1); + + rpsc_acc->speed_info[0].port_speed_cap = + bfa_os_htons(oper_speed->port_speed_cap); + + rpsc_acc->speed_info[0].port_op_speed = + bfa_os_htons(oper_speed->port_op_speed); + + return (sizeof(struct fc_rpsc_acc_s)); + +} + +/* + * TBD - + * . get rid of unnecessary memsets + */ + +u16 +fc_logo_rsp_parse(struct fchs_s *fchs, int len) +{ + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + len = len; + if (els_cmd->els_code != FC_ELS_ACC) + return FC_PARSE_FAILURE; + + return FC_PARSE_OK; +} + +u16 +fc_pdisc_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size) +{ + struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1); + + bfa_os_memcpy(pdisc, &plogi_tmpl, sizeof(struct fc_logi_s)); + + pdisc->els_cmd.els_code = FC_ELS_PDISC; + fc_els_req_build(fchs, d_id, s_id, ox_id); + + pdisc->csp.rxsz = pdisc->class3.rxsz = bfa_os_htons(pdu_size); + pdisc->port_name = port_name; + pdisc->node_name = node_name; + + return (sizeof(struct fc_logi_s)); +} + +u16 +fc_pdisc_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name) +{ + struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1); + + if (len < sizeof(struct fc_logi_s)) + return (FC_PARSE_LEN_INVAL); + + if (pdisc->els_cmd.els_code != FC_ELS_ACC) + return (FC_PARSE_ACC_INVAL); + + if (!wwn_is_equal(pdisc->port_name, port_name)) + return (FC_PARSE_PWWN_NOT_EQUAL); + + if (!pdisc->class3.class_valid) + return (FC_PARSE_NWWN_NOT_EQUAL); + + if (bfa_os_ntohs(pdisc->class3.rxsz) < (FC_MIN_PDUSZ)) + return (FC_PARSE_RXSZ_INVAL); + + return (FC_PARSE_OK); +} + +u16 +fc_prlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id, + int num_pages) +{ + struct fc_prlo_s *prlo = (struct fc_prlo_s *) (fchs + 1); + int page; + + fc_els_req_build(fchs, d_id, s_id, ox_id); + memset(prlo, 0, (num_pages * 16) + 4); + prlo->command = FC_ELS_PRLO; + prlo->page_len = 0x10; + prlo->payload_len = bfa_os_htons((num_pages * 16) + 4); + + for (page = 0; page < num_pages; page++) { + prlo->prlo_params[page].type = FC_TYPE_FCP; + prlo->prlo_params[page].opa_valid = 0; + prlo->prlo_params[page].rpa_valid = 0; + prlo->prlo_params[page].orig_process_assc = 0; + prlo->prlo_params[page].resp_process_assc = 0; + } + + return (bfa_os_ntohs(prlo->payload_len)); +} + +u16 +fc_prlo_rsp_parse(struct fchs_s *fchs, int len) +{ + struct fc_prlo_acc_s *prlo = (struct fc_prlo_acc_s *) (fchs + 1); + int num_pages = 0; + int page = 0; + + len = len; + + if (prlo->command != FC_ELS_ACC) + return (FC_PARSE_FAILURE); + + num_pages = ((bfa_os_ntohs(prlo->payload_len)) - 4) / 16; + + for (page = 0; page < num_pages; page++) { + if (prlo->prlo_acc_params[page].type != FC_TYPE_FCP) + return FC_PARSE_FAILURE; + + if (prlo->prlo_acc_params[page].opa_valid != 0) + return FC_PARSE_FAILURE; + + if (prlo->prlo_acc_params[page].rpa_valid != 0) + return FC_PARSE_FAILURE; + + if (prlo->prlo_acc_params[page].orig_process_assc != 0) + return FC_PARSE_FAILURE; + + if (prlo->prlo_acc_params[page].resp_process_assc != 0) + return FC_PARSE_FAILURE; + } + return (FC_PARSE_OK); + +} + +u16 +fc_tprlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, int num_pages, + enum fc_tprlo_type tprlo_type, u32 tpr_id) +{ + struct fc_tprlo_s *tprlo = (struct fc_tprlo_s *) (fchs + 1); + int page; + + fc_els_req_build(fchs, d_id, s_id, ox_id); + memset(tprlo, 0, (num_pages * 16) + 4); + tprlo->command = FC_ELS_TPRLO; + tprlo->page_len = 0x10; + tprlo->payload_len = bfa_os_htons((num_pages * 16) + 4); + + for (page = 0; page < num_pages; page++) { + tprlo->tprlo_params[page].type = FC_TYPE_FCP; + tprlo->tprlo_params[page].opa_valid = 0; + tprlo->tprlo_params[page].rpa_valid = 0; + tprlo->tprlo_params[page].orig_process_assc = 0; + tprlo->tprlo_params[page].resp_process_assc = 0; + if (tprlo_type == FC_GLOBAL_LOGO) { + tprlo->tprlo_params[page].global_process_logout = 1; + } else if (tprlo_type == FC_TPR_LOGO) { + tprlo->tprlo_params[page].tpo_nport_valid = 1; + tprlo->tprlo_params[page].tpo_nport_id = (tpr_id); + } + } + + return (bfa_os_ntohs(tprlo->payload_len)); +} + +u16 +fc_tprlo_rsp_parse(struct fchs_s *fchs, int len) +{ + struct fc_tprlo_acc_s *tprlo = (struct fc_tprlo_acc_s *) (fchs + 1); + int num_pages = 0; + int page = 0; + + len = len; + + if (tprlo->command != FC_ELS_ACC) + return (FC_PARSE_ACC_INVAL); + + num_pages = (bfa_os_ntohs(tprlo->payload_len) - 4) / 16; + + for (page = 0; page < num_pages; page++) { + if (tprlo->tprlo_acc_params[page].type != FC_TYPE_FCP) + return (FC_PARSE_NOT_FCP); + if (tprlo->tprlo_acc_params[page].opa_valid != 0) + return (FC_PARSE_OPAFLAG_INVAL); + if (tprlo->tprlo_acc_params[page].rpa_valid != 0) + return (FC_PARSE_RPAFLAG_INVAL); + if (tprlo->tprlo_acc_params[page].orig_process_assc != 0) + return (FC_PARSE_OPA_INVAL); + if (tprlo->tprlo_acc_params[page].resp_process_assc != 0) + return (FC_PARSE_RPA_INVAL); + } + return (FC_PARSE_OK); +} + +enum fc_parse_status +fc_rrq_rsp_parse(struct fchs_s *fchs, int len) +{ + struct fc_els_cmd_s *els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + len = len; + if (els_cmd->els_code != FC_ELS_ACC) + return FC_PARSE_FAILURE; + + return FC_PARSE_OK; +} + +u16 +fc_ba_rjt_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, u32 reason_code, + u32 reason_expl) +{ + struct fc_ba_rjt_s *ba_rjt = (struct fc_ba_rjt_s *) (fchs + 1); + + fc_bls_rsp_build(fchs, d_id, s_id, ox_id); + + fchs->cat_info = FC_CAT_BA_RJT; + ba_rjt->reason_code = reason_code; + ba_rjt->reason_expl = reason_expl; + return (sizeof(struct fc_ba_rjt_s)); +} + +static void +fc_gs_cthdr_build(struct ct_hdr_s *cthdr, u32 s_id, u16 cmd_code) +{ + bfa_os_memset(cthdr, 0, sizeof(struct ct_hdr_s)); + cthdr->rev_id = CT_GS3_REVISION; + cthdr->gs_type = CT_GSTYPE_DIRSERVICE; + cthdr->gs_sub_type = CT_GSSUBTYPE_NAMESERVER; + cthdr->cmd_rsp_code = bfa_os_htons(cmd_code); +} + +static void +fc_gs_fdmi_cthdr_build(struct ct_hdr_s *cthdr, u32 s_id, u16 cmd_code) +{ + bfa_os_memset(cthdr, 0, sizeof(struct ct_hdr_s)); + cthdr->rev_id = CT_GS3_REVISION; + cthdr->gs_type = CT_GSTYPE_MGMTSERVICE; + cthdr->gs_sub_type = CT_GSSUBTYPE_HBA_MGMTSERVER; + cthdr->cmd_rsp_code = bfa_os_htons(cmd_code); +} + +static void +fc_gs_ms_cthdr_build(struct ct_hdr_s *cthdr, u32 s_id, u16 cmd_code, + u8 sub_type) +{ + bfa_os_memset(cthdr, 0, sizeof(struct ct_hdr_s)); + cthdr->rev_id = CT_GS3_REVISION; + cthdr->gs_type = CT_GSTYPE_MGMTSERVICE; + cthdr->gs_sub_type = sub_type; + cthdr->cmd_rsp_code = bfa_os_htons(cmd_code); +} + +u16 +fc_gidpn_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + wwn_t port_name) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_gidpn_req_s *gidpn = + (struct fcgs_gidpn_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_GID_PN); + + bfa_os_memset(gidpn, 0, sizeof(struct fcgs_gidpn_req_s)); + gidpn->port_name = port_name; + return (sizeof(struct fcgs_gidpn_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_gpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + u32 port_id) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + fcgs_gpnid_req_t *gpnid = (fcgs_gpnid_req_t *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_GPN_ID); + + bfa_os_memset(gpnid, 0, sizeof(fcgs_gpnid_req_t)); + gpnid->dap = port_id; + return (sizeof(fcgs_gpnid_req_t) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_gnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + u32 port_id) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + fcgs_gnnid_req_t *gnnid = (fcgs_gnnid_req_t *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_GNN_ID); + + bfa_os_memset(gnnid, 0, sizeof(fcgs_gnnid_req_t)); + gnnid->dap = port_id; + return (sizeof(fcgs_gnnid_req_t) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_ct_rsp_parse(struct ct_hdr_s *cthdr) +{ + if (bfa_os_ntohs(cthdr->cmd_rsp_code) != CT_RSP_ACCEPT) { + if (cthdr->reason_code == CT_RSN_LOGICAL_BUSY) + return FC_PARSE_BUSY; + else + return FC_PARSE_FAILURE; + } + + return FC_PARSE_OK; +} + +u16 +fc_scr_build(struct fchs_s *fchs, struct fc_scr_s *scr, u8 set_br_reg, + u32 s_id, u16 ox_id) +{ + u32 d_id = bfa_os_hton3b(FC_FABRIC_CONTROLLER); + + fc_els_req_build(fchs, d_id, s_id, ox_id); + + bfa_os_memset(scr, 0, sizeof(struct fc_scr_s)); + scr->command = FC_ELS_SCR; + scr->reg_func = FC_SCR_REG_FUNC_FULL; + if (set_br_reg) + scr->vu_reg_func = FC_VU_SCR_REG_FUNC_FABRIC_NAME_CHANGE; + + return (sizeof(struct fc_scr_s)); +} + +u16 +fc_rscn_build(struct fchs_s *fchs, struct fc_rscn_pl_s *rscn, u32 s_id, + u16 ox_id) +{ + u32 d_id = bfa_os_hton3b(FC_FABRIC_CONTROLLER); + u16 payldlen; + + fc_els_req_build(fchs, d_id, s_id, ox_id); + rscn->command = FC_ELS_RSCN; + rscn->pagelen = sizeof(rscn->event[0]); + + payldlen = sizeof(u32) + rscn->pagelen; + rscn->payldlen = bfa_os_htons(payldlen); + + rscn->event[0].format = FC_RSCN_FORMAT_PORTID; + rscn->event[0].portid = s_id; + + return (sizeof(struct fc_rscn_pl_s)); +} + +u16 +fc_rftid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + enum bfa_port_role roles) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rftid_req_s *rftid = + (struct fcgs_rftid_req_s *) (cthdr + 1); + u32 type_value, d_id = bfa_os_hton3b(FC_NAME_SERVER); + u8 index; + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_RFT_ID); + + bfa_os_memset(rftid, 0, sizeof(struct fcgs_rftid_req_s)); + + rftid->dap = s_id; + + /* By default, FCP FC4 Type is registered */ + index = FC_TYPE_FCP >> 5; + type_value = 1 << (FC_TYPE_FCP % 32); + rftid->fc4_type[index] = bfa_os_htonl(type_value); + + if (roles & BFA_PORT_ROLE_FCP_IPFC) { + index = FC_TYPE_IP >> 5; + type_value = 1 << (FC_TYPE_IP % 32); + rftid->fc4_type[index] |= bfa_os_htonl(type_value); + } + + return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rftid_build_sol(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 ox_id, u8 *fc4_bitmap, + u32 bitmap_size) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rftid_req_s *rftid = + (struct fcgs_rftid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_RFT_ID); + + bfa_os_memset(rftid, 0, sizeof(struct fcgs_rftid_req_s)); + + rftid->dap = s_id; + bfa_os_memcpy((void *)rftid->fc4_type, (void *)fc4_bitmap, + (bitmap_size < 32 ? bitmap_size : 32)); + + return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rffid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + u8 fc4_type, u8 fc4_ftrs) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rffid_req_s *rffid = + (struct fcgs_rffid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_RFF_ID); + + bfa_os_memset(rffid, 0, sizeof(struct fcgs_rffid_req_s)); + + rffid->dap = s_id; + rffid->fc4ftr_bits = fc4_ftrs; + rffid->fc4_type = fc4_type; + + return (sizeof(struct fcgs_rffid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rspnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id, + u8 *name) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rspnid_req_s *rspnid = + (struct fcgs_rspnid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, ox_id); + fc_gs_cthdr_build(cthdr, s_id, GS_RSPN_ID); + + bfa_os_memset(rspnid, 0, sizeof(struct fcgs_rspnid_req_s)); + + rspnid->dap = s_id; + rspnid->spn_len = (u8) strlen((char *)name); + strncpy((char *)rspnid->spn, (char *)name, rspnid->spn_len); + + return (sizeof(struct fcgs_rspnid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_gid_ft_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u8 fc4_type) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_gidft_req_s *gidft = + (struct fcgs_gidft_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + + fc_gs_cthdr_build(cthdr, s_id, GS_GID_FT); + + bfa_os_memset(gidft, 0, sizeof(struct fcgs_gidft_req_s)); + gidft->fc4_type = fc4_type; + gidft->domain_id = 0; + gidft->area_id = 0; + + return (sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, + wwn_t port_name) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rpnid_req_s *rpnid = + (struct fcgs_rpnid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_cthdr_build(cthdr, s_id, GS_RPN_ID); + + bfa_os_memset(rpnid, 0, sizeof(struct fcgs_rpnid_req_s)); + rpnid->port_id = port_id; + rpnid->port_name = port_name; + + return (sizeof(struct fcgs_rpnid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, + wwn_t node_name) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rnnid_req_s *rnnid = + (struct fcgs_rnnid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_cthdr_build(cthdr, s_id, GS_RNN_ID); + + bfa_os_memset(rnnid, 0, sizeof(struct fcgs_rnnid_req_s)); + rnnid->port_id = port_id; + rnnid->node_name = node_name; + + return (sizeof(struct fcgs_rnnid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rcsid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, + u32 cos) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rcsid_req_s *rcsid = + (struct fcgs_rcsid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_cthdr_build(cthdr, s_id, GS_RCS_ID); + + bfa_os_memset(rcsid, 0, sizeof(struct fcgs_rcsid_req_s)); + rcsid->port_id = port_id; + rcsid->cos = cos; + + return (sizeof(struct fcgs_rcsid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_rptid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id, + u8 port_type) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_rptid_req_s *rptid = + (struct fcgs_rptid_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_cthdr_build(cthdr, s_id, GS_RPT_ID); + + bfa_os_memset(rptid, 0, sizeof(struct fcgs_rptid_req_s)); + rptid->port_id = port_id; + rptid->port_type = port_type; + + return (sizeof(struct fcgs_rptid_req_s) + sizeof(struct ct_hdr_s)); +} + +u16 +fc_ganxt_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + struct fcgs_ganxt_req_s *ganxt = + (struct fcgs_ganxt_req_s *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_NAME_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_cthdr_build(cthdr, s_id, GS_GA_NXT); + + bfa_os_memset(ganxt, 0, sizeof(struct fcgs_ganxt_req_s)); + ganxt->port_id = port_id; + + return (sizeof(struct ct_hdr_s) + sizeof(struct fcgs_ganxt_req_s)); +} + +/* + * Builds fc hdr and ct hdr for FDMI requests. + */ +u16 +fc_fdmi_reqhdr_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 cmd_code) +{ + + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + u32 d_id = bfa_os_hton3b(FC_MGMT_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_fdmi_cthdr_build(cthdr, s_id, cmd_code); + + return (sizeof(struct ct_hdr_s)); +} + +/* + * Given a FC4 Type, this function returns a fc4 type bitmask + */ +void +fc_get_fc4type_bitmask(u8 fc4_type, u8 *bit_mask) +{ + u8 index; + u32 *ptr = (u32 *) bit_mask; + u32 type_value; + + /* + * @todo : Check for bitmask size + */ + + index = fc4_type >> 5; + type_value = 1 << (fc4_type % 32); + ptr[index] = bfa_os_htonl(type_value); + +} + +/* + * GMAL Request + */ +u16 +fc_gmal_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + fcgs_gmal_req_t *gmal = (fcgs_gmal_req_t *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_MGMT_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_ms_cthdr_build(cthdr, s_id, GS_FC_GMAL_CMD, + CT_GSSUBTYPE_CFGSERVER); + + bfa_os_memset(gmal, 0, sizeof(fcgs_gmal_req_t)); + gmal->wwn = wwn; + + return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gmal_req_t)); +} + +/* + * GFN (Get Fabric Name) Request + */ +u16 +fc_gfn_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn) +{ + struct ct_hdr_s *cthdr = (struct ct_hdr_s *) pyld; + fcgs_gfn_req_t *gfn = (fcgs_gfn_req_t *) (cthdr + 1); + u32 d_id = bfa_os_hton3b(FC_MGMT_SERVER); + + fc_gs_fchdr_build(fchs, d_id, s_id, 0); + fc_gs_ms_cthdr_build(cthdr, s_id, GS_FC_GFN_CMD, + CT_GSSUBTYPE_CFGSERVER); + + bfa_os_memset(gfn, 0, sizeof(fcgs_gfn_req_t)); + gfn->wwn = wwn; + + return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gfn_req_t)); +} diff --git a/drivers/scsi/bfa/fcbuild.h b/drivers/scsi/bfa/fcbuild.h new file mode 100644 index 000000000000..4d248424f7b3 --- /dev/null +++ b/drivers/scsi/bfa/fcbuild.h @@ -0,0 +1,273 @@ +/* + * 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. + */ +/* + * fcbuild.h - FC link service frame building and parsing routines + */ + +#ifndef __FCBUILD_H__ +#define __FCBUILD_H__ + +#include +#include +#include +#include +#include +#include + +/* + * Utility Macros/functions + */ + +#define fcif_sof_set(_ifhdr, _sof) (_ifhdr)->sof = FC_ ## _sof +#define fcif_eof_set(_ifhdr, _eof) (_ifhdr)->eof = FC_ ## _eof + +#define wwn_is_equal(_wwn1, _wwn2) \ + (memcmp(&(_wwn1), &(_wwn2), sizeof(wwn_t)) == 0) + +#define fc_roundup(_l, _s) (((_l) + ((_s) - 1)) & ~((_s) - 1)) + +/* + * Given the fc response length, this routine will return + * the length of the actual payload bytes following the CT header. + * + * Assumes the input response length does not include the crc, eof, etc. + */ +static inline u32 +fc_get_ctresp_pyld_len(u32 resp_len) +{ + return (resp_len - sizeof(struct ct_hdr_s)); +} + +/* + * Convert bfa speed to rpsc speed value. + */ +static inline enum bfa_pport_speed +fc_rpsc_operspeed_to_bfa_speed(enum fc_rpsc_op_speed_s speed) +{ + switch (speed) { + + case RPSC_OP_SPEED_1G: + return BFA_PPORT_SPEED_1GBPS; + + case RPSC_OP_SPEED_2G: + return BFA_PPORT_SPEED_2GBPS; + + case RPSC_OP_SPEED_4G: + return BFA_PPORT_SPEED_4GBPS; + + case RPSC_OP_SPEED_8G: + return BFA_PPORT_SPEED_8GBPS; + + default: + return BFA_PPORT_SPEED_UNKNOWN; + } +} + +/* + * Convert RPSC speed to bfa speed value. + */ +static inline enum fc_rpsc_op_speed_s +fc_bfa_speed_to_rpsc_operspeed(enum bfa_pport_speed op_speed) +{ + switch (op_speed) { + + case BFA_PPORT_SPEED_1GBPS: + return RPSC_OP_SPEED_1G; + + case BFA_PPORT_SPEED_2GBPS: + return RPSC_OP_SPEED_2G; + + case BFA_PPORT_SPEED_4GBPS: + return RPSC_OP_SPEED_4G; + + case BFA_PPORT_SPEED_8GBPS: + return RPSC_OP_SPEED_8G; + + default: + return RPSC_OP_SPEED_NOT_EST; + } +} +enum fc_parse_status { + FC_PARSE_OK = 0, + FC_PARSE_FAILURE = 1, + FC_PARSE_BUSY = 2, + FC_PARSE_LEN_INVAL, + FC_PARSE_ACC_INVAL, + FC_PARSE_PWWN_NOT_EQUAL, + FC_PARSE_NWWN_NOT_EQUAL, + FC_PARSE_RXSZ_INVAL, + FC_PARSE_NOT_FCP, + FC_PARSE_OPAFLAG_INVAL, + FC_PARSE_RPAFLAG_INVAL, + FC_PARSE_OPA_INVAL, + FC_PARSE_RPA_INVAL, + +}; + +struct fc_templates_s { + struct fchs_s fc_els_req; + struct fchs_s fc_bls_req; + struct fc_logi_s plogi; + struct fc_rrq_s rrq; +}; + +void fcbuild_init(void); + +u16 fc_flogi_build(struct fchs_s *fchs, struct fc_logi_s *flogi, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name, u16 pdu_size, u8 set_npiv, + u8 set_auth, u16 local_bb_credits); +u16 fc_fdisc_build(struct fchs_s *buf, struct fc_logi_s *flogi, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name, u16 pdu_size); +u16 fc_flogi_acc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name, u16 pdu_size, + u16 local_bb_credits); +u16 fc_plogi_build(struct fchs_s *fchs, void *pld, u32 d_id, + u32 s_id, u16 ox_id, wwn_t port_name, + wwn_t node_name, u16 pdu_size); +enum fc_parse_status fc_plogi_parse(struct fchs_s *fchs); +u16 fc_abts_build(struct fchs_s *buf, u32 d_id, u32 s_id, + u16 ox_id); +enum fc_parse_status fc_abts_rsp_parse(struct fchs_s *buf, int len); +u16 fc_rrq_build(struct fchs_s *buf, struct fc_rrq_s *rrq, u32 d_id, + u32 s_id, u16 ox_id, u16 rrq_oxid); +enum fc_parse_status fc_rrq_rsp_parse(struct fchs_s *buf, int len); +u16 fc_rspnid_build(struct fchs_s *fchs, void *pld, u32 s_id, + u16 ox_id, u8 *name); +u16 fc_rftid_build(struct fchs_s *fchs, void *pld, u32 s_id, + u16 ox_id, enum bfa_port_role role); +u16 fc_rftid_build_sol(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 ox_id, u8 *fc4_bitmap, + u32 bitmap_size); +u16 fc_rffid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 ox_id, u8 fc4_type, u8 fc4_ftrs); +u16 fc_gidpn_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 ox_id, wwn_t port_name); +u16 fc_gpnid_build(struct fchs_s *fchs, void *pld, u32 s_id, + u16 ox_id, u32 port_id); +u16 fc_scr_build(struct fchs_s *fchs, struct fc_scr_s *scr, + u8 set_br_reg, u32 s_id, u16 ox_id); +u16 fc_plogi_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, + u32 s_id, u16 ox_id, + wwn_t port_name, wwn_t node_name, u16 pdu_size); + +u16 fc_adisc_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, + u32 d_id, u32 s_id, u16 ox_id, + wwn_t port_name, wwn_t node_name); +enum fc_parse_status fc_adisc_parse(struct fchs_s *fchs, void *pld, + u32 host_dap, + wwn_t node_name, wwn_t port_name); +enum fc_parse_status fc_adisc_rsp_parse(struct fc_adisc_s *adisc, int len, + wwn_t port_name, wwn_t node_name); +u16 fc_adisc_acc_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, + u32 d_id, u32 s_id, u16 ox_id, + wwn_t port_name, wwn_t node_name); +u16 fc_ls_rjt_build(struct fchs_s *fchs, struct fc_ls_rjt_s *ls_rjt, + u32 d_id, u32 s_id, u16 ox_id, + u8 reason_code, u8 reason_code_expl); +u16 fc_ls_acc_build(struct fchs_s *fchs, struct fc_els_cmd_s *els_cmd, + u32 d_id, u32 s_id, u16 ox_id); +u16 fc_prli_build(struct fchs_s *fchs, void *pld, u32 d_id, + u32 s_id, u16 ox_id); +enum fc_parse_status fc_prli_rsp_parse(struct fc_prli_s *prli, int len); + +u16 fc_prli_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, + u32 s_id, u16 ox_id, + enum bfa_port_role role); +u16 fc_rnid_build(struct fchs_s *fchs, struct fc_rnid_cmd_s *rnid, + u32 d_id, u32 s_id, u16 ox_id, + u32 data_format); +u16 fc_rnid_acc_build(struct fchs_s *fchs, struct fc_rnid_acc_s *rnid_acc, + u32 d_id, u32 s_id, u16 ox_id, + u32 data_format, + struct fc_rnid_common_id_data_s *common_id_data, + struct fc_rnid_general_topology_data_s * + gen_topo_data); +u16 fc_rpsc2_build(struct fchs_s *fchs, struct fc_rpsc2_cmd_s *rps2c, + u32 d_id, u32 s_id, + u32 *pid_list, u16 npids); +u16 fc_rpsc_build(struct fchs_s *fchs, struct fc_rpsc_cmd_s *rpsc, + u32 d_id, u32 s_id, u16 ox_id); +u16 fc_rpsc_acc_build(struct fchs_s *fchs, struct fc_rpsc_acc_s *rpsc_acc, + u32 d_id, u32 s_id, u16 ox_id, + struct fc_rpsc_speed_info_s *oper_speed); +u16 fc_gid_ft_build(struct fchs_s *fchs, void *pld, u32 s_id, + u8 fc4_type); +u16 fc_rpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u32 port_id, wwn_t port_name); +u16 fc_rnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u32 port_id, wwn_t node_name); +u16 fc_rcsid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u32 port_id, u32 cos); +u16 fc_rptid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u32 port_id, u8 port_type); +u16 fc_ganxt_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u32 port_id); +u16 fc_logo_build(struct fchs_s *fchs, struct fc_logo_s *logo, + u32 d_id, u32 s_id, u16 ox_id, + wwn_t port_name); +u16 fc_logo_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, + u32 s_id, u16 ox_id); +u16 fc_fdmi_reqhdr_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 cmd_code); +u16 fc_gmal_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, + wwn_t wwn); +u16 fc_gfn_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, + wwn_t wwn); +void fc_get_fc4type_bitmask(u8 fc4_type, u8 *bit_mask); +void fc_els_req_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id); +enum fc_parse_status fc_els_rsp_parse(struct fchs_s *fchs, int len); +enum fc_parse_status fc_plogi_rsp_parse(struct fchs_s *fchs, int len, + wwn_t port_name); +enum fc_parse_status fc_prli_parse(struct fc_prli_s *prli); +enum fc_parse_status fc_pdisc_parse(struct fchs_s *fchs, wwn_t node_name, + wwn_t port_name); +u16 fc_ba_acc_build(struct fchs_s *fchs, struct fc_ba_acc_s *ba_acc, + u32 d_id, u32 s_id, u16 ox_id, + u16 rx_id); +int fc_logout_params_pages(struct fchs_s *fc_frame, u8 els_code); +u16 fc_tprlo_acc_build(struct fchs_s *fchs, + struct fc_tprlo_acc_s *tprlo_acc, + u32 d_id, u32 s_id, u16 ox_id, + int num_pages); +u16 fc_prlo_acc_build(struct fchs_s *fchs, struct fc_prlo_acc_s *prlo_acc, + u32 d_id, u32 s_id, u16 ox_id, + int num_pages); +u16 fc_logo_rsp_parse(struct fchs_s *fchs, int len); +u16 fc_pdisc_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, wwn_t port_name, wwn_t node_name, + u16 pdu_size); +u16 fc_pdisc_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name); +u16 fc_prlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, int num_pages); +u16 fc_prlo_rsp_parse(struct fchs_s *fchs, int len); +u16 fc_tprlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, int num_pages, + enum fc_tprlo_type tprlo_type, u32 tpr_id); +u16 fc_tprlo_rsp_parse(struct fchs_s *fchs, int len); +u16 fc_ba_rjt_build(struct fchs_s *fchs, u32 d_id, u32 s_id, + u16 ox_id, u32 reason_code, + u32 reason_expl); +u16 fc_gnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, + u16 ox_id, u32 port_id); +u16 fc_ct_rsp_parse(struct ct_hdr_s *cthdr); +u16 fc_rscn_build(struct fchs_s *fchs, struct fc_rscn_pl_s *rscn, + u32 s_id, u16 ox_id); +#endif diff --git a/drivers/scsi/bfa/fcpim.c b/drivers/scsi/bfa/fcpim.c new file mode 100644 index 000000000000..8ce5d8934677 --- /dev/null +++ b/drivers/scsi/bfa/fcpim.c @@ -0,0 +1,844 @@ +/* + * 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. + */ + +/** + * fcpim.c - FCP initiator mode i-t nexus state machine + */ + +#include +#include +#include "fcs_fcpim.h" +#include "fcs_rport.h" +#include "fcs_lport.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs.h" +#include +#include +#include + +BFA_TRC_FILE(FCS, FCPIM); + +/* + * forward declarations + */ +static void bfa_fcs_itnim_timeout(void *arg); +static void bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim); +static void bfa_fcs_itnim_send_prli(void *itnim_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_itnim_prli_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim, + enum bfa_itnim_aen_event event); + +/** + * fcs_itnim_sm FCS itnim state machine events + */ + +enum bfa_fcs_itnim_event { + BFA_FCS_ITNIM_SM_ONLINE = 1, /* rport online event */ + BFA_FCS_ITNIM_SM_OFFLINE = 2, /* rport offline */ + BFA_FCS_ITNIM_SM_FRMSENT = 3, /* prli frame is sent */ + BFA_FCS_ITNIM_SM_RSP_OK = 4, /* good response */ + BFA_FCS_ITNIM_SM_RSP_ERROR = 5, /* error response */ + BFA_FCS_ITNIM_SM_TIMEOUT = 6, /* delay timeout */ + BFA_FCS_ITNIM_SM_HCB_OFFLINE = 7, /* BFA online callback */ + BFA_FCS_ITNIM_SM_HCB_ONLINE = 8, /* BFA offline callback */ + BFA_FCS_ITNIM_SM_INITIATOR = 9, /* rport is initiator */ + BFA_FCS_ITNIM_SM_DELETE = 10, /* delete event from rport */ + BFA_FCS_ITNIM_SM_PRLO = 11, /* delete event from rport */ +}; + +static void bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); +static void bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event); + +static struct bfa_sm_table_s itnim_sm_table[] = { + {BFA_SM(bfa_fcs_itnim_sm_offline), BFA_ITNIM_OFFLINE}, + {BFA_SM(bfa_fcs_itnim_sm_prli_send), BFA_ITNIM_PRLI_SEND}, + {BFA_SM(bfa_fcs_itnim_sm_prli), BFA_ITNIM_PRLI_SENT}, + {BFA_SM(bfa_fcs_itnim_sm_prli_retry), BFA_ITNIM_PRLI_RETRY}, + {BFA_SM(bfa_fcs_itnim_sm_hcb_online), BFA_ITNIM_HCB_ONLINE}, + {BFA_SM(bfa_fcs_itnim_sm_online), BFA_ITNIM_ONLINE}, + {BFA_SM(bfa_fcs_itnim_sm_hcb_offline), BFA_ITNIM_HCB_OFFLINE}, + {BFA_SM(bfa_fcs_itnim_sm_initiator), BFA_ITNIM_INITIATIOR}, +}; + +/** + * fcs_itnim_sm FCS itnim state machine + */ + +static void +bfa_fcs_itnim_sm_offline(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_ONLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send); + bfa_fcs_itnim_send_prli(itnim, NULL); + break; + + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_INITIATOR: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } + +} + +static void +bfa_fcs_itnim_sm_prli_send(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_FRMSENT: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli); + break; + + case BFA_FCS_ITNIM_SM_INITIATOR: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); + bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe); + break; + + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcxp_walloc_cancel(itnim->fcs->bfa, &itnim->fcxp_wqe); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_itnim_sm_prli(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_RSP_OK: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_online); + bfa_itnim_online(itnim->bfa_itnim, itnim->seq_rec); + break; + + case BFA_FCS_ITNIM_SM_RSP_ERROR: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_retry); + bfa_timer_start(itnim->fcs->bfa, &itnim->timer, + bfa_fcs_itnim_timeout, itnim, + BFA_FCS_RETRY_TIMEOUT); + break; + + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcxp_discard(itnim->fcxp); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_INITIATOR: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); + /* + * dont discard fcxp. accept will reach same state + */ + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcxp_discard(itnim->fcxp); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_itnim_sm_prli_retry(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_TIMEOUT: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_prli_send); + bfa_fcs_itnim_send_prli(itnim, NULL); + break; + + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_timer_stop(&itnim->timer); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_INITIATOR: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_initiator); + bfa_timer_stop(&itnim->timer); + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_timer_stop(&itnim->timer); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_itnim_sm_hcb_online(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_HCB_ONLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_online); + bfa_fcb_itnim_online(itnim->itnim_drv); + bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_ONLINE); + break; + + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_itnim_offline(itnim->bfa_itnim); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline); + bfa_fcb_itnim_offline(itnim->itnim_drv); + bfa_itnim_offline(itnim->bfa_itnim); + if (bfa_fcs_port_is_online(itnim->rport->port) == BFA_TRUE) { + bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_DISCONNECT); + } else { + bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_OFFLINE); + } + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_itnim_sm_hcb_offline(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_HCB_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + +/* + * This state is set when a discovered rport is also in intiator mode. + * This ITN is marked as no_op and is not active and will not be truned into + * online state. + */ +static void +bfa_fcs_itnim_sm_initiator(struct bfa_fcs_itnim_s *itnim, + enum bfa_fcs_itnim_event event) +{ + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_trc(itnim->fcs, event); + + switch (event) { + case BFA_FCS_ITNIM_SM_OFFLINE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_rport_itnim_ack(itnim->rport); + break; + + case BFA_FCS_ITNIM_SM_RSP_ERROR: + case BFA_FCS_ITNIM_SM_ONLINE: + case BFA_FCS_ITNIM_SM_INITIATOR: + break; + + case BFA_FCS_ITNIM_SM_DELETE: + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + bfa_fcs_itnim_free(itnim); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * itnim_private FCS ITNIM private interfaces + */ + +static void +bfa_fcs_itnim_aen_post(struct bfa_fcs_itnim_s *itnim, + enum bfa_itnim_aen_event event) +{ + struct bfa_fcs_rport_s *rport = itnim->rport; + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = rport->fcs->logm; + wwn_t lpwwn = bfa_fcs_port_get_pwwn(rport->port); + wwn_t rpwwn = rport->pwwn; + char lpwwn_ptr[BFA_STRING_32]; + char rpwwn_ptr[BFA_STRING_32]; + + /* + * Don't post events for well known addresses + */ + if (BFA_FCS_PID_IS_WKA(rport->pid)) + return; + + wwn2str(lpwwn_ptr, lpwwn); + wwn2str(rpwwn_ptr, rpwwn); + + switch (event) { + case BFA_ITNIM_AEN_ONLINE: + bfa_log(logmod, BFA_AEN_ITNIM_ONLINE, rpwwn_ptr, lpwwn_ptr); + break; + case BFA_ITNIM_AEN_OFFLINE: + bfa_log(logmod, BFA_AEN_ITNIM_OFFLINE, rpwwn_ptr, lpwwn_ptr); + break; + case BFA_ITNIM_AEN_DISCONNECT: + bfa_log(logmod, BFA_AEN_ITNIM_DISCONNECT, rpwwn_ptr, lpwwn_ptr); + break; + default: + break; + } + + aen_data.itnim.vf_id = rport->port->fabric->vf_id; + aen_data.itnim.ppwwn = + bfa_fcs_port_get_pwwn(bfa_fcs_get_base_port(itnim->fcs)); + aen_data.itnim.lpwwn = lpwwn; + aen_data.itnim.rpwwn = rpwwn; +} + +static void +bfa_fcs_itnim_send_prli(void *itnim_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_itnim_s *itnim = itnim_cbarg; + struct bfa_fcs_rport_s *rport = itnim->rport; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(itnim->fcs, itnim->rport->pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + itnim->stats.fcxp_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &itnim->fcxp_wqe, + bfa_fcs_itnim_send_prli, itnim); + return; + } + itnim->fcxp = fcxp; + + len = fc_prli_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), itnim->rport->pid, + bfa_fcs_port_get_fcid(port), 0); + + bfa_fcxp_send(fcxp, rport->bfa_rport, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, len, &fchs, + bfa_fcs_itnim_prli_response, (void *)itnim, FC_MAX_PDUSZ, + FC_RA_TOV); + + itnim->stats.prli_sent++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_FRMSENT); +} + +static void +bfa_fcs_itnim_prli_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cbarg; + struct fc_els_cmd_s *els_cmd; + struct fc_prli_s *prli_resp; + struct fc_ls_rjt_s *ls_rjt; + struct fc_prli_params_s *sparams; + + bfa_trc(itnim->fcs, req_status); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + itnim->stats.prli_rsp_err++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR); + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + + if (els_cmd->els_code == FC_ELS_ACC) { + prli_resp = (struct fc_prli_s *) els_cmd; + + if (fc_prli_rsp_parse(prli_resp, rsp_len) != FC_PARSE_OK) { + bfa_trc(itnim->fcs, rsp_len); + /* + * Check if this r-port is also in Initiator mode. + * If so, we need to set this ITN as a no-op. + */ + if (prli_resp->parampage.servparams.initiator) { + bfa_trc(itnim->fcs, prli_resp->parampage.type); + itnim->rport->scsi_function = + BFA_RPORT_INITIATOR; + itnim->stats.prli_rsp_acc++; + bfa_sm_send_event(itnim, + BFA_FCS_ITNIM_SM_INITIATOR); + return; + } + + itnim->stats.prli_rsp_parse_err++; + return; + } + itnim->rport->scsi_function = BFA_RPORT_TARGET; + + sparams = &prli_resp->parampage.servparams; + itnim->seq_rec = sparams->retry; + itnim->rec_support = sparams->rec_support; + itnim->task_retry_id = sparams->task_retry_id; + itnim->conf_comp = sparams->confirm; + + itnim->stats.prli_rsp_acc++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_OK); + } else { + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + + bfa_trc(itnim->fcs, ls_rjt->reason_code); + bfa_trc(itnim->fcs, ls_rjt->reason_code_expl); + + itnim->stats.prli_rsp_rjt++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_RSP_ERROR); + } +} + +static void +bfa_fcs_itnim_timeout(void *arg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)arg; + + itnim->stats.timeout++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_TIMEOUT); +} + +static void +bfa_fcs_itnim_free(struct bfa_fcs_itnim_s *itnim) +{ + bfa_itnim_delete(itnim->bfa_itnim); + bfa_fcb_itnim_free(itnim->fcs->bfad, itnim->itnim_drv); +} + + + +/** + * itnim_public FCS ITNIM public interfaces + */ + +/** + * Called by rport when a new rport is created. + * + * @param[in] rport - remote port. + */ +struct bfa_fcs_itnim_s * +bfa_fcs_itnim_create(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_port_s *port = rport->port; + struct bfa_fcs_itnim_s *itnim; + struct bfad_itnim_s *itnim_drv; + struct bfa_itnim_s *bfa_itnim; + + /* + * call bfad to allocate the itnim + */ + bfa_fcb_itnim_alloc(port->fcs->bfad, &itnim, &itnim_drv); + if (itnim == NULL) { + bfa_trc(port->fcs, rport->pwwn); + return NULL; + } + + /* + * Initialize itnim + */ + itnim->rport = rport; + itnim->fcs = rport->fcs; + itnim->itnim_drv = itnim_drv; + + /* + * call BFA to create the itnim + */ + bfa_itnim = bfa_itnim_create(port->fcs->bfa, rport->bfa_rport, itnim); + + if (bfa_itnim == NULL) { + bfa_trc(port->fcs, rport->pwwn); + bfa_fcb_itnim_free(port->fcs->bfad, itnim_drv); + bfa_assert(0); + return NULL; + } + + itnim->bfa_itnim = bfa_itnim; + itnim->seq_rec = BFA_FALSE; + itnim->rec_support = BFA_FALSE; + itnim->conf_comp = BFA_FALSE; + itnim->task_retry_id = BFA_FALSE; + + /* + * Set State machine + */ + bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_offline); + + return itnim; +} + +/** + * Called by rport to delete the instance of FCPIM. + * + * @param[in] rport - remote port. + */ +void +bfa_fcs_itnim_delete(struct bfa_fcs_itnim_s *itnim) +{ + bfa_trc(itnim->fcs, itnim->rport->pid); + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_DELETE); +} + +/** + * Notification from rport that PLOGI is complete to initiate FC-4 session. + */ +void +bfa_fcs_itnim_rport_online(struct bfa_fcs_itnim_s *itnim) +{ + itnim->stats.onlines++; + + if (!BFA_FCS_PID_IS_WKA(itnim->rport->pid)) { + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_ONLINE); + } else { + /* + * For well known addresses, we set the itnim to initiator + * state + */ + itnim->stats.initiator++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_INITIATOR); + } +} + +/** + * Called by rport to handle a remote device offline. + */ +void +bfa_fcs_itnim_rport_offline(struct bfa_fcs_itnim_s *itnim) +{ + itnim->stats.offlines++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_OFFLINE); +} + +/** + * Called by rport when remote port is known to be an initiator from + * PRLI received. + */ +void +bfa_fcs_itnim_is_initiator(struct bfa_fcs_itnim_s *itnim) +{ + bfa_trc(itnim->fcs, itnim->rport->pid); + itnim->stats.initiator++; + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_INITIATOR); +} + +/** + * Called by rport to check if the itnim is online. + */ +bfa_status_t +bfa_fcs_itnim_get_online_state(struct bfa_fcs_itnim_s *itnim) +{ + bfa_trc(itnim->fcs, itnim->rport->pid); + switch (bfa_sm_to_state(itnim_sm_table, itnim->sm)) { + case BFA_ITNIM_ONLINE: + case BFA_ITNIM_INITIATIOR: + return BFA_STATUS_OK; + + default: + return BFA_STATUS_NO_FCPIM_NEXUS; + + } +} + +/** + * BFA completion callback for bfa_itnim_online(). + */ +void +bfa_cb_itnim_online(void *cbarg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cbarg; + + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_ONLINE); +} + +/** + * BFA completion callback for bfa_itnim_offline(). + */ +void +bfa_cb_itnim_offline(void *cb_arg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cb_arg; + + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_HCB_OFFLINE); +} + +/** + * Mark the beginning of PATH TOV handling. IO completion callbacks + * are still pending. + */ +void +bfa_cb_itnim_tov_begin(void *cb_arg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cb_arg; + + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_fcb_itnim_tov_begin(itnim->itnim_drv); +} + +/** + * Mark the end of PATH TOV handling. All pending IOs are already cleaned up. + */ +void +bfa_cb_itnim_tov(void *cb_arg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cb_arg; + + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_fcb_itnim_tov(itnim->itnim_drv); +} + +/** + * BFA notification to FCS/driver for second level error recovery. + * + * Atleast one I/O request has timedout and target is unresponsive to + * repeated abort requests. Second level error recovery should be initiated + * by starting implicit logout and recovery procedures. + */ +void +bfa_cb_itnim_sler(void *cb_arg) +{ + struct bfa_fcs_itnim_s *itnim = (struct bfa_fcs_itnim_s *)cb_arg; + + itnim->stats.sler++; + bfa_trc(itnim->fcs, itnim->rport->pwwn); + bfa_fcs_rport_logo_imp(itnim->rport); +} + +struct bfa_fcs_itnim_s * +bfa_fcs_itnim_lookup(struct bfa_fcs_port_s *port, wwn_t rpwwn) +{ + struct bfa_fcs_rport_s *rport; + rport = bfa_fcs_rport_lookup(port, rpwwn); + + if (!rport) + return NULL; + + bfa_assert(rport->itnim != NULL); + return (rport->itnim); +} + +bfa_status_t +bfa_fcs_itnim_attr_get(struct bfa_fcs_port_s *port, wwn_t rpwwn, + struct bfa_itnim_attr_s *attr) +{ + struct bfa_fcs_itnim_s *itnim = NULL; + + itnim = bfa_fcs_itnim_lookup(port, rpwwn); + + if (itnim == NULL) + return BFA_STATUS_NO_FCPIM_NEXUS; + + attr->state = bfa_sm_to_state(itnim_sm_table, itnim->sm); + attr->retry = itnim->seq_rec; + attr->rec_support = itnim->rec_support; + attr->conf_comp = itnim->conf_comp; + attr->task_retry_id = itnim->task_retry_id; + + return BFA_STATUS_OK; +} + +bfa_status_t +bfa_fcs_itnim_stats_get(struct bfa_fcs_port_s *port, wwn_t rpwwn, + struct bfa_itnim_stats_s *stats) +{ + struct bfa_fcs_itnim_s *itnim = NULL; + + bfa_assert(port != NULL); + + itnim = bfa_fcs_itnim_lookup(port, rpwwn); + + if (itnim == NULL) + return BFA_STATUS_NO_FCPIM_NEXUS; + + bfa_os_memcpy(stats, &itnim->stats, sizeof(struct bfa_itnim_stats_s)); + + return BFA_STATUS_OK; +} + +bfa_status_t +bfa_fcs_itnim_stats_clear(struct bfa_fcs_port_s *port, wwn_t rpwwn) +{ + struct bfa_fcs_itnim_s *itnim = NULL; + + bfa_assert(port != NULL); + + itnim = bfa_fcs_itnim_lookup(port, rpwwn); + + if (itnim == NULL) + return BFA_STATUS_NO_FCPIM_NEXUS; + + bfa_os_memset(&itnim->stats, 0, sizeof(struct bfa_itnim_stats_s)); + return BFA_STATUS_OK; +} + +void +bfa_fcs_fcpim_uf_recv(struct bfa_fcs_itnim_s *itnim, struct fchs_s *fchs, + u16 len) +{ + struct fc_els_cmd_s *els_cmd; + + bfa_trc(itnim->fcs, fchs->type); + + if (fchs->type != FC_TYPE_ELS) + return; + + els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + bfa_trc(itnim->fcs, els_cmd->els_code); + + switch (els_cmd->els_code) { + case FC_ELS_PRLO: + /* bfa_sm_send_event(itnim, BFA_FCS_ITNIM_SM_PRLO); */ + break; + + default: + bfa_assert(0); + } +} + +void +bfa_fcs_itnim_pause(struct bfa_fcs_itnim_s *itnim) +{ +} + +void +bfa_fcs_itnim_resume(struct bfa_fcs_itnim_s *itnim) +{ +} + +/** + * Module initialization + */ +void +bfa_fcs_fcpim_modinit(struct bfa_fcs_s *fcs) +{ +} + +/** + * Module cleanup + */ +void +bfa_fcs_fcpim_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} + + diff --git a/drivers/scsi/bfa/fcptm.c b/drivers/scsi/bfa/fcptm.c new file mode 100644 index 000000000000..8c8b08c72e7a --- /dev/null +++ b/drivers/scsi/bfa/fcptm.c @@ -0,0 +1,68 @@ +/* + * 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. + */ + +/** + * This file contains dummy FCPTM routines to aid in Initiator Mode only + * compilation of OS driver. + * + */ + +#include "bfa_os_inc.h" +#include "fcs_rport.h" +#include "fcs_fcptm.h" +#include "fcs/bfa_fcs_rport.h" + +struct bfa_fcs_tin_s * +bfa_fcs_tin_create(struct bfa_fcs_rport_s *rport) +{ + return NULL; +} + +void +bfa_fcs_tin_delete(struct bfa_fcs_tin_s *tin) +{ +} + +void +bfa_fcs_tin_rport_offline(struct bfa_fcs_tin_s *tin) +{ +} + +void +bfa_fcs_tin_rport_online(struct bfa_fcs_tin_s *tin) +{ +} + +void +bfa_fcs_tin_rx_prli(struct bfa_fcs_tin_s *tin, struct fchs_s *fchs, u16 len) +{ +} + +void +bfa_fcs_fcptm_uf_recv(struct bfa_fcs_tin_s *tin, struct fchs_s *fchs, u16 len) +{ +} + +void +bfa_fcs_tin_pause(struct bfa_fcs_tin_s *tin) +{ +} + +void +bfa_fcs_tin_resume(struct bfa_fcs_tin_s *tin) +{ +} diff --git a/drivers/scsi/bfa/fcs.h b/drivers/scsi/bfa/fcs.h new file mode 100644 index 000000000000..deee685e8478 --- /dev/null +++ b/drivers/scsi/bfa/fcs.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/** + * fcs.h FCS module functions + */ + + +#ifndef __FCS_H__ +#define __FCS_H__ + +#define __fcs_min_cfg(__fcs) (__fcs)->min_cfg + +void bfa_fcs_modexit_comp(struct bfa_fcs_s *fcs); + +#endif /* __FCS_H__ */ diff --git a/drivers/scsi/bfa/fcs_auth.h b/drivers/scsi/bfa/fcs_auth.h new file mode 100644 index 000000000000..65d155fea3d7 --- /dev/null +++ b/drivers/scsi/bfa/fcs_auth.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/** + * fcs_uf.h FCS unsolicited frame receive + */ + + +#ifndef __FCS_AUTH_H__ +#define __FCS_AUTH_H__ + +#include +#include +#include + +/* + * fcs friend functions: only between fcs modules + */ +void bfa_fcs_auth_uf_recv(struct bfa_fcs_fabric_s *fabric, int len); +void bfa_fcs_auth_start(struct bfa_fcs_fabric_s *fabric); +void bfa_fcs_auth_stop(struct bfa_fcs_fabric_s *fabric); + +#endif /* __FCS_UF_H__ */ diff --git a/drivers/scsi/bfa/fcs_fabric.h b/drivers/scsi/bfa/fcs_fabric.h new file mode 100644 index 000000000000..eee960820f86 --- /dev/null +++ b/drivers/scsi/bfa/fcs_fabric.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +/** + * fcs_lport.h FCS logical port interfaces + */ + +#ifndef __FCS_FABRIC_H__ +#define __FCS_FABRIC_H__ + +#include +#include +#include + +/* +* fcs friend functions: only between fcs modules + */ +void bfa_fcs_fabric_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_fabric_modexit(struct bfa_fcs_s *fcs); +void bfa_fcs_fabric_modsusp(struct bfa_fcs_s *fcs); +void bfa_fcs_fabric_link_up(struct bfa_fcs_fabric_s *fabric); +void bfa_fcs_fabric_link_down(struct bfa_fcs_fabric_s *fabric); +void bfa_fcs_fabric_addvport(struct bfa_fcs_fabric_s *fabric, + struct bfa_fcs_vport_s *vport); +void bfa_fcs_fabric_delvport(struct bfa_fcs_fabric_s *fabric, + struct bfa_fcs_vport_s *vport); +int bfa_fcs_fabric_is_online(struct bfa_fcs_fabric_s *fabric); +struct bfa_fcs_vport_s *bfa_fcs_fabric_vport_lookup( + struct bfa_fcs_fabric_s *fabric, wwn_t pwwn); +void bfa_fcs_fabric_modstart(struct bfa_fcs_s *fcs); +void bfa_fcs_fabric_uf_recv(struct bfa_fcs_fabric_s *fabric, + struct fchs_s *fchs, u16 len); +u16 bfa_fcs_fabric_vport_count(struct bfa_fcs_fabric_s *fabric); +bfa_boolean_t bfa_fcs_fabric_is_loopback(struct bfa_fcs_fabric_s *fabric); +enum bfa_pport_type bfa_fcs_fabric_port_type(struct bfa_fcs_fabric_s *fabric); +void bfa_fcs_fabric_psymb_init(struct bfa_fcs_fabric_s *fabric); +void bfa_fcs_fabric_port_delete_comp(struct bfa_fcs_fabric_s *fabric); + +bfa_status_t bfa_fcs_fabric_addvf(struct bfa_fcs_fabric_s *vf, + struct bfa_fcs_s *fcs, struct bfa_port_cfg_s *port_cfg, + struct bfad_vf_s *vf_drv); +void bfa_fcs_auth_finished(struct bfa_fcs_fabric_s *fabric, + enum auth_status status); + +void bfa_fcs_fabric_set_fabric_name(struct bfa_fcs_fabric_s *fabric, + wwn_t fabric_name); +#endif /* __FCS_FABRIC_H__ */ diff --git a/drivers/scsi/bfa/fcs_fcpim.h b/drivers/scsi/bfa/fcs_fcpim.h new file mode 100644 index 000000000000..61e9e2687de3 --- /dev/null +++ b/drivers/scsi/bfa/fcs_fcpim.h @@ -0,0 +1,44 @@ +/* + * 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. + */ +#ifndef __FCS_FCPIM_H__ +#define __FCS_FCPIM_H__ + +#include +#include +#include + +/* + * Following routines are from FCPIM and will be called by rport. + */ +struct bfa_fcs_itnim_s *bfa_fcs_itnim_create(struct bfa_fcs_rport_s *rport); +void bfa_fcs_itnim_delete(struct bfa_fcs_itnim_s *itnim); +void bfa_fcs_itnim_rport_offline(struct bfa_fcs_itnim_s *itnim); +void bfa_fcs_itnim_rport_online(struct bfa_fcs_itnim_s *itnim); +bfa_status_t bfa_fcs_itnim_get_online_state(struct bfa_fcs_itnim_s *itnim); + +void bfa_fcs_itnim_is_initiator(struct bfa_fcs_itnim_s *itnim); +void bfa_fcs_itnim_pause(struct bfa_fcs_itnim_s *itnim); +void bfa_fcs_itnim_resume(struct bfa_fcs_itnim_s *itnim); + +/* + * Modudle init/cleanup routines. + */ +void bfa_fcs_fcpim_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_fcpim_modexit(struct bfa_fcs_s *fcs); +void bfa_fcs_fcpim_uf_recv(struct bfa_fcs_itnim_s *itnim, struct fchs_s *fchs, + u16 len); +#endif /* __FCS_FCPIM_H__ */ diff --git a/drivers/scsi/bfa/fcs_fcptm.h b/drivers/scsi/bfa/fcs_fcptm.h new file mode 100644 index 000000000000..ffff0829fd31 --- /dev/null +++ b/drivers/scsi/bfa/fcs_fcptm.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef __FCS_FCPTM_H__ +#define __FCS_FCPTM_H__ + +#include +#include +#include + +/* + * Following routines are from FCPTM and will be called by rport. + */ +struct bfa_fcs_tin_s *bfa_fcs_tin_create(struct bfa_fcs_rport_s *rport); +void bfa_fcs_tin_rport_offline(struct bfa_fcs_tin_s *tin); +void bfa_fcs_tin_rport_online(struct bfa_fcs_tin_s *tin); +void bfa_fcs_tin_delete(struct bfa_fcs_tin_s *tin); +void bfa_fcs_tin_rx_prli(struct bfa_fcs_tin_s *tin, struct fchs_s *fchs, + u16 len); +void bfa_fcs_tin_pause(struct bfa_fcs_tin_s *tin); +void bfa_fcs_tin_resume(struct bfa_fcs_tin_s *tin); + +/* + * Modudle init/cleanup routines. + */ +void bfa_fcs_fcptm_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_fcptm_modexit(struct bfa_fcs_s *fcs); +void bfa_fcs_fcptm_uf_recv(struct bfa_fcs_tin_s *tin, struct fchs_s *fchs, + u16 len); + +#endif /* __FCS_FCPTM_H__ */ diff --git a/drivers/scsi/bfa/fcs_fcxp.h b/drivers/scsi/bfa/fcs_fcxp.h new file mode 100644 index 000000000000..8277fe9c2b70 --- /dev/null +++ b/drivers/scsi/bfa/fcs_fcxp.h @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/** + * fcs_fcxp.h FCXP helper macros for FCS + */ + + +#ifndef __FCS_FCXP_H__ +#define __FCS_FCXP_H__ + +#define bfa_fcs_fcxp_alloc(__fcs) \ + bfa_fcxp_alloc(NULL, (__fcs)->bfa, 0, 0, NULL, NULL, NULL, NULL) + +#endif /* __FCS_FCXP_H__ */ diff --git a/drivers/scsi/bfa/fcs_lport.h b/drivers/scsi/bfa/fcs_lport.h new file mode 100644 index 000000000000..ae744ba35671 --- /dev/null +++ b/drivers/scsi/bfa/fcs_lport.h @@ -0,0 +1,117 @@ +/* + * 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. + */ + +/** + * fcs_lport.h FCS logical port interfaces + */ + +#ifndef __FCS_LPORT_H__ +#define __FCS_LPORT_H__ + +#define __VPORT_H__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * PID used in P2P/N2N ( In Big Endian) + */ +#define N2N_LOCAL_PID 0x010000 +#define N2N_REMOTE_PID 0x020000 + +/* + * Misc Timeouts + */ +/* + * To be used when spawning a timer before retrying a failed command. Milli + * Secs. + */ +#define BFA_FCS_RETRY_TIMEOUT 2000 + +/* + * Check for Port/Vport Mode/Role + */ +#define BFA_FCS_VPORT_IS_INITIATOR_MODE(port) \ + (port->port_cfg.roles & BFA_PORT_ROLE_FCP_IM) + +#define BFA_FCS_VPORT_IS_TARGET_MODE(port) \ + (port->port_cfg.roles & BFA_PORT_ROLE_FCP_TM) + +#define BFA_FCS_VPORT_IS_IPFC_MODE(port) \ + (port->port_cfg.roles & BFA_PORT_ROLE_FCP_IPFC) + +/* + * Is this a Well Known Address + */ +#define BFA_FCS_PID_IS_WKA(pid) ((bfa_os_ntoh3b(pid) > 0xFFF000) ? 1 : 0) + +/* + * Pointer to elements within Port + */ +#define BFA_FCS_GET_HAL_FROM_PORT(port) (port->fcs->bfa) +#define BFA_FCS_GET_NS_FROM_PORT(port) (&port->port_topo.pfab.ns) +#define BFA_FCS_GET_SCN_FROM_PORT(port) (&port->port_topo.pfab.scn) +#define BFA_FCS_GET_MS_FROM_PORT(port) (&port->port_topo.pfab.ms) +#define BFA_FCS_GET_FDMI_FROM_PORT(port) (&port->port_topo.pfab.ms.fdmi) + +/* + * handler for unsolicied frames + */ +void bfa_fcs_port_uf_recv(struct bfa_fcs_port_s *lport, struct fchs_s *fchs, + u16 len); + +/* + * Following routines will be called by Fabric to indicate port + * online/offline to vport. + */ +void bfa_fcs_lport_init(struct bfa_fcs_port_s *lport, struct bfa_fcs_s *fcs, + u16 vf_id, struct bfa_port_cfg_s *port_cfg, + struct bfa_fcs_vport_s *vport); +void bfa_fcs_port_online(struct bfa_fcs_port_s *port); +void bfa_fcs_port_offline(struct bfa_fcs_port_s *port); +void bfa_fcs_port_delete(struct bfa_fcs_port_s *port); +bfa_boolean_t bfa_fcs_port_is_online(struct bfa_fcs_port_s *port); + +/* + * Lookup rport based on PID + */ +struct bfa_fcs_rport_s *bfa_fcs_port_get_rport_by_pid( + struct bfa_fcs_port_s *port, u32 pid); + +/* + * Lookup rport based on PWWN + */ +struct bfa_fcs_rport_s *bfa_fcs_port_get_rport_by_pwwn( + struct bfa_fcs_port_s *port, wwn_t pwwn); +struct bfa_fcs_rport_s *bfa_fcs_port_get_rport_by_nwwn( + struct bfa_fcs_port_s *port, wwn_t nwwn); +void bfa_fcs_port_add_rport(struct bfa_fcs_port_s *port, + struct bfa_fcs_rport_s *rport); +void bfa_fcs_port_del_rport(struct bfa_fcs_port_s *port, + struct bfa_fcs_rport_s *rport); + +void bfa_fcs_port_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_port_modexit(struct bfa_fcs_s *fcs); +void bfa_fcs_port_lip(struct bfa_fcs_port_s *port); + +#endif /* __FCS_LPORT_H__ */ diff --git a/drivers/scsi/bfa/fcs_ms.h b/drivers/scsi/bfa/fcs_ms.h new file mode 100644 index 000000000000..b6a8c12876f4 --- /dev/null +++ b/drivers/scsi/bfa/fcs_ms.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +/** + * fcs_ms.h FCS ms interfaces + */ +#ifndef __FCS_MS_H__ +#define __FCS_MS_H__ + +/* MS FCS routines */ +void bfa_fcs_port_ms_init(struct bfa_fcs_port_s *port); +void bfa_fcs_port_ms_offline(struct bfa_fcs_port_s *port); +void bfa_fcs_port_ms_online(struct bfa_fcs_port_s *port); +void bfa_fcs_port_ms_fabric_rscn(struct bfa_fcs_port_s *port); + +/* FDMI FCS routines */ +void bfa_fcs_port_fdmi_init(struct bfa_fcs_port_ms_s *ms); +void bfa_fcs_port_fdmi_offline(struct bfa_fcs_port_ms_s *ms); +void bfa_fcs_port_fdmi_online(struct bfa_fcs_port_ms_s *ms); + +#endif diff --git a/drivers/scsi/bfa/fcs_port.h b/drivers/scsi/bfa/fcs_port.h new file mode 100644 index 000000000000..abb65191dd27 --- /dev/null +++ b/drivers/scsi/bfa/fcs_port.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/** + * fcs_pport.h FCS physical port interfaces + */ + + +#ifndef __FCS_PPORT_H__ +#define __FCS_PPORT_H__ + +/* + * fcs friend functions: only between fcs modules + */ +void bfa_fcs_pport_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_pport_modexit(struct bfa_fcs_s *fcs); + +#endif /* __FCS_PPORT_H__ */ diff --git a/drivers/scsi/bfa/fcs_rport.h b/drivers/scsi/bfa/fcs_rport.h new file mode 100644 index 000000000000..f601e9d74236 --- /dev/null +++ b/drivers/scsi/bfa/fcs_rport.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +/** + * fcs_rport.h FCS rport interfaces and defines + */ + +#ifndef __FCS_RPORT_H__ +#define __FCS_RPORT_H__ + +#include + +void bfa_fcs_rport_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_rport_modexit(struct bfa_fcs_s *fcs); + +void bfa_fcs_rport_uf_recv(struct bfa_fcs_rport_s *rport, struct fchs_s *fchs, + u16 len); +void bfa_fcs_rport_scn(struct bfa_fcs_rport_s *rport); + +struct bfa_fcs_rport_s *bfa_fcs_rport_create(struct bfa_fcs_port_s *port, + u32 pid); +void bfa_fcs_rport_delete(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_online(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_offline(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_start(struct bfa_fcs_port_s *port, struct fchs_s *rx_fchs, + struct fc_logi_s *plogi_rsp); +void bfa_fcs_rport_plogi_create(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs, + struct fc_logi_s *plogi); +void bfa_fcs_rport_plogi(struct bfa_fcs_rport_s *rport, struct fchs_s *fchs, + struct fc_logi_s *plogi); +void bfa_fcs_rport_logo_imp(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_itnim_ack(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_itntm_ack(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_tin_ack(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_fcptm_offline_done(struct bfa_fcs_rport_s *rport); +int bfa_fcs_rport_get_state(struct bfa_fcs_rport_s *rport); +struct bfa_fcs_rport_s *bfa_fcs_rport_create_by_wwn(struct bfa_fcs_port_s *port, + wwn_t wwn); + + +/* Rport Features */ +void bfa_fcs_rpf_init(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rpf_rport_online(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rpf_rport_offline(struct bfa_fcs_rport_s *rport); + +#endif /* __FCS_RPORT_H__ */ diff --git a/drivers/scsi/bfa/fcs_trcmod.h b/drivers/scsi/bfa/fcs_trcmod.h new file mode 100644 index 000000000000..41b5ae8d7644 --- /dev/null +++ b/drivers/scsi/bfa/fcs_trcmod.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +/** + * fcs_trcmod.h BFA FCS trace modules + */ + +#ifndef __FCS_TRCMOD_H__ +#define __FCS_TRCMOD_H__ + +#include + +/* + * !!! Only append to the enums defined here to avoid any versioning + * !!! needed between trace utility and driver version + */ +enum { + BFA_TRC_FCS_FABRIC = 1, + BFA_TRC_FCS_VFAPI = 2, + BFA_TRC_FCS_PORT = 3, + BFA_TRC_FCS_VPORT = 4, + BFA_TRC_FCS_VP_API = 5, + BFA_TRC_FCS_VPS = 6, + BFA_TRC_FCS_RPORT = 7, + BFA_TRC_FCS_FCPIM = 8, + BFA_TRC_FCS_FCPTM = 9, + BFA_TRC_FCS_NS = 10, + BFA_TRC_FCS_SCN = 11, + BFA_TRC_FCS_LOOP = 12, + BFA_TRC_FCS_UF = 13, + BFA_TRC_FCS_PPORT = 14, + BFA_TRC_FCS_FCPIP = 15, + BFA_TRC_FCS_PORT_API = 16, + BFA_TRC_FCS_RPORT_API = 17, + BFA_TRC_FCS_AUTH = 18, + BFA_TRC_FCS_N2N = 19, + BFA_TRC_FCS_MS = 20, + BFA_TRC_FCS_FDMI = 21, + BFA_TRC_FCS_RPORT_FTRS = 22, +}; + +#endif /* __FCS_TRCMOD_H__ */ diff --git a/drivers/scsi/bfa/fcs_uf.h b/drivers/scsi/bfa/fcs_uf.h new file mode 100644 index 000000000000..96f1bdcb31ed --- /dev/null +++ b/drivers/scsi/bfa/fcs_uf.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/** + * fcs_uf.h FCS unsolicited frame receive + */ + + +#ifndef __FCS_UF_H__ +#define __FCS_UF_H__ + +/* + * fcs friend functions: only between fcs modules + */ +void bfa_fcs_uf_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_uf_modexit(struct bfa_fcs_s *fcs); + +#endif /* __FCS_UF_H__ */ diff --git a/drivers/scsi/bfa/fcs_vport.h b/drivers/scsi/bfa/fcs_vport.h new file mode 100644 index 000000000000..9e80b6a97b7f --- /dev/null +++ b/drivers/scsi/bfa/fcs_vport.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#ifndef __FCS_VPORT_H__ +#define __FCS_VPORT_H__ + +#include +#include +#include + +/* + * Modudle init/cleanup routines. + */ + +void bfa_fcs_vport_modinit(struct bfa_fcs_s *fcs); +void bfa_fcs_vport_modexit(struct bfa_fcs_s *fcs); + +void bfa_fcs_vport_cleanup(struct bfa_fcs_vport_s *vport); +void bfa_fcs_vport_online(struct bfa_fcs_vport_s *vport); +void bfa_fcs_vport_offline(struct bfa_fcs_vport_s *vport); +void bfa_fcs_vport_delete_comp(struct bfa_fcs_vport_s *vport); +u32 bfa_fcs_vport_get_max(struct bfa_fcs_s *fcs); + +#endif /* __FCS_VPORT_H__ */ + diff --git a/drivers/scsi/bfa/fdmi.c b/drivers/scsi/bfa/fdmi.c new file mode 100644 index 000000000000..b845eb272c78 --- /dev/null +++ b/drivers/scsi/bfa/fdmi.c @@ -0,0 +1,1223 @@ +/* + * 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. + */ + +/** + * port_api.c BFA FCS port + */ + + +#include +#include +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "lport_priv.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include + +BFA_TRC_FILE(FCS, FDMI); + +#define BFA_FCS_FDMI_CMD_MAX_RETRIES 2 + +/* + * forward declarations + */ +static void bfa_fcs_port_fdmi_send_rhba(void *fdmi_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_fdmi_send_rprt(void *fdmi_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_fdmi_send_rpa(void *fdmi_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_fdmi_rhba_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_fdmi_rprt_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_fdmi_rpa_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_fdmi_timeout(void *arg); +static u16 bfa_fcs_port_fdmi_build_rhba_pyld( + struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld); +static u16 bfa_fcs_port_fdmi_build_rprt_pyld( + struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld); +static u16 bfa_fcs_port_fdmi_build_rpa_pyld( + struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld); +static u16 bfa_fcs_port_fdmi_build_portattr_block( + struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld); +void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi, + struct bfa_fcs_fdmi_hba_attr_s *hba_attr); +void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi, + struct bfa_fcs_fdmi_port_attr_s *port_attr); +/** + * fcs_fdmi_sm FCS FDMI state machine + */ + +/** + * FDMI State Machine events + */ +enum port_fdmi_event { + FDMISM_EVENT_PORT_ONLINE = 1, + FDMISM_EVENT_PORT_OFFLINE = 2, + FDMISM_EVENT_RSP_OK = 4, + FDMISM_EVENT_RSP_ERROR = 5, + FDMISM_EVENT_TIMEOUT = 6, + FDMISM_EVENT_RHBA_SENT = 7, + FDMISM_EVENT_RPRT_SENT = 8, + FDMISM_EVENT_RPA_SENT = 9, +}; + +static void bfa_fcs_port_fdmi_sm_offline(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_sending_rhba(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rhba(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rhba_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_sending_rprt(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rprt(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rprt_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_sending_rpa(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rpa(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_rpa_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +static void bfa_fcs_port_fdmi_sm_online(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event); +/** + * Start in offline state - awaiting MS to send start. + */ +static void +bfa_fcs_port_fdmi_sm_offline(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + fdmi->retry_cnt = 0; + + switch (event) { + case FDMISM_EVENT_PORT_ONLINE: + if (port->vport) { + /* + * For Vports, register a new port. + */ + bfa_sm_set_state(fdmi, + bfa_fcs_port_fdmi_sm_sending_rprt); + bfa_fcs_port_fdmi_send_rprt(fdmi, NULL); + } else { + /* + * For a base port, we should first register the HBA + * atribute. The HBA attribute also contains the base + * port registration. + */ + bfa_sm_set_state(fdmi, + bfa_fcs_port_fdmi_sm_sending_rhba); + bfa_fcs_port_fdmi_send_rhba(fdmi, NULL); + } + break; + + case FDMISM_EVENT_PORT_OFFLINE: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_sending_rhba(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RHBA_SENT: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rhba); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rhba(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RSP_ERROR: + /* + * if max retries have not been reached, start timer for a + * delayed retry + */ + if (fdmi->retry_cnt++ < BFA_FCS_FDMI_CMD_MAX_RETRIES) { + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rhba_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->timer, bfa_fcs_port_fdmi_timeout, + fdmi, BFA_FCS_RETRY_TIMEOUT); + } else { + /* + * set state to offline + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + } + break; + + case FDMISM_EVENT_RSP_OK: + /* + * Initiate Register Port Attributes + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_sending_rpa); + fdmi->retry_cnt = 0; + bfa_fcs_port_fdmi_send_rpa(fdmi, NULL); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_fcxp_discard(fdmi->fcxp); + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rhba_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_sending_rhba); + bfa_fcs_port_fdmi_send_rhba(fdmi, NULL); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_timer_stop(&fdmi->timer); + break; + + default: + bfa_assert(0); + } +} + +/* +* RPRT : Register Port + */ +static void +bfa_fcs_port_fdmi_sm_sending_rprt(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RPRT_SENT: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rprt); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rprt(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RSP_ERROR: + /* + * if max retries have not been reached, start timer for a + * delayed retry + */ + if (fdmi->retry_cnt++ < BFA_FCS_FDMI_CMD_MAX_RETRIES) { + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rprt_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->timer, bfa_fcs_port_fdmi_timeout, + fdmi, BFA_FCS_RETRY_TIMEOUT); + + } else { + /* + * set state to offline + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + fdmi->retry_cnt = 0; + } + break; + + case FDMISM_EVENT_RSP_OK: + fdmi->retry_cnt = 0; + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_online); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_fcxp_discard(fdmi->fcxp); + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rprt_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_sending_rprt); + bfa_fcs_port_fdmi_send_rprt(fdmi, NULL); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_timer_stop(&fdmi->timer); + break; + + default: + bfa_assert(0); + } +} + +/* + * Register Port Attributes + */ +static void +bfa_fcs_port_fdmi_sm_sending_rpa(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RPA_SENT: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rpa); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rpa(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_RSP_ERROR: + /* + * if max retries have not been reached, start timer for a + * delayed retry + */ + if (fdmi->retry_cnt++ < BFA_FCS_FDMI_CMD_MAX_RETRIES) { + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_rpa_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(port), + &fdmi->timer, bfa_fcs_port_fdmi_timeout, + fdmi, BFA_FCS_RETRY_TIMEOUT); + } else { + /* + * set state to offline + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + fdmi->retry_cnt = 0; + } + break; + + case FDMISM_EVENT_RSP_OK: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_online); + fdmi->retry_cnt = 0; + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_fcxp_discard(fdmi->fcxp); + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_rpa_retry(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_sending_rpa); + bfa_fcs_port_fdmi_send_rpa(fdmi, NULL); + break; + + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + bfa_timer_stop(&fdmi->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_fdmi_sm_online(struct bfa_fcs_port_fdmi_s *fdmi, + enum port_fdmi_event event) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + bfa_trc(port->fcs, event); + + switch (event) { + case FDMISM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); + break; + + default: + bfa_assert(0); + } +} + + +/** +* RHBA : Register HBA Attributes. + */ +static void +bfa_fcs_port_fdmi_send_rhba(void *fdmi_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_fdmi_s *fdmi = fdmi_cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct fchs_s fchs; + int len, attr_len; + struct bfa_fcxp_s *fcxp; + u8 *pyld; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &fdmi->fcxp_wqe, + bfa_fcs_port_fdmi_send_rhba, fdmi); + return; + } + fdmi->fcxp = fcxp; + + pyld = bfa_fcxp_get_reqbuf(fcxp); + bfa_os_memset(pyld, 0, FC_MAX_PDUSZ); + + len = fc_fdmi_reqhdr_build(&fchs, pyld, bfa_fcs_port_get_fcid(port), + FDMI_RHBA); + + attr_len = bfa_fcs_port_fdmi_build_rhba_pyld(fdmi, + (u8 *) ((struct ct_hdr_s *) pyld + 1)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, (len + attr_len), &fchs, + bfa_fcs_port_fdmi_rhba_response, (void *)fdmi, + FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(fdmi, FDMISM_EVENT_RHBA_SENT); +} + +static u16 +bfa_fcs_port_fdmi_build_rhba_pyld(struct bfa_fcs_port_fdmi_s *fdmi, + u8 *pyld) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct bfa_fcs_fdmi_hba_attr_s hba_attr; /* @todo */ + struct bfa_fcs_fdmi_hba_attr_s *fcs_hba_attr = &hba_attr; /* @todo */ + struct fdmi_rhba_s *rhba = (struct fdmi_rhba_s *) pyld; + struct fdmi_attr_s *attr; + u8 *curr_ptr; + u16 len, count; + + /* + * get hba attributes + */ + bfa_fcs_fdmi_get_hbaattr(fdmi, fcs_hba_attr); + + rhba->hba_id = bfa_fcs_port_get_pwwn(port); + rhba->port_list.num_ports = bfa_os_htonl(1); + rhba->port_list.port_entry = bfa_fcs_port_get_pwwn(port); + + len = sizeof(rhba->hba_id) + sizeof(rhba->port_list); + + count = 0; + len += sizeof(rhba->hba_attr_blk.attr_count); + + /* + * fill out the invididual entries of the HBA attrib Block + */ + curr_ptr = (u8 *) &rhba->hba_attr_blk.hba_attr; + + /* + * Node Name + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_NODENAME); + attr->len = sizeof(wwn_t); + memcpy(attr->value, &bfa_fcs_port_get_nwwn(port), attr->len); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Manufacturer + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_MANUFACTURER); + attr->len = (u16) strlen(fcs_hba_attr->manufacturer); + memcpy(attr->value, fcs_hba_attr->manufacturer, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Serial Number + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_SERIALNUM); + attr->len = (u16) strlen(fcs_hba_attr->serial_num); + memcpy(attr->value, fcs_hba_attr->serial_num, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Model + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_MODEL); + attr->len = (u16) strlen(fcs_hba_attr->model); + memcpy(attr->value, fcs_hba_attr->model, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Model Desc + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_MODEL_DESC); + attr->len = (u16) strlen(fcs_hba_attr->model_desc); + memcpy(attr->value, fcs_hba_attr->model_desc, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * H/W Version + */ + if (fcs_hba_attr->hw_version[0] != '\0') { + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_HW_VERSION); + attr->len = (u16) strlen(fcs_hba_attr->hw_version); + memcpy(attr->value, fcs_hba_attr->hw_version, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + } + + /* + * Driver Version + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_DRIVER_VERSION); + attr->len = (u16) strlen(fcs_hba_attr->driver_version); + memcpy(attr->value, fcs_hba_attr->driver_version, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len;; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Option Rom Version + */ + if (fcs_hba_attr->option_rom_ver[0] != '\0') { + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_ROM_VERSION); + attr->len = (u16) strlen(fcs_hba_attr->option_rom_ver); + memcpy(attr->value, fcs_hba_attr->option_rom_ver, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + } + + /* + * f/w Version = driver version + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_FW_VERSION); + attr->len = (u16) strlen(fcs_hba_attr->driver_version); + memcpy(attr->value, fcs_hba_attr->driver_version, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * OS Name + */ + if (fcs_hba_attr->os_name[0] != '\0') { + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_OS_NAME); + attr->len = (u16) strlen(fcs_hba_attr->os_name); + memcpy(attr->value, fcs_hba_attr->os_name, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + } + + /* + * MAX_CT_PAYLOAD + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_HBA_ATTRIB_MAX_CT); + attr->len = sizeof(fcs_hba_attr->max_ct_pyld); + memcpy(attr->value, &fcs_hba_attr->max_ct_pyld, attr->len); + len += attr->len; + count++; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Update size of payload + */ + len += ((sizeof(attr->type) + sizeof(attr->len)) * count); + + rhba->hba_attr_blk.attr_count = bfa_os_htonl(count); + return len; +} + +static void +bfa_fcs_port_fdmi_rhba_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_fdmi_s *fdmi = (struct bfa_fcs_port_fdmi_s *)cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_OK); + return; + } + + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); +} + +/** +* RPRT : Register Port + */ +static void +bfa_fcs_port_fdmi_send_rprt(void *fdmi_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_fdmi_s *fdmi = fdmi_cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct fchs_s fchs; + u16 len, attr_len; + struct bfa_fcxp_s *fcxp; + u8 *pyld; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &fdmi->fcxp_wqe, + bfa_fcs_port_fdmi_send_rprt, fdmi); + return; + } + fdmi->fcxp = fcxp; + + pyld = bfa_fcxp_get_reqbuf(fcxp); + bfa_os_memset(pyld, 0, FC_MAX_PDUSZ); + + len = fc_fdmi_reqhdr_build(&fchs, pyld, bfa_fcs_port_get_fcid(port), + FDMI_RPRT); + + attr_len = bfa_fcs_port_fdmi_build_rprt_pyld(fdmi, + (u8 *) ((struct ct_hdr_s *) pyld + 1)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len + attr_len, &fchs, + bfa_fcs_port_fdmi_rprt_response, (void *)fdmi, + FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(fdmi, FDMISM_EVENT_RPRT_SENT); +} + +/** + * This routine builds Port Attribute Block that used in RPA, RPRT commands. + */ +static u16 +bfa_fcs_port_fdmi_build_portattr_block(struct bfa_fcs_port_fdmi_s *fdmi, + u8 *pyld) +{ + struct bfa_fcs_fdmi_port_attr_s fcs_port_attr; + struct fdmi_port_attr_s *port_attrib = (struct fdmi_port_attr_s *) pyld; + struct fdmi_attr_s *attr; + u8 *curr_ptr; + u16 len; + u8 count = 0; + + /* + * get port attributes + */ + bfa_fcs_fdmi_get_portattr(fdmi, &fcs_port_attr); + + len = sizeof(port_attrib->attr_count); + + /* + * fill out the invididual entries + */ + curr_ptr = (u8 *) &port_attrib->port_attr; + + /* + * FC4 Types + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_FC4_TYPES); + attr->len = sizeof(fcs_port_attr.supp_fc4_types); + memcpy(attr->value, fcs_port_attr.supp_fc4_types, attr->len); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * Supported Speed + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_SUPP_SPEED); + attr->len = sizeof(fcs_port_attr.supp_speed); + memcpy(attr->value, &fcs_port_attr.supp_speed, attr->len); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * current Port Speed + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_PORT_SPEED); + attr->len = sizeof(fcs_port_attr.curr_speed); + memcpy(attr->value, &fcs_port_attr.curr_speed, attr->len); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * max frame size + */ + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_FRAME_SIZE); + attr->len = sizeof(fcs_port_attr.max_frm_size); + memcpy(attr->value, &fcs_port_attr.max_frm_size, attr->len); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + /* + * OS Device Name + */ + if (fcs_port_attr.os_device_name[0] != '\0') { + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_DEV_NAME); + attr->len = (u16) strlen(fcs_port_attr.os_device_name); + memcpy(attr->value, fcs_port_attr.os_device_name, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + } + /* + * Host Name + */ + if (fcs_port_attr.host_name[0] != '\0') { + attr = (struct fdmi_attr_s *) curr_ptr; + attr->type = bfa_os_htons(FDMI_PORT_ATTRIB_HOST_NAME); + attr->len = (u16) strlen(fcs_port_attr.host_name); + memcpy(attr->value, fcs_port_attr.host_name, attr->len); + /* variable fields need to be 4 byte aligned */ + attr->len = fc_roundup(attr->len, sizeof(u32)); + curr_ptr += sizeof(attr->type) + sizeof(attr->len) + attr->len; + len += attr->len; + ++count; + attr->len = + bfa_os_htons(attr->len + sizeof(attr->type) + + sizeof(attr->len)); + + } + + /* + * Update size of payload + */ + port_attrib->attr_count = bfa_os_htonl(count); + len += ((sizeof(attr->type) + sizeof(attr->len)) * count); + return len; +} + +static u16 +bfa_fcs_port_fdmi_build_rprt_pyld(struct bfa_fcs_port_fdmi_s *fdmi, + u8 *pyld) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct fdmi_rprt_s *rprt = (struct fdmi_rprt_s *) pyld; + u16 len; + + rprt->hba_id = bfa_fcs_port_get_pwwn(bfa_fcs_get_base_port(port->fcs)); + rprt->port_name = bfa_fcs_port_get_pwwn(port); + + len = bfa_fcs_port_fdmi_build_portattr_block(fdmi, + (u8 *) &rprt->port_attr_blk); + + len += sizeof(rprt->hba_id) + sizeof(rprt->port_name); + + return len; +} + +static void +bfa_fcs_port_fdmi_rprt_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_fdmi_s *fdmi = (struct bfa_fcs_port_fdmi_s *)cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_OK); + return; + } + + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); +} + +/** +* RPA : Register Port Attributes. + */ +static void +bfa_fcs_port_fdmi_send_rpa(void *fdmi_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_fdmi_s *fdmi = fdmi_cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct fchs_s fchs; + u16 len, attr_len; + struct bfa_fcxp_s *fcxp; + u8 *pyld; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &fdmi->fcxp_wqe, + bfa_fcs_port_fdmi_send_rpa, fdmi); + return; + } + fdmi->fcxp = fcxp; + + pyld = bfa_fcxp_get_reqbuf(fcxp); + bfa_os_memset(pyld, 0, FC_MAX_PDUSZ); + + len = fc_fdmi_reqhdr_build(&fchs, pyld, bfa_fcs_port_get_fcid(port), + FDMI_RPA); + + attr_len = bfa_fcs_port_fdmi_build_rpa_pyld(fdmi, + (u8 *) ((struct ct_hdr_s *) pyld + 1)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len + attr_len, &fchs, + bfa_fcs_port_fdmi_rpa_response, (void *)fdmi, + FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(fdmi, FDMISM_EVENT_RPA_SENT); +} + +static u16 +bfa_fcs_port_fdmi_build_rpa_pyld(struct bfa_fcs_port_fdmi_s *fdmi, + u8 *pyld) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct fdmi_rpa_s *rpa = (struct fdmi_rpa_s *) pyld; + u16 len; + + rpa->port_name = bfa_fcs_port_get_pwwn(port); + + len = bfa_fcs_port_fdmi_build_portattr_block(fdmi, + (u8 *) &rpa->port_attr_blk); + + len += sizeof(rpa->port_name); + + return len; +} + +static void +bfa_fcs_port_fdmi_rpa_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_fdmi_s *fdmi = (struct bfa_fcs_port_fdmi_s *)cbarg; + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_OK); + return; + } + + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(fdmi, FDMISM_EVENT_RSP_ERROR); +} + +static void +bfa_fcs_port_fdmi_timeout(void *arg) +{ + struct bfa_fcs_port_fdmi_s *fdmi = (struct bfa_fcs_port_fdmi_s *)arg; + + bfa_sm_send_event(fdmi, FDMISM_EVENT_TIMEOUT); +} + +void +bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi, + struct bfa_fcs_fdmi_hba_attr_s *hba_attr) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct bfa_fcs_driver_info_s *driver_info = &port->fcs->driver_info; + struct bfa_adapter_attr_s adapter_attr; + + bfa_os_memset(hba_attr, 0, sizeof(struct bfa_fcs_fdmi_hba_attr_s)); + bfa_os_memset(&adapter_attr, 0, sizeof(struct bfa_adapter_attr_s)); + + bfa_ioc_get_adapter_attr(&port->fcs->bfa->ioc, &adapter_attr); + + strncpy(hba_attr->manufacturer, adapter_attr.manufacturer, + sizeof(adapter_attr.manufacturer)); + + strncpy(hba_attr->serial_num, adapter_attr.serial_num, + sizeof(adapter_attr.serial_num)); + + strncpy(hba_attr->model, adapter_attr.model, sizeof(hba_attr->model)); + + strncpy(hba_attr->model_desc, adapter_attr.model_descr, + sizeof(hba_attr->model_desc)); + + strncpy(hba_attr->hw_version, adapter_attr.hw_ver, + sizeof(hba_attr->hw_version)); + + strncpy(hba_attr->driver_version, (char *)driver_info->version, + sizeof(hba_attr->driver_version)); + + strncpy(hba_attr->option_rom_ver, adapter_attr.optrom_ver, + sizeof(hba_attr->option_rom_ver)); + + strncpy(hba_attr->fw_version, adapter_attr.fw_ver, + sizeof(hba_attr->fw_version)); + + strncpy(hba_attr->os_name, driver_info->host_os_name, + sizeof(hba_attr->os_name)); + + /* + * If there is a patch level, append it to the os name along with a + * separator + */ + if (driver_info->host_os_patch[0] != '\0') { + strncat(hba_attr->os_name, BFA_FCS_PORT_SYMBNAME_SEPARATOR, + sizeof(BFA_FCS_PORT_SYMBNAME_SEPARATOR)); + strncat(hba_attr->os_name, driver_info->host_os_patch, + sizeof(driver_info->host_os_patch)); + } + + hba_attr->max_ct_pyld = bfa_os_htonl(FC_MAX_PDUSZ); + +} + +void +bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi, + struct bfa_fcs_fdmi_port_attr_s *port_attr) +{ + struct bfa_fcs_port_s *port = fdmi->ms->port; + struct bfa_fcs_driver_info_s *driver_info = &port->fcs->driver_info; + struct bfa_pport_attr_s pport_attr; + + bfa_os_memset(port_attr, 0, sizeof(struct bfa_fcs_fdmi_port_attr_s)); + + /* + * get pport attributes from hal + */ + bfa_pport_get_attr(port->fcs->bfa, &pport_attr); + + /* + * get FC4 type Bitmask + */ + fc_get_fc4type_bitmask(FC_TYPE_FCP, port_attr->supp_fc4_types); + + /* + * Supported Speeds + */ + port_attr->supp_speed = bfa_os_htonl(BFA_FCS_FDMI_SUPORTED_SPEEDS); + + /* + * Current Speed + */ + port_attr->curr_speed = bfa_os_htonl(pport_attr.speed); + + /* + * Max PDU Size. + */ + port_attr->max_frm_size = bfa_os_htonl(FC_MAX_PDUSZ); + + /* + * OS device Name + */ + strncpy(port_attr->os_device_name, (char *)driver_info->os_device_name, + sizeof(port_attr->os_device_name)); + + /* + * Host name + */ + strncpy(port_attr->host_name, (char *)driver_info->host_machine_name, + sizeof(port_attr->host_name)); + +} + + +void +bfa_fcs_port_fdmi_init(struct bfa_fcs_port_ms_s *ms) +{ + struct bfa_fcs_port_fdmi_s *fdmi = &ms->fdmi; + + fdmi->ms = ms; + bfa_sm_set_state(fdmi, bfa_fcs_port_fdmi_sm_offline); +} + +void +bfa_fcs_port_fdmi_offline(struct bfa_fcs_port_ms_s *ms) +{ + struct bfa_fcs_port_fdmi_s *fdmi = &ms->fdmi; + + fdmi->ms = ms; + bfa_sm_send_event(fdmi, FDMISM_EVENT_PORT_OFFLINE); +} + +void +bfa_fcs_port_fdmi_online(struct bfa_fcs_port_ms_s *ms) +{ + struct bfa_fcs_port_fdmi_s *fdmi = &ms->fdmi; + + fdmi->ms = ms; + bfa_sm_send_event(fdmi, FDMISM_EVENT_PORT_ONLINE); +} diff --git a/drivers/scsi/bfa/include/aen/bfa_aen.h b/drivers/scsi/bfa/include/aen/bfa_aen.h new file mode 100644 index 000000000000..da8cac093d3d --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen.h @@ -0,0 +1,92 @@ +/* + * 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. + */ +#ifndef __BFA_AEN_H__ +#define __BFA_AEN_H__ + +#include "defs/bfa_defs_aen.h" + +#define BFA_AEN_MAX_ENTRY 512 + +extern s32 bfa_aen_max_cfg_entry; +struct bfa_aen_s { + void *bfad; + s32 max_entry; + s32 write_index; + s32 read_index; + u32 bfad_num; + u32 seq_num; + void (*aen_cb_notify)(void *bfad); + void (*gettimeofday)(struct bfa_timeval_s *tv); + struct bfa_trc_mod_s *trcmod; + struct bfa_aen_entry_s list[BFA_AEN_MAX_ENTRY]; /* Must be the last */ +}; + + +/** + * Public APIs + */ +static inline void +bfa_aen_set_max_cfg_entry(int max_entry) +{ + bfa_aen_max_cfg_entry = max_entry; +} + +static inline s32 +bfa_aen_get_max_cfg_entry(void) +{ + return bfa_aen_max_cfg_entry; +} + +static inline s32 +bfa_aen_get_meminfo(void) +{ + return (sizeof(struct bfa_aen_entry_s) * bfa_aen_get_max_cfg_entry()); +} + +static inline s32 +bfa_aen_get_wi(struct bfa_aen_s *aen) +{ + return aen->write_index; +} + +static inline s32 +bfa_aen_get_ri(struct bfa_aen_s *aen) +{ + return aen->read_index; +} + +static inline s32 +bfa_aen_fetch_count(struct bfa_aen_s *aen, s32 read_index) +{ + return ((aen->write_index + aen->max_entry) - read_index) + % aen->max_entry; +} + +s32 bfa_aen_init(struct bfa_aen_s *aen, struct bfa_trc_mod_s *trcmod, + void *bfad, u32 inst_id, void (*aen_cb_notify)(void *), + void (*gettimeofday)(struct bfa_timeval_s *)); + +s32 bfa_aen_post(struct bfa_aen_s *aen, enum bfa_aen_category aen_category, + int aen_type, union bfa_aen_data_u *aen_data); + +s32 bfa_aen_fetch(struct bfa_aen_s *aen, struct bfa_aen_entry_s *aen_entry, + s32 entry_space, s32 rii, s32 *ri_arr, + s32 ri_arr_cnt); + +s32 bfa_aen_get_inst(struct bfa_aen_s *aen); + +#endif /* __BFA_AEN_H__ */ diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_adapter.h b/drivers/scsi/bfa/include/aen/bfa_aen_adapter.h new file mode 100644 index 000000000000..260d3ea1cab3 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_adapter.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +/* messages define for BFA_AEN_CAT_ADAPTER Module */ +#ifndef __bfa_aen_adapter_h__ +#define __bfa_aen_adapter_h__ + +#include +#include + +#define BFA_AEN_ADAPTER_ADD \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ADAPTER, BFA_ADAPTER_AEN_ADD) +#define BFA_AEN_ADAPTER_REMOVE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ADAPTER, BFA_ADAPTER_AEN_REMOVE) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_audit.h b/drivers/scsi/bfa/include/aen/bfa_aen_audit.h new file mode 100644 index 000000000000..12cd7aab5d53 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_audit.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +/* messages define for BFA_AEN_CAT_AUDIT Module */ +#ifndef __bfa_aen_audit_h__ +#define __bfa_aen_audit_h__ + +#include +#include + +#define BFA_AEN_AUDIT_AUTH_ENABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_AUDIT, BFA_AUDIT_AEN_AUTH_ENABLE) +#define BFA_AEN_AUDIT_AUTH_DISABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_AUDIT, BFA_AUDIT_AEN_AUTH_DISABLE) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_ethport.h b/drivers/scsi/bfa/include/aen/bfa_aen_ethport.h new file mode 100644 index 000000000000..507d0b58d149 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_ethport.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +/* messages define for BFA_AEN_CAT_ETHPORT Module */ +#ifndef __bfa_aen_ethport_h__ +#define __bfa_aen_ethport_h__ + +#include +#include + +#define BFA_AEN_ETHPORT_LINKUP \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ETHPORT, BFA_ETHPORT_AEN_LINKUP) +#define BFA_AEN_ETHPORT_LINKDOWN \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ETHPORT, BFA_ETHPORT_AEN_LINKDOWN) +#define BFA_AEN_ETHPORT_ENABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ETHPORT, BFA_ETHPORT_AEN_ENABLE) +#define BFA_AEN_ETHPORT_DISABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ETHPORT, BFA_ETHPORT_AEN_DISABLE) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_ioc.h b/drivers/scsi/bfa/include/aen/bfa_aen_ioc.h new file mode 100644 index 000000000000..71378b446b69 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_ioc.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/* messages define for BFA_AEN_CAT_IOC Module */ +#ifndef __bfa_aen_ioc_h__ +#define __bfa_aen_ioc_h__ + +#include +#include + +#define BFA_AEN_IOC_HBGOOD \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_IOC, BFA_IOC_AEN_HBGOOD) +#define BFA_AEN_IOC_HBFAIL \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_IOC, BFA_IOC_AEN_HBFAIL) +#define BFA_AEN_IOC_ENABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_IOC, BFA_IOC_AEN_ENABLE) +#define BFA_AEN_IOC_DISABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_IOC, BFA_IOC_AEN_DISABLE) +#define BFA_AEN_IOC_FWMISMATCH \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_IOC, BFA_IOC_AEN_FWMISMATCH) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_itnim.h b/drivers/scsi/bfa/include/aen/bfa_aen_itnim.h new file mode 100644 index 000000000000..a7d8ddcfef99 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_itnim.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/* messages define for BFA_AEN_CAT_ITNIM Module */ +#ifndef __bfa_aen_itnim_h__ +#define __bfa_aen_itnim_h__ + +#include +#include + +#define BFA_AEN_ITNIM_ONLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ITNIM, BFA_ITNIM_AEN_ONLINE) +#define BFA_AEN_ITNIM_OFFLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ITNIM, BFA_ITNIM_AEN_OFFLINE) +#define BFA_AEN_ITNIM_DISCONNECT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_ITNIM, BFA_ITNIM_AEN_DISCONNECT) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_lport.h b/drivers/scsi/bfa/include/aen/bfa_aen_lport.h new file mode 100644 index 000000000000..5a8ebb65193f --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_lport.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +/* messages define for BFA_AEN_CAT_LPORT Module */ +#ifndef __bfa_aen_lport_h__ +#define __bfa_aen_lport_h__ + +#include +#include + +#define BFA_AEN_LPORT_NEW \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NEW) +#define BFA_AEN_LPORT_DELETE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_DELETE) +#define BFA_AEN_LPORT_ONLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_ONLINE) +#define BFA_AEN_LPORT_OFFLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_OFFLINE) +#define BFA_AEN_LPORT_DISCONNECT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_DISCONNECT) +#define BFA_AEN_LPORT_NEW_PROP \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NEW_PROP) +#define BFA_AEN_LPORT_DELETE_PROP \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_DELETE_PROP) +#define BFA_AEN_LPORT_NEW_STANDARD \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NEW_STANDARD) +#define BFA_AEN_LPORT_DELETE_STANDARD \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_DELETE_STANDARD) +#define BFA_AEN_LPORT_NPIV_DUP_WWN \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NPIV_DUP_WWN) +#define BFA_AEN_LPORT_NPIV_FABRIC_MAX \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NPIV_FABRIC_MAX) +#define BFA_AEN_LPORT_NPIV_UNKNOWN \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_LPORT, BFA_LPORT_AEN_NPIV_UNKNOWN) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_port.h b/drivers/scsi/bfa/include/aen/bfa_aen_port.h new file mode 100644 index 000000000000..9add905a622d --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_port.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +/* messages define for BFA_AEN_CAT_PORT Module */ +#ifndef __bfa_aen_port_h__ +#define __bfa_aen_port_h__ + +#include +#include + +#define BFA_AEN_PORT_ONLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_ONLINE) +#define BFA_AEN_PORT_OFFLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_OFFLINE) +#define BFA_AEN_PORT_RLIR \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_RLIR) +#define BFA_AEN_PORT_SFP_INSERT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_SFP_INSERT) +#define BFA_AEN_PORT_SFP_REMOVE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_SFP_REMOVE) +#define BFA_AEN_PORT_SFP_POM \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_SFP_POM) +#define BFA_AEN_PORT_ENABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_ENABLE) +#define BFA_AEN_PORT_DISABLE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_DISABLE) +#define BFA_AEN_PORT_AUTH_ON \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_AUTH_ON) +#define BFA_AEN_PORT_AUTH_OFF \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_AUTH_OFF) +#define BFA_AEN_PORT_DISCONNECT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_DISCONNECT) +#define BFA_AEN_PORT_QOS_NEG \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_QOS_NEG) +#define BFA_AEN_PORT_FABRIC_NAME_CHANGE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_FABRIC_NAME_CHANGE) +#define BFA_AEN_PORT_SFP_ACCESS_ERROR \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_SFP_ACCESS_ERROR) +#define BFA_AEN_PORT_SFP_UNSUPPORT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_PORT, BFA_PORT_AEN_SFP_UNSUPPORT) + +#endif + diff --git a/drivers/scsi/bfa/include/aen/bfa_aen_rport.h b/drivers/scsi/bfa/include/aen/bfa_aen_rport.h new file mode 100644 index 000000000000..7e4be1fd5e15 --- /dev/null +++ b/drivers/scsi/bfa/include/aen/bfa_aen_rport.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/* messages define for BFA_AEN_CAT_RPORT Module */ +#ifndef __bfa_aen_rport_h__ +#define __bfa_aen_rport_h__ + +#include +#include + +#define BFA_AEN_RPORT_ONLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_RPORT, BFA_RPORT_AEN_ONLINE) +#define BFA_AEN_RPORT_OFFLINE \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_RPORT, BFA_RPORT_AEN_OFFLINE) +#define BFA_AEN_RPORT_DISCONNECT \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_RPORT, BFA_RPORT_AEN_DISCONNECT) +#define BFA_AEN_RPORT_QOS_PRIO \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_RPORT, BFA_RPORT_AEN_QOS_PRIO) +#define BFA_AEN_RPORT_QOS_FLOWID \ + BFA_LOG_CREATE_ID(BFA_AEN_CAT_RPORT, BFA_RPORT_AEN_QOS_FLOWID) + +#endif + diff --git a/drivers/scsi/bfa/include/bfa.h b/drivers/scsi/bfa/include/bfa.h new file mode 100644 index 000000000000..64c1412c5703 --- /dev/null +++ b/drivers/scsi/bfa/include/bfa.h @@ -0,0 +1,177 @@ +/* + * 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. + */ +#ifndef __BFA_H__ +#define __BFA_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct bfa_s; +#include + +struct bfa_pcidev_s; + +/** + * PCI devices supported by the current BFA + */ +struct bfa_pciid_s { + u16 device_id; + u16 vendor_id; +}; + +extern char bfa_version[]; + +/** + * BFA Power Mgmt Commands + */ +enum bfa_pm_cmd { + BFA_PM_CTL_D0 = 0, + BFA_PM_CTL_D1 = 1, + BFA_PM_CTL_D2 = 2, + BFA_PM_CTL_D3 = 3, +}; + +/** + * BFA memory resources + */ +enum bfa_mem_type { + BFA_MEM_TYPE_KVA = 1, /*! Kernel Virtual Memory *(non-dma-able) */ + BFA_MEM_TYPE_DMA = 2, /*! DMA-able memory */ + BFA_MEM_TYPE_MAX = BFA_MEM_TYPE_DMA, +}; + +struct bfa_mem_elem_s { + enum bfa_mem_type mem_type; /* see enum bfa_mem_type */ + u32 mem_len; /* Total Length in Bytes */ + u8 *kva; /* kernel virtual address */ + u64 dma; /* dma address if DMA memory */ + u8 *kva_curp; /* kva allocation cursor */ + u64 dma_curp; /* dma allocation cursor */ +}; + +struct bfa_meminfo_s { + struct bfa_mem_elem_s meminfo[BFA_MEM_TYPE_MAX]; +}; +#define bfa_meminfo_kva(_m) \ + (_m)->meminfo[BFA_MEM_TYPE_KVA - 1].kva_curp +#define bfa_meminfo_dma_virt(_m) \ + (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].kva_curp +#define bfa_meminfo_dma_phys(_m) \ + (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].dma_curp + +/** + * Generic Scatter Gather Element used by driver + */ +struct bfa_sge_s { + u32 sg_len; + void *sg_addr; +}; + +#define bfa_sge_to_be(__sge) do { \ + ((u32 *)(__sge))[0] = bfa_os_htonl(((u32 *)(__sge))[0]); \ + ((u32 *)(__sge))[1] = bfa_os_htonl(((u32 *)(__sge))[1]); \ + ((u32 *)(__sge))[2] = bfa_os_htonl(((u32 *)(__sge))[2]); \ +} while (0) + + +/* + * bfa stats interfaces + */ +#define bfa_stats(_mod, _stats) (_mod)->stats._stats ++ + +#define bfa_ioc_get_stats(__bfa, __ioc_stats) \ + bfa_ioc_fetch_stats(&(__bfa)->ioc, __ioc_stats) +#define bfa_ioc_clear_stats(__bfa) \ + bfa_ioc_clr_stats(&(__bfa)->ioc) + +/* + * bfa API functions + */ +void bfa_get_pciids(struct bfa_pciid_s **pciids, int *npciids); +void bfa_cfg_get_default(struct bfa_iocfc_cfg_s *cfg); +void bfa_cfg_get_min(struct bfa_iocfc_cfg_s *cfg); +void bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo); +void bfa_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, + struct bfa_meminfo_s *meminfo, + struct bfa_pcidev_s *pcidev); +void bfa_init_trc(struct bfa_s *bfa, struct bfa_trc_mod_s *trcmod); +void bfa_init_log(struct bfa_s *bfa, struct bfa_log_mod_s *logmod); +void bfa_init_aen(struct bfa_s *bfa, struct bfa_aen_s *aen); +void bfa_init_plog(struct bfa_s *bfa, struct bfa_plog_s *plog); +void bfa_detach(struct bfa_s *bfa); +void bfa_init(struct bfa_s *bfa); +void bfa_start(struct bfa_s *bfa); +void bfa_stop(struct bfa_s *bfa); +void bfa_attach_fcs(struct bfa_s *bfa); +void bfa_cb_init(void *bfad, bfa_status_t status); +void bfa_cb_stop(void *bfad, bfa_status_t status); +void bfa_cb_updateq(void *bfad, bfa_status_t status); + +bfa_boolean_t bfa_intx(struct bfa_s *bfa); +void bfa_isr_enable(struct bfa_s *bfa); +void bfa_isr_disable(struct bfa_s *bfa); +void bfa_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap, + u32 *num_vecs, u32 *max_vec_bit); +#define bfa_msix(__bfa, __vec) (__bfa)->msix.handler[__vec](__bfa, __vec) + +void bfa_comp_deq(struct bfa_s *bfa, struct list_head *comp_q); +void bfa_comp_process(struct bfa_s *bfa, struct list_head *comp_q); +void bfa_comp_free(struct bfa_s *bfa, struct list_head *comp_q); + +typedef void (*bfa_cb_ioc_t) (void *cbarg, enum bfa_status status); +void bfa_iocfc_get_attr(struct bfa_s *bfa, struct bfa_iocfc_attr_s *attr); +bfa_status_t bfa_iocfc_get_stats(struct bfa_s *bfa, + struct bfa_iocfc_stats_s *stats, + bfa_cb_ioc_t cbfn, void *cbarg); +bfa_status_t bfa_iocfc_clear_stats(struct bfa_s *bfa, + bfa_cb_ioc_t cbfn, void *cbarg); +void bfa_get_attr(struct bfa_s *bfa, struct bfa_ioc_attr_s *ioc_attr); + +void bfa_adapter_get_attr(struct bfa_s *bfa, + struct bfa_adapter_attr_s *ad_attr); +u64 bfa_adapter_get_id(struct bfa_s *bfa); + +bfa_status_t bfa_iocfc_israttr_set(struct bfa_s *bfa, + struct bfa_iocfc_intr_attr_s *attr); + +void bfa_iocfc_enable(struct bfa_s *bfa); +void bfa_iocfc_disable(struct bfa_s *bfa); +void bfa_ioc_auto_recover(bfa_boolean_t auto_recover); +void bfa_cb_ioc_disable(void *bfad); +void bfa_timer_tick(struct bfa_s *bfa); +#define bfa_timer_start(_bfa, _timer, _timercb, _arg, _timeout) \ + bfa_timer_begin(&(_bfa)->timer_mod, _timer, _timercb, _arg, _timeout) + +/* + * BFA debug API functions + */ +bfa_status_t bfa_debug_fwtrc(struct bfa_s *bfa, void *trcdata, int *trclen); +bfa_status_t bfa_debug_fwsave(struct bfa_s *bfa, void *trcdata, int *trclen); + +#include "bfa_priv.h" + +#endif /* __BFA_H__ */ diff --git a/drivers/scsi/bfa/include/bfa_fcpim.h b/drivers/scsi/bfa/include/bfa_fcpim.h new file mode 100644 index 000000000000..04789795fa53 --- /dev/null +++ b/drivers/scsi/bfa/include/bfa_fcpim.h @@ -0,0 +1,159 @@ +/* + * 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. + */ + +#ifndef __BFA_FCPIM_H__ +#define __BFA_FCPIM_H__ + +#include +#include +#include +#include + +/* + * forward declarations + */ +struct bfa_itnim_s; +struct bfa_ioim_s; +struct bfa_tskim_s; +struct bfad_ioim_s; +struct bfad_tskim_s; + +/* + * bfa fcpim module API functions + */ +void bfa_fcpim_path_tov_set(struct bfa_s *bfa, u16 path_tov); +u16 bfa_fcpim_path_tov_get(struct bfa_s *bfa); +void bfa_fcpim_qdepth_set(struct bfa_s *bfa, u16 q_depth); +u16 bfa_fcpim_qdepth_get(struct bfa_s *bfa); +bfa_status_t bfa_fcpim_get_modstats(struct bfa_s *bfa, + struct bfa_fcpim_stats_s *modstats); +bfa_status_t bfa_fcpim_clr_modstats(struct bfa_s *bfa); + +/* + * bfa itnim API functions + */ +struct bfa_itnim_s *bfa_itnim_create(struct bfa_s *bfa, + struct bfa_rport_s *rport, void *itnim); +void bfa_itnim_delete(struct bfa_itnim_s *itnim); +void bfa_itnim_online(struct bfa_itnim_s *itnim, + bfa_boolean_t seq_rec); +void bfa_itnim_offline(struct bfa_itnim_s *itnim); +void bfa_itnim_get_stats(struct bfa_itnim_s *itnim, + struct bfa_itnim_hal_stats_s *stats); +void bfa_itnim_clear_stats(struct bfa_itnim_s *itnim); + + +/** + * BFA completion callback for bfa_itnim_online(). + * + * @param[in] itnim FCS or driver itnim instance + * + * return None + */ +void bfa_cb_itnim_online(void *itnim); + +/** + * BFA completion callback for bfa_itnim_offline(). + * + * @param[in] itnim FCS or driver itnim instance + * + * return None + */ +void bfa_cb_itnim_offline(void *itnim); +void bfa_cb_itnim_tov_begin(void *itnim); +void bfa_cb_itnim_tov(void *itnim); + +/** + * BFA notification to FCS/driver for second level error recovery. + * + * Atleast one I/O request has timedout and target is unresponsive to + * repeated abort requests. Second level error recovery should be initiated + * by starting implicit logout and recovery procedures. + * + * @param[in] itnim FCS or driver itnim instance + * + * return None + */ +void bfa_cb_itnim_sler(void *itnim); + +/* + * bfa ioim API functions + */ +struct bfa_ioim_s *bfa_ioim_alloc(struct bfa_s *bfa, + struct bfad_ioim_s *dio, + struct bfa_itnim_s *itnim, + u16 nsgles); + +void bfa_ioim_free(struct bfa_ioim_s *ioim); +void bfa_ioim_start(struct bfa_ioim_s *ioim); +void bfa_ioim_abort(struct bfa_ioim_s *ioim); +void bfa_ioim_delayed_comp(struct bfa_ioim_s *ioim, + bfa_boolean_t iotov); + + +/** + * I/O completion notification. + * + * @param[in] dio driver IO structure + * @param[in] io_status IO completion status + * @param[in] scsi_status SCSI status returned by target + * @param[in] sns_len SCSI sense length, 0 if none + * @param[in] sns_info SCSI sense data, if any + * @param[in] residue Residual length + * + * @return None + */ +void bfa_cb_ioim_done(void *bfad, struct bfad_ioim_s *dio, + enum bfi_ioim_status io_status, + u8 scsi_status, int sns_len, + u8 *sns_info, s32 residue); + +/** + * I/O good completion notification. + * + * @param[in] dio driver IO structure + * + * @return None + */ +void bfa_cb_ioim_good_comp(void *bfad, struct bfad_ioim_s *dio); + +/** + * I/O abort completion notification + * + * @param[in] dio driver IO that was aborted + * + * @return None + */ +void bfa_cb_ioim_abort(void *bfad, struct bfad_ioim_s *dio); +void bfa_cb_ioim_resfree(void *hcb_bfad); + +void bfa_cb_ioim_resfree(void *hcb_bfad); + +/* + * bfa tskim API functions + */ +struct bfa_tskim_s *bfa_tskim_alloc(struct bfa_s *bfa, + struct bfad_tskim_s *dtsk); +void bfa_tskim_free(struct bfa_tskim_s *tskim); +void bfa_tskim_start(struct bfa_tskim_s *tskim, + struct bfa_itnim_s *itnim, lun_t lun, + enum fcp_tm_cmnd tm, u8 t_secs); +void bfa_cb_tskim_done(void *bfad, struct bfad_tskim_s *dtsk, + enum bfi_tskim_status tsk_status); + +#endif /* __BFA_FCPIM_H__ */ + diff --git a/drivers/scsi/bfa/include/bfa_fcptm.h b/drivers/scsi/bfa/include/bfa_fcptm.h new file mode 100644 index 000000000000..5f5ffe0bb1bb --- /dev/null +++ b/drivers/scsi/bfa/include/bfa_fcptm.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef __BFA_FCPTM_H__ +#define __BFA_FCPTM_H__ + +#include +#include +#include + +/* + * forward declarations + */ +struct bfa_tin_s; +struct bfa_iotm_s; +struct bfa_tsktm_s; + +/* + * bfa fcptm module API functions + */ +void bfa_fcptm_path_tov_set(struct bfa_s *bfa, u16 path_tov); +u16 bfa_fcptm_path_tov_get(struct bfa_s *bfa); +void bfa_fcptm_qdepth_set(struct bfa_s *bfa, u16 q_depth); +u16 bfa_fcptm_qdepth_get(struct bfa_s *bfa); + +/* + * bfa tin API functions + */ +void bfa_tin_get_stats(struct bfa_tin_s *tin, struct bfa_tin_stats_s *stats); +void bfa_tin_clear_stats(struct bfa_tin_s *tin); + +#endif /* __BFA_FCPTM_H__ */ + diff --git a/drivers/scsi/bfa/include/bfa_svc.h b/drivers/scsi/bfa/include/bfa_svc.h new file mode 100644 index 000000000000..0c80b74f72ef --- /dev/null +++ b/drivers/scsi/bfa/include/bfa_svc.h @@ -0,0 +1,324 @@ +/* + * 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. + */ +#ifndef __BFA_SVC_H__ +#define __BFA_SVC_H__ + +/* + * forward declarations + */ +struct bfa_fcxp_s; + +#include +#include +#include +#include +#include +#include + +/** + * BFA rport information. + */ +struct bfa_rport_info_s { + u16 max_frmsz; /* max rcv pdu size */ + u32 pid : 24, /* remote port ID */ + lp_tag : 8; + u32 local_pid : 24, /* local port ID */ + cisc : 8; /* CIRO supported */ + u8 fc_class; /* supported FC classes. enum fc_cos */ + u8 vf_en; /* virtual fabric enable */ + u16 vf_id; /* virtual fabric ID */ + enum bfa_pport_speed speed; /* Rport's current speed */ +}; + +/** + * BFA rport data structure + */ +struct bfa_rport_s { + struct list_head qe; /* queue element */ + bfa_sm_t sm; /* state machine */ + struct bfa_s *bfa; /* backpointer to BFA */ + void *rport_drv; /* fcs/driver rport object */ + u16 fw_handle; /* firmware rport handle */ + u16 rport_tag; /* BFA rport tag */ + struct bfa_rport_info_s rport_info; /* rport info from *fcs/driver */ + struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */ + struct bfa_cb_qe_s hcb_qe; /* BFA callback qelem */ + struct bfa_rport_hal_stats_s stats; /* BFA rport statistics */ + struct bfa_rport_qos_attr_s qos_attr; + union a { + bfa_status_t status; /* f/w status */ + void *fw_msg; /* QoS scn event */ + } event_arg; +}; +#define BFA_RPORT_FC_COS(_rport) ((_rport)->rport_info.fc_class) + +/** + * Send completion callback. + */ +typedef void (*bfa_cb_fcxp_send_t) (void *bfad_fcxp, struct bfa_fcxp_s *fcxp, + void *cbarg, enum bfa_status req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs); + +/** + * BFA fcxp allocation (asynchronous) + */ +typedef void (*bfa_fcxp_alloc_cbfn_t) (void *cbarg, struct bfa_fcxp_s *fcxp); + +struct bfa_fcxp_wqe_s { + struct list_head qe; + bfa_fcxp_alloc_cbfn_t alloc_cbfn; + void *alloc_cbarg; +}; + +typedef u64 (*bfa_fcxp_get_sgaddr_t) (void *bfad_fcxp, int sgeid); +typedef u32 (*bfa_fcxp_get_sglen_t) (void *bfad_fcxp, int sgeid); + +#define BFA_UF_BUFSZ (2 * 1024 + 256) + +/** + * @todo private + */ +struct bfa_uf_buf_s { + u8 d[BFA_UF_BUFSZ]; +}; + + +struct bfa_uf_s { + struct list_head qe; /* queue element */ + struct bfa_s *bfa; /* bfa instance */ + u16 uf_tag; /* identifying tag f/w messages */ + u16 vf_id; + u16 src_rport_handle; + u16 rsvd; + u8 *data_ptr; + u16 data_len; /* actual receive length */ + u16 pb_len; /* posted buffer length */ + void *buf_kva; /* buffer virtual address */ + u64 buf_pa; /* buffer physical address */ + struct bfa_cb_qe_s hcb_qe; /* comp: BFA comp qelem */ + struct bfa_sge_s sges[BFI_SGE_INLINE_MAX]; +}; + +typedef void (*bfa_cb_pport_t) (void *cbarg, enum bfa_status status); + +/** + * bfa lport login/logout service interface + */ +struct bfa_lps_s { + struct list_head qe; /* queue element */ + struct bfa_s *bfa; /* parent bfa instance */ + bfa_sm_t sm; /* finite state machine */ + u8 lp_tag; /* lport tag */ + u8 reqq; /* lport request queue */ + u8 alpa; /* ALPA for loop topologies */ + u32 lp_pid; /* lport port ID */ + bfa_boolean_t fdisc; /* send FDISC instead of FLOGI*/ + bfa_boolean_t auth_en; /* enable authentication */ + bfa_boolean_t auth_req; /* authentication required */ + bfa_boolean_t npiv_en; /* NPIV is allowed by peer */ + bfa_boolean_t fport; /* attached peer is F_PORT */ + bfa_boolean_t brcd_switch;/* attached peer is brcd switch */ + bfa_status_t status; /* login status */ + u16 pdusz; /* max receive PDU size */ + u16 pr_bbcred; /* BB_CREDIT from peer */ + u8 lsrjt_rsn; /* LSRJT reason */ + u8 lsrjt_expl; /* LSRJT explanation */ + wwn_t pwwn; /* port wwn of lport */ + wwn_t nwwn; /* node wwn of lport */ + wwn_t pr_pwwn; /* port wwn of lport peer */ + wwn_t pr_nwwn; /* node wwn of lport peer */ + mac_t lp_mac; /* fpma/spma MAC for lport */ + mac_t fcf_mac; /* FCF MAC of lport */ + struct bfa_reqq_wait_s wqe; /* request wait queue element */ + void *uarg; /* user callback arg */ + struct bfa_cb_qe_s hcb_qe; /* comp: callback qelem */ + struct bfi_lps_login_rsp_s *loginrsp; + bfa_eproto_status_t ext_status; +}; + +/* + * bfa pport API functions + */ +bfa_status_t bfa_pport_enable(struct bfa_s *bfa); +bfa_status_t bfa_pport_disable(struct bfa_s *bfa); +bfa_status_t bfa_pport_cfg_speed(struct bfa_s *bfa, + enum bfa_pport_speed speed); +enum bfa_pport_speed bfa_pport_get_speed(struct bfa_s *bfa); +bfa_status_t bfa_pport_cfg_topology(struct bfa_s *bfa, + enum bfa_pport_topology topo); +enum bfa_pport_topology bfa_pport_get_topology(struct bfa_s *bfa); +bfa_status_t bfa_pport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa); +bfa_boolean_t bfa_pport_get_hardalpa(struct bfa_s *bfa, u8 *alpa); +u8 bfa_pport_get_myalpa(struct bfa_s *bfa); +bfa_status_t bfa_pport_clr_hardalpa(struct bfa_s *bfa); +bfa_status_t bfa_pport_cfg_maxfrsize(struct bfa_s *bfa, u16 maxsize); +u16 bfa_pport_get_maxfrsize(struct bfa_s *bfa); +u32 bfa_pport_mypid(struct bfa_s *bfa); +u8 bfa_pport_get_rx_bbcredit(struct bfa_s *bfa); +bfa_status_t bfa_pport_trunk_enable(struct bfa_s *bfa, u8 bitmap); +bfa_status_t bfa_pport_trunk_disable(struct bfa_s *bfa); +bfa_boolean_t bfa_pport_trunk_query(struct bfa_s *bfa, u32 *bitmap); +void bfa_pport_get_attr(struct bfa_s *bfa, struct bfa_pport_attr_s *attr); +wwn_t bfa_pport_get_wwn(struct bfa_s *bfa, bfa_boolean_t node); +bfa_status_t bfa_pport_get_stats(struct bfa_s *bfa, + union bfa_pport_stats_u *stats, + bfa_cb_pport_t cbfn, void *cbarg); +bfa_status_t bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, + void *cbarg); +void bfa_pport_event_register(struct bfa_s *bfa, + void (*event_cbfn) (void *cbarg, + bfa_pport_event_t event), void *event_cbarg); +bfa_boolean_t bfa_pport_is_disabled(struct bfa_s *bfa); +void bfa_pport_cfg_qos(struct bfa_s *bfa, bfa_boolean_t on_off); +void bfa_pport_cfg_ratelim(struct bfa_s *bfa, bfa_boolean_t on_off); +bfa_status_t bfa_pport_cfg_ratelim_speed(struct bfa_s *bfa, + enum bfa_pport_speed speed); +enum bfa_pport_speed bfa_pport_get_ratelim_speed(struct bfa_s *bfa); + +void bfa_pport_set_tx_bbcredit(struct bfa_s *bfa, u16 tx_bbcredit); +void bfa_pport_busy(struct bfa_s *bfa, bfa_boolean_t status); +void bfa_pport_beacon(struct bfa_s *bfa, bfa_boolean_t beacon, + bfa_boolean_t link_e2e_beacon); +void bfa_cb_pport_event(void *cbarg, bfa_pport_event_t event); +void bfa_pport_qos_get_attr(struct bfa_s *bfa, struct bfa_qos_attr_s *qos_attr); +void bfa_pport_qos_get_vc_attr(struct bfa_s *bfa, + struct bfa_qos_vc_attr_s *qos_vc_attr); +bfa_status_t bfa_pport_get_qos_stats(struct bfa_s *bfa, + union bfa_pport_stats_u *stats, + bfa_cb_pport_t cbfn, void *cbarg); +bfa_status_t bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, + void *cbarg); +bfa_boolean_t bfa_pport_is_ratelim(struct bfa_s *bfa); +bfa_boolean_t bfa_pport_is_linkup(struct bfa_s *bfa); + +/* + * bfa rport API functions + */ +struct bfa_rport_s *bfa_rport_create(struct bfa_s *bfa, void *rport_drv); +void bfa_rport_delete(struct bfa_rport_s *rport); +void bfa_rport_online(struct bfa_rport_s *rport, + struct bfa_rport_info_s *rport_info); +void bfa_rport_offline(struct bfa_rport_s *rport); +void bfa_rport_speed(struct bfa_rport_s *rport, enum bfa_pport_speed speed); +void bfa_rport_get_stats(struct bfa_rport_s *rport, + struct bfa_rport_hal_stats_s *stats); +void bfa_rport_clear_stats(struct bfa_rport_s *rport); +void bfa_cb_rport_online(void *rport); +void bfa_cb_rport_offline(void *rport); +void bfa_cb_rport_qos_scn_flowid(void *rport, + struct bfa_rport_qos_attr_s old_qos_attr, + struct bfa_rport_qos_attr_s new_qos_attr); +void bfa_cb_rport_qos_scn_prio(void *rport, + struct bfa_rport_qos_attr_s old_qos_attr, + struct bfa_rport_qos_attr_s new_qos_attr); +void bfa_rport_get_qos_attr(struct bfa_rport_s *rport, + struct bfa_rport_qos_attr_s *qos_attr); + +/* + * bfa fcxp API functions + */ +struct bfa_fcxp_s *bfa_fcxp_alloc(void *bfad_fcxp, struct bfa_s *bfa, + int nreq_sgles, int nrsp_sgles, + bfa_fcxp_get_sgaddr_t get_req_sga, + bfa_fcxp_get_sglen_t get_req_sglen, + bfa_fcxp_get_sgaddr_t get_rsp_sga, + bfa_fcxp_get_sglen_t get_rsp_sglen); +void bfa_fcxp_alloc_wait(struct bfa_s *bfa, struct bfa_fcxp_wqe_s *wqe, + bfa_fcxp_alloc_cbfn_t alloc_cbfn, void *cbarg); +void bfa_fcxp_walloc_cancel(struct bfa_s *bfa, + struct bfa_fcxp_wqe_s *wqe); +void bfa_fcxp_discard(struct bfa_fcxp_s *fcxp); + +void *bfa_fcxp_get_reqbuf(struct bfa_fcxp_s *fcxp); +void *bfa_fcxp_get_rspbuf(struct bfa_fcxp_s *fcxp); + +void bfa_fcxp_free(struct bfa_fcxp_s *fcxp); + +void bfa_fcxp_send(struct bfa_fcxp_s *fcxp, + struct bfa_rport_s *rport, u16 vf_id, u8 lp_tag, + bfa_boolean_t cts, enum fc_cos cos, + u32 reqlen, struct fchs_s *fchs, + bfa_cb_fcxp_send_t cbfn, + void *cbarg, + u32 rsp_maxlen, u8 rsp_timeout); +bfa_status_t bfa_fcxp_abort(struct bfa_fcxp_s *fcxp); +u32 bfa_fcxp_get_reqbufsz(struct bfa_fcxp_s *fcxp); +u32 bfa_fcxp_get_maxrsp(struct bfa_s *bfa); + +static inline void * +bfa_uf_get_frmbuf(struct bfa_uf_s *uf) +{ + return uf->data_ptr; +} + +static inline u16 +bfa_uf_get_frmlen(struct bfa_uf_s *uf) +{ + return uf->data_len; +} + +/** + * Callback prototype for unsolicited frame receive handler. + * + * @param[in] cbarg callback arg for receive handler + * @param[in] uf unsolicited frame descriptor + * + * @return None + */ +typedef void (*bfa_cb_uf_recv_t) (void *cbarg, struct bfa_uf_s *uf); + +/* + * bfa uf API functions + */ +void bfa_uf_recv_register(struct bfa_s *bfa, bfa_cb_uf_recv_t ufrecv, + void *cbarg); +void bfa_uf_free(struct bfa_uf_s *uf); + +/** + * bfa lport service api + */ + +struct bfa_lps_s *bfa_lps_alloc(struct bfa_s *bfa); +void bfa_lps_delete(struct bfa_lps_s *lps); +void bfa_lps_discard(struct bfa_lps_s *lps); +void bfa_lps_flogi(struct bfa_lps_s *lps, void *uarg, u8 alpa, u16 pdusz, + wwn_t pwwn, wwn_t nwwn, bfa_boolean_t auth_en); +void bfa_lps_fdisc(struct bfa_lps_s *lps, void *uarg, u16 pdusz, wwn_t pwwn, + wwn_t nwwn); +void bfa_lps_flogo(struct bfa_lps_s *lps); +void bfa_lps_fdisclogo(struct bfa_lps_s *lps); +u8 bfa_lps_get_tag(struct bfa_lps_s *lps); +bfa_boolean_t bfa_lps_is_npiv_en(struct bfa_lps_s *lps); +bfa_boolean_t bfa_lps_is_fport(struct bfa_lps_s *lps); +bfa_boolean_t bfa_lps_is_brcd_fabric(struct bfa_lps_s *lps); +bfa_boolean_t bfa_lps_is_authreq(struct bfa_lps_s *lps); +bfa_eproto_status_t bfa_lps_get_extstatus(struct bfa_lps_s *lps); +u32 bfa_lps_get_pid(struct bfa_lps_s *lps); +u8 bfa_lps_get_tag_from_pid(struct bfa_s *bfa, u32 pid); +u16 bfa_lps_get_peer_bbcredit(struct bfa_lps_s *lps); +wwn_t bfa_lps_get_peer_pwwn(struct bfa_lps_s *lps); +wwn_t bfa_lps_get_peer_nwwn(struct bfa_lps_s *lps); +u8 bfa_lps_get_lsrjt_rsn(struct bfa_lps_s *lps); +u8 bfa_lps_get_lsrjt_expl(struct bfa_lps_s *lps); +void bfa_cb_lps_flogi_comp(void *bfad, void *uarg, bfa_status_t status); +void bfa_cb_lps_flogo_comp(void *bfad, void *uarg); +void bfa_cb_lps_fdisc_comp(void *bfad, void *uarg, bfa_status_t status); +void bfa_cb_lps_fdisclogo_comp(void *bfad, void *uarg); + +#endif /* __BFA_SVC_H__ */ + diff --git a/drivers/scsi/bfa/include/bfa_timer.h b/drivers/scsi/bfa/include/bfa_timer.h new file mode 100644 index 000000000000..e407103fa565 --- /dev/null +++ b/drivers/scsi/bfa/include/bfa_timer.h @@ -0,0 +1,53 @@ +/* + * 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. + */ +#ifndef __BFA_TIMER_H__ +#define __BFA_TIMER_H__ + +#include +#include + +struct bfa_s; + +typedef void (*bfa_timer_cbfn_t)(void *); + +/** + * BFA timer data structure + */ +struct bfa_timer_s { + struct list_head qe; + bfa_timer_cbfn_t timercb; + void *arg; + int timeout; /**< in millisecs. */ +}; + +/** + * Timer module structure + */ +struct bfa_timer_mod_s { + struct list_head timer_q; +}; + +#define BFA_TIMER_FREQ 500 /**< specified in millisecs */ + +void bfa_timer_beat(struct bfa_timer_mod_s *mod); +void bfa_timer_init(struct bfa_timer_mod_s *mod); +void bfa_timer_begin(struct bfa_timer_mod_s *mod, struct bfa_timer_s *timer, + bfa_timer_cbfn_t timercb, void *arg, + unsigned int timeout); +void bfa_timer_stop(struct bfa_timer_s *timer); + +#endif /* __BFA_TIMER_H__ */ diff --git a/drivers/scsi/bfa/include/bfi/bfi.h b/drivers/scsi/bfa/include/bfi/bfi.h new file mode 100644 index 000000000000..6cadfe0d4ba1 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi.h @@ -0,0 +1,174 @@ +/* + * 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. + */ + +#ifndef __BFI_H__ +#define __BFI_H__ + +#include +#include + +#pragma pack(1) + +/** + * Msg header common to all msgs + */ +struct bfi_mhdr_s { + u8 msg_class; /* @ref bfi_mclass_t */ + u8 msg_id; /* msg opcode with in the class */ + union { + struct { + u8 rsvd; + u8 lpu_id; /* msg destination */ + } h2i; + u16 i2htok; /* token in msgs to host */ + } mtag; +}; + +#define bfi_h2i_set(_mh, _mc, _op, _lpuid) do { \ + (_mh).msg_class = (_mc); \ + (_mh).msg_id = (_op); \ + (_mh).mtag.h2i.lpu_id = (_lpuid); \ +} while (0) + +#define bfi_i2h_set(_mh, _mc, _op, _i2htok) do { \ + (_mh).msg_class = (_mc); \ + (_mh).msg_id = (_op); \ + (_mh).mtag.i2htok = (_i2htok); \ +} while (0) + +/* + * Message opcodes: 0-127 to firmware, 128-255 to host + */ +#define BFI_I2H_OPCODE_BASE 128 +#define BFA_I2HM(_x) ((_x) + BFI_I2H_OPCODE_BASE) + +/** + **************************************************************************** + * + * Scatter Gather Element and Page definition + * + **************************************************************************** + */ + +#define BFI_SGE_INLINE 1 +#define BFI_SGE_INLINE_MAX (BFI_SGE_INLINE + 1) + +/** + * SG Flags + */ +enum { + BFI_SGE_DATA = 0, /* data address, not last */ + BFI_SGE_DATA_CPL = 1, /* data addr, last in current page */ + BFI_SGE_DATA_LAST = 3, /* data address, last */ + BFI_SGE_LINK = 2, /* link address */ + BFI_SGE_PGDLEN = 2, /* cumulative data length for page */ +}; + +/** + * DMA addresses + */ +union bfi_addr_u { + struct { + u32 addr_lo; + u32 addr_hi; + } a32; +}; + +/** + * Scatter Gather Element + */ +struct bfi_sge_s { +#ifdef __BIGENDIAN + u32 flags : 2, + rsvd : 2, + sg_len : 28; +#else + u32 sg_len : 28, + rsvd : 2, + flags : 2; +#endif + union bfi_addr_u sga; +}; + +/** + * Scatter Gather Page + */ +#define BFI_SGPG_DATA_SGES 7 +#define BFI_SGPG_SGES_MAX (BFI_SGPG_DATA_SGES + 1) +#define BFI_SGPG_RSVD_WD_LEN 8 +struct bfi_sgpg_s { + struct bfi_sge_s sges[BFI_SGPG_SGES_MAX]; + u32 rsvd[BFI_SGPG_RSVD_WD_LEN]; +}; + +/* + * Large Message structure - 128 Bytes size Msgs + */ +#define BFI_LMSG_SZ 128 +#define BFI_LMSG_PL_WSZ \ + ((BFI_LMSG_SZ - sizeof(struct bfi_mhdr_s)) / 4) + +struct bfi_msg_s { + struct bfi_mhdr_s mhdr; + u32 pl[BFI_LMSG_PL_WSZ]; +}; + +/** + * Mailbox message structure + */ +#define BFI_MBMSG_SZ 7 +struct bfi_mbmsg_s { + struct bfi_mhdr_s mh; + u32 pl[BFI_MBMSG_SZ]; +}; + +/** + * Message Classes + */ +enum bfi_mclass { + BFI_MC_IOC = 1, /* IO Controller (IOC) */ + BFI_MC_DIAG = 2, /* Diagnostic Msgs */ + BFI_MC_FLASH = 3, /* Flash message class */ + BFI_MC_CEE = 4, + BFI_MC_FC_PORT = 5, /* FC port */ + BFI_MC_IOCFC = 6, /* FC - IO Controller (IOC) */ + BFI_MC_LL = 7, /* Link Layer */ + BFI_MC_UF = 8, /* Unsolicited frame receive */ + BFI_MC_FCXP = 9, /* FC Transport */ + BFI_MC_LPS = 10, /* lport fc login services */ + BFI_MC_RPORT = 11, /* Remote port */ + BFI_MC_ITNIM = 12, /* I-T nexus (Initiator mode) */ + BFI_MC_IOIM_READ = 13, /* read IO (Initiator mode) */ + BFI_MC_IOIM_WRITE = 14, /* write IO (Initiator mode) */ + BFI_MC_IOIM_IO = 15, /* IO (Initiator mode) */ + BFI_MC_IOIM = 16, /* IO (Initiator mode) */ + BFI_MC_IOIM_IOCOM = 17, /* good IO completion */ + BFI_MC_TSKIM = 18, /* Initiator Task management */ + BFI_MC_SBOOT = 19, /* SAN boot services */ + BFI_MC_IPFC = 20, /* IP over FC Msgs */ + BFI_MC_PORT = 21, /* Physical port */ + BFI_MC_MAX = 32 +}; + +#define BFI_IOC_MAX_CQS 4 +#define BFI_IOC_MAX_CQS_ASIC 8 +#define BFI_IOC_MSGLEN_MAX 32 /* 32 bytes */ + +#pragma pack() + +#endif /* __BFI_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_boot.h b/drivers/scsi/bfa/include/bfi/bfi_boot.h new file mode 100644 index 000000000000..5955afe7d108 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_boot.h @@ -0,0 +1,34 @@ +/* + * 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. + */ +/* + * bfi_boot.h + */ + +#ifndef __BFI_BOOT_H__ +#define __BFI_BOOT_H__ + +#define BFI_BOOT_TYPE_OFF 8 +#define BFI_BOOT_PARAM_OFF 12 + +#define BFI_BOOT_TYPE_NORMAL 0 /* param is device id */ +#define BFI_BOOT_TYPE_FLASH 1 +#define BFI_BOOT_TYPE_MEMTEST 2 + +#define BFI_BOOT_MEMTEST_RES_ADDR 0x900 +#define BFI_BOOT_MEMTEST_RES_SIG 0xA0A1A2A3 + +#endif diff --git a/drivers/scsi/bfa/include/bfi/bfi_cbreg.h b/drivers/scsi/bfa/include/bfi/bfi_cbreg.h new file mode 100644 index 000000000000..b3bb52b565b1 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_cbreg.h @@ -0,0 +1,305 @@ +/* + * 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. + */ + +/* + * bfi_cbreg.h crossbow host block register definitions + * + * !!! Do not edit. Auto generated. !!! + */ + +#ifndef __BFI_CBREG_H__ +#define __BFI_CBREG_H__ + + +#define HOSTFN0_INT_STATUS 0x00014000 +#define __HOSTFN0_INT_STATUS_LVL_MK 0x00f00000 +#define __HOSTFN0_INT_STATUS_LVL_SH 20 +#define __HOSTFN0_INT_STATUS_LVL(_v) ((_v) << __HOSTFN0_INT_STATUS_LVL_SH) +#define __HOSTFN0_INT_STATUS_P 0x000fffff +#define HOSTFN0_INT_MSK 0x00014004 +#define HOST_PAGE_NUM_FN0 0x00014008 +#define __HOST_PAGE_NUM_FN 0x000001ff +#define HOSTFN1_INT_STATUS 0x00014100 +#define __HOSTFN1_INT_STAT_LVL_MK 0x00f00000 +#define __HOSTFN1_INT_STAT_LVL_SH 20 +#define __HOSTFN1_INT_STAT_LVL(_v) ((_v) << __HOSTFN1_INT_STAT_LVL_SH) +#define __HOSTFN1_INT_STAT_P 0x000fffff +#define HOSTFN1_INT_MSK 0x00014104 +#define HOST_PAGE_NUM_FN1 0x00014108 +#define APP_PLL_400_CTL_REG 0x00014204 +#define __P_400_PLL_LOCK 0x80000000 +#define __APP_PLL_400_SRAM_USE_100MHZ 0x00100000 +#define __APP_PLL_400_RESET_TIMER_MK 0x000e0000 +#define __APP_PLL_400_RESET_TIMER_SH 17 +#define __APP_PLL_400_RESET_TIMER(_v) ((_v) << __APP_PLL_400_RESET_TIMER_SH) +#define __APP_PLL_400_LOGIC_SOFT_RESET 0x00010000 +#define __APP_PLL_400_CNTLMT0_1_MK 0x0000c000 +#define __APP_PLL_400_CNTLMT0_1_SH 14 +#define __APP_PLL_400_CNTLMT0_1(_v) ((_v) << __APP_PLL_400_CNTLMT0_1_SH) +#define __APP_PLL_400_JITLMT0_1_MK 0x00003000 +#define __APP_PLL_400_JITLMT0_1_SH 12 +#define __APP_PLL_400_JITLMT0_1(_v) ((_v) << __APP_PLL_400_JITLMT0_1_SH) +#define __APP_PLL_400_HREF 0x00000800 +#define __APP_PLL_400_HDIV 0x00000400 +#define __APP_PLL_400_P0_1_MK 0x00000300 +#define __APP_PLL_400_P0_1_SH 8 +#define __APP_PLL_400_P0_1(_v) ((_v) << __APP_PLL_400_P0_1_SH) +#define __APP_PLL_400_Z0_2_MK 0x000000e0 +#define __APP_PLL_400_Z0_2_SH 5 +#define __APP_PLL_400_Z0_2(_v) ((_v) << __APP_PLL_400_Z0_2_SH) +#define __APP_PLL_400_RSEL200500 0x00000010 +#define __APP_PLL_400_ENARST 0x00000008 +#define __APP_PLL_400_BYPASS 0x00000004 +#define __APP_PLL_400_LRESETN 0x00000002 +#define __APP_PLL_400_ENABLE 0x00000001 +#define APP_PLL_212_CTL_REG 0x00014208 +#define __P_212_PLL_LOCK 0x80000000 +#define __APP_PLL_212_RESET_TIMER_MK 0x000e0000 +#define __APP_PLL_212_RESET_TIMER_SH 17 +#define __APP_PLL_212_RESET_TIMER(_v) ((_v) << __APP_PLL_212_RESET_TIMER_SH) +#define __APP_PLL_212_LOGIC_SOFT_RESET 0x00010000 +#define __APP_PLL_212_CNTLMT0_1_MK 0x0000c000 +#define __APP_PLL_212_CNTLMT0_1_SH 14 +#define __APP_PLL_212_CNTLMT0_1(_v) ((_v) << __APP_PLL_212_CNTLMT0_1_SH) +#define __APP_PLL_212_JITLMT0_1_MK 0x00003000 +#define __APP_PLL_212_JITLMT0_1_SH 12 +#define __APP_PLL_212_JITLMT0_1(_v) ((_v) << __APP_PLL_212_JITLMT0_1_SH) +#define __APP_PLL_212_HREF 0x00000800 +#define __APP_PLL_212_HDIV 0x00000400 +#define __APP_PLL_212_P0_1_MK 0x00000300 +#define __APP_PLL_212_P0_1_SH 8 +#define __APP_PLL_212_P0_1(_v) ((_v) << __APP_PLL_212_P0_1_SH) +#define __APP_PLL_212_Z0_2_MK 0x000000e0 +#define __APP_PLL_212_Z0_2_SH 5 +#define __APP_PLL_212_Z0_2(_v) ((_v) << __APP_PLL_212_Z0_2_SH) +#define __APP_PLL_212_RSEL200500 0x00000010 +#define __APP_PLL_212_ENARST 0x00000008 +#define __APP_PLL_212_BYPASS 0x00000004 +#define __APP_PLL_212_LRESETN 0x00000002 +#define __APP_PLL_212_ENABLE 0x00000001 +#define HOST_SEM0_REG 0x00014230 +#define __HOST_SEMAPHORE 0x00000001 +#define HOST_SEM1_REG 0x00014234 +#define HOST_SEM2_REG 0x00014238 +#define HOST_SEM3_REG 0x0001423c +#define HOST_SEM0_INFO_REG 0x00014240 +#define HOST_SEM1_INFO_REG 0x00014244 +#define HOST_SEM2_INFO_REG 0x00014248 +#define HOST_SEM3_INFO_REG 0x0001424c +#define HOSTFN0_LPU0_CMD_STAT 0x00019000 +#define __HOSTFN0_LPU0_MBOX_INFO_MK 0xfffffffe +#define __HOSTFN0_LPU0_MBOX_INFO_SH 1 +#define __HOSTFN0_LPU0_MBOX_INFO(_v) ((_v) << __HOSTFN0_LPU0_MBOX_INFO_SH) +#define __HOSTFN0_LPU0_MBOX_CMD_STATUS 0x00000001 +#define LPU0_HOSTFN0_CMD_STAT 0x00019008 +#define __LPU0_HOSTFN0_MBOX_INFO_MK 0xfffffffe +#define __LPU0_HOSTFN0_MBOX_INFO_SH 1 +#define __LPU0_HOSTFN0_MBOX_INFO(_v) ((_v) << __LPU0_HOSTFN0_MBOX_INFO_SH) +#define __LPU0_HOSTFN0_MBOX_CMD_STATUS 0x00000001 +#define HOSTFN1_LPU1_CMD_STAT 0x00019014 +#define __HOSTFN1_LPU1_MBOX_INFO_MK 0xfffffffe +#define __HOSTFN1_LPU1_MBOX_INFO_SH 1 +#define __HOSTFN1_LPU1_MBOX_INFO(_v) ((_v) << __HOSTFN1_LPU1_MBOX_INFO_SH) +#define __HOSTFN1_LPU1_MBOX_CMD_STATUS 0x00000001 +#define LPU1_HOSTFN1_CMD_STAT 0x0001901c +#define __LPU1_HOSTFN1_MBOX_INFO_MK 0xfffffffe +#define __LPU1_HOSTFN1_MBOX_INFO_SH 1 +#define __LPU1_HOSTFN1_MBOX_INFO(_v) ((_v) << __LPU1_HOSTFN1_MBOX_INFO_SH) +#define __LPU1_HOSTFN1_MBOX_CMD_STATUS 0x00000001 +#define CPE_Q0_DEPTH 0x00010014 +#define CPE_Q0_PI 0x0001001c +#define CPE_Q0_CI 0x00010020 +#define CPE_Q1_DEPTH 0x00010034 +#define CPE_Q1_PI 0x0001003c +#define CPE_Q1_CI 0x00010040 +#define CPE_Q2_DEPTH 0x00010054 +#define CPE_Q2_PI 0x0001005c +#define CPE_Q2_CI 0x00010060 +#define CPE_Q3_DEPTH 0x00010074 +#define CPE_Q3_PI 0x0001007c +#define CPE_Q3_CI 0x00010080 +#define CPE_Q4_DEPTH 0x00010094 +#define CPE_Q4_PI 0x0001009c +#define CPE_Q4_CI 0x000100a0 +#define CPE_Q5_DEPTH 0x000100b4 +#define CPE_Q5_PI 0x000100bc +#define CPE_Q5_CI 0x000100c0 +#define CPE_Q6_DEPTH 0x000100d4 +#define CPE_Q6_PI 0x000100dc +#define CPE_Q6_CI 0x000100e0 +#define CPE_Q7_DEPTH 0x000100f4 +#define CPE_Q7_PI 0x000100fc +#define CPE_Q7_CI 0x00010100 +#define RME_Q0_DEPTH 0x00011014 +#define RME_Q0_PI 0x0001101c +#define RME_Q0_CI 0x00011020 +#define RME_Q1_DEPTH 0x00011034 +#define RME_Q1_PI 0x0001103c +#define RME_Q1_CI 0x00011040 +#define RME_Q2_DEPTH 0x00011054 +#define RME_Q2_PI 0x0001105c +#define RME_Q2_CI 0x00011060 +#define RME_Q3_DEPTH 0x00011074 +#define RME_Q3_PI 0x0001107c +#define RME_Q3_CI 0x00011080 +#define RME_Q4_DEPTH 0x00011094 +#define RME_Q4_PI 0x0001109c +#define RME_Q4_CI 0x000110a0 +#define RME_Q5_DEPTH 0x000110b4 +#define RME_Q5_PI 0x000110bc +#define RME_Q5_CI 0x000110c0 +#define RME_Q6_DEPTH 0x000110d4 +#define RME_Q6_PI 0x000110dc +#define RME_Q6_CI 0x000110e0 +#define RME_Q7_DEPTH 0x000110f4 +#define RME_Q7_PI 0x000110fc +#define RME_Q7_CI 0x00011100 +#define PSS_CTL_REG 0x00018800 +#define __PSS_I2C_CLK_DIV_MK 0x00030000 +#define __PSS_I2C_CLK_DIV_SH 16 +#define __PSS_I2C_CLK_DIV(_v) ((_v) << __PSS_I2C_CLK_DIV_SH) +#define __PSS_LMEM_INIT_DONE 0x00001000 +#define __PSS_LMEM_RESET 0x00000200 +#define __PSS_LMEM_INIT_EN 0x00000100 +#define __PSS_LPU1_RESET 0x00000002 +#define __PSS_LPU0_RESET 0x00000001 + + +/* + * These definitions are either in error/missing in spec. Its auto-generated + * from hard coded values in regparse.pl. + */ +#define __EMPHPOST_AT_4G_MK_FIX 0x0000001c +#define __EMPHPOST_AT_4G_SH_FIX 0x00000002 +#define __EMPHPRE_AT_4G_FIX 0x00000003 +#define __SFP_TXRATE_EN_FIX 0x00000100 +#define __SFP_RXRATE_EN_FIX 0x00000080 + + +/* + * These register definitions are auto-generated from hard coded values + * in regparse.pl. + */ +#define HOSTFN0_LPU_MBOX0_0 0x00019200 +#define HOSTFN1_LPU_MBOX0_8 0x00019260 +#define LPU_HOSTFN0_MBOX0_0 0x00019280 +#define LPU_HOSTFN1_MBOX0_8 0x000192e0 + + +/* + * These register mapping definitions are auto-generated from mapping tables + * in regparse.pl. + */ +#define BFA_IOC0_HBEAT_REG HOST_SEM0_INFO_REG +#define BFA_IOC0_STATE_REG HOST_SEM1_INFO_REG +#define BFA_IOC1_HBEAT_REG HOST_SEM2_INFO_REG +#define BFA_IOC1_STATE_REG HOST_SEM3_INFO_REG +#define BFA_FW_USE_COUNT HOST_SEM4_INFO_REG + +#define CPE_Q_DEPTH(__n) \ + (CPE_Q0_DEPTH + (__n) * (CPE_Q1_DEPTH - CPE_Q0_DEPTH)) +#define CPE_Q_PI(__n) \ + (CPE_Q0_PI + (__n) * (CPE_Q1_PI - CPE_Q0_PI)) +#define CPE_Q_CI(__n) \ + (CPE_Q0_CI + (__n) * (CPE_Q1_CI - CPE_Q0_CI)) +#define RME_Q_DEPTH(__n) \ + (RME_Q0_DEPTH + (__n) * (RME_Q1_DEPTH - RME_Q0_DEPTH)) +#define RME_Q_PI(__n) \ + (RME_Q0_PI + (__n) * (RME_Q1_PI - RME_Q0_PI)) +#define RME_Q_CI(__n) \ + (RME_Q0_CI + (__n) * (RME_Q1_CI - RME_Q0_CI)) + +#define CPE_Q_NUM(__fn, __q) (((__fn) << 2) + (__q)) +#define RME_Q_NUM(__fn, __q) (((__fn) << 2) + (__q)) +#define CPE_Q_MASK(__q) ((__q) & 0x3) +#define RME_Q_MASK(__q) ((__q) & 0x3) + + +/* + * PCI MSI-X vector defines + */ +enum { + BFA_MSIX_CPE_Q0 = 0, + BFA_MSIX_CPE_Q1 = 1, + BFA_MSIX_CPE_Q2 = 2, + BFA_MSIX_CPE_Q3 = 3, + BFA_MSIX_CPE_Q4 = 4, + BFA_MSIX_CPE_Q5 = 5, + BFA_MSIX_CPE_Q6 = 6, + BFA_MSIX_CPE_Q7 = 7, + BFA_MSIX_RME_Q0 = 8, + BFA_MSIX_RME_Q1 = 9, + BFA_MSIX_RME_Q2 = 10, + BFA_MSIX_RME_Q3 = 11, + BFA_MSIX_RME_Q4 = 12, + BFA_MSIX_RME_Q5 = 13, + BFA_MSIX_RME_Q6 = 14, + BFA_MSIX_RME_Q7 = 15, + BFA_MSIX_ERR_EMC = 16, + BFA_MSIX_ERR_LPU0 = 17, + BFA_MSIX_ERR_LPU1 = 18, + BFA_MSIX_ERR_PSS = 19, + BFA_MSIX_MBOX_LPU0 = 20, + BFA_MSIX_MBOX_LPU1 = 21, + BFA_MSIX_CB_MAX = 22, +}; + +/* + * And corresponding host interrupt status bit field defines + */ +#define __HFN_INT_CPE_Q0 0x00000001U +#define __HFN_INT_CPE_Q1 0x00000002U +#define __HFN_INT_CPE_Q2 0x00000004U +#define __HFN_INT_CPE_Q3 0x00000008U +#define __HFN_INT_CPE_Q4 0x00000010U +#define __HFN_INT_CPE_Q5 0x00000020U +#define __HFN_INT_CPE_Q6 0x00000040U +#define __HFN_INT_CPE_Q7 0x00000080U +#define __HFN_INT_RME_Q0 0x00000100U +#define __HFN_INT_RME_Q1 0x00000200U +#define __HFN_INT_RME_Q2 0x00000400U +#define __HFN_INT_RME_Q3 0x00000800U +#define __HFN_INT_RME_Q4 0x00001000U +#define __HFN_INT_RME_Q5 0x00002000U +#define __HFN_INT_RME_Q6 0x00004000U +#define __HFN_INT_RME_Q7 0x00008000U +#define __HFN_INT_ERR_EMC 0x00010000U +#define __HFN_INT_ERR_LPU0 0x00020000U +#define __HFN_INT_ERR_LPU1 0x00040000U +#define __HFN_INT_ERR_PSS 0x00080000U +#define __HFN_INT_MBOX_LPU0 0x00100000U +#define __HFN_INT_MBOX_LPU1 0x00200000U +#define __HFN_INT_MBOX1_LPU0 0x00400000U +#define __HFN_INT_MBOX1_LPU1 0x00800000U +#define __HFN_INT_CPE_MASK 0x000000ffU +#define __HFN_INT_RME_MASK 0x0000ff00U + + +/* + * crossbow memory map. + */ +#define PSS_SMEM_PAGE_START 0x8000 +#define PSS_SMEM_PGNUM(_pg0, _ma) ((_pg0) + ((_ma) >> 15)) +#define PSS_SMEM_PGOFF(_ma) ((_ma) & 0x7fff) + +/* + * End of crossbow memory map + */ + + +#endif /* __BFI_CBREG_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_cee.h b/drivers/scsi/bfa/include/bfi/bfi_cee.h new file mode 100644 index 000000000000..0970596583ea --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_cee.h @@ -0,0 +1,119 @@ +/* + * 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. + */ +/** + * Copyright (c) 2006-2009 Brocade Communications Systems, Inc. + * All rights reserved. + * + * bfi_dcbx.h BFI Interface (Mailbox commands and related structures) + * between host driver and DCBX/LLDP firmware module. + * +**/ + +#ifndef __BFI_CEE_H__ +#define __BFI_CEE_H__ + +#include + +#pragma pack(1) + + +enum bfi_cee_h2i_msgs_e { + BFI_CEE_H2I_GET_CFG_REQ = 1, + BFI_CEE_H2I_RESET_STATS = 2, + BFI_CEE_H2I_GET_STATS_REQ = 3, +}; + + +enum bfi_cee_i2h_msgs_e { + BFI_CEE_I2H_GET_CFG_RSP = BFA_I2HM(1), + BFI_CEE_I2H_RESET_STATS_RSP = BFA_I2HM(2), + BFI_CEE_I2H_GET_STATS_RSP = BFA_I2HM(3), +}; + + +/* Data structures */ + +/* + * BFI_CEE_H2I_RESET_STATS + */ +struct bfi_lldp_reset_stats_s { + struct bfi_mhdr_s mh; +}; + +/* + * BFI_CEE_H2I_RESET_STATS + */ +struct bfi_cee_reset_stats_s { + struct bfi_mhdr_s mh; +}; + +/* + * BFI_CEE_H2I_GET_CFG_REQ + */ +struct bfi_cee_get_req_s { + struct bfi_mhdr_s mh; + union bfi_addr_u dma_addr; +}; + + +/* + * BFI_CEE_I2H_GET_CFG_RSP + */ +struct bfi_cee_get_rsp_s { + struct bfi_mhdr_s mh; + u8 cmd_status; + u8 rsvd[3]; +}; + +/* + * BFI_CEE_H2I_GET_STATS_REQ + */ +struct bfi_cee_stats_req_s { + struct bfi_mhdr_s mh; + union bfi_addr_u dma_addr; +}; + + +/* + * BFI_CEE_I2H_GET_STATS_RSP + */ +struct bfi_cee_stats_rsp_s { + struct bfi_mhdr_s mh; + u8 cmd_status; + u8 rsvd[3]; +}; + + + +union bfi_cee_h2i_msg_u { + struct bfi_mhdr_s mh; + struct bfi_cee_get_req_s get_req; + struct bfi_cee_stats_req_s stats_req; +}; + + +union bfi_cee_i2h_msg_u { + struct bfi_mhdr_s mh; + struct bfi_cee_get_rsp_s get_rsp; + struct bfi_cee_stats_rsp_s stats_rsp; +}; + +#pragma pack() + + +#endif /* __BFI_CEE_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_ctreg.h b/drivers/scsi/bfa/include/bfi/bfi_ctreg.h new file mode 100644 index 000000000000..d3caa58c0a0a --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_ctreg.h @@ -0,0 +1,611 @@ +/* + * 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. + */ + +/* + * bfi_ctreg.h catapult host block register definitions + * + * !!! Do not edit. Auto generated. !!! + */ + +#ifndef __BFI_CTREG_H__ +#define __BFI_CTREG_H__ + + +#define HOSTFN0_LPU_MBOX0_0 0x00019200 +#define HOSTFN1_LPU_MBOX0_8 0x00019260 +#define LPU_HOSTFN0_MBOX0_0 0x00019280 +#define LPU_HOSTFN1_MBOX0_8 0x000192e0 +#define HOSTFN2_LPU_MBOX0_0 0x00019400 +#define HOSTFN3_LPU_MBOX0_8 0x00019460 +#define LPU_HOSTFN2_MBOX0_0 0x00019480 +#define LPU_HOSTFN3_MBOX0_8 0x000194e0 +#define HOSTFN0_INT_STATUS 0x00014000 +#define __HOSTFN0_HALT_OCCURRED 0x01000000 +#define __HOSTFN0_INT_STATUS_LVL_MK 0x00f00000 +#define __HOSTFN0_INT_STATUS_LVL_SH 20 +#define __HOSTFN0_INT_STATUS_LVL(_v) ((_v) << __HOSTFN0_INT_STATUS_LVL_SH) +#define __HOSTFN0_INT_STATUS_P_MK 0x000f0000 +#define __HOSTFN0_INT_STATUS_P_SH 16 +#define __HOSTFN0_INT_STATUS_P(_v) ((_v) << __HOSTFN0_INT_STATUS_P_SH) +#define __HOSTFN0_INT_STATUS_F 0x0000ffff +#define HOSTFN0_INT_MSK 0x00014004 +#define HOST_PAGE_NUM_FN0 0x00014008 +#define __HOST_PAGE_NUM_FN 0x000001ff +#define HOST_MSIX_ERR_INDEX_FN0 0x0001400c +#define __MSIX_ERR_INDEX_FN 0x000001ff +#define HOSTFN1_INT_STATUS 0x00014100 +#define __HOSTFN1_HALT_OCCURRED 0x01000000 +#define __HOSTFN1_INT_STATUS_LVL_MK 0x00f00000 +#define __HOSTFN1_INT_STATUS_LVL_SH 20 +#define __HOSTFN1_INT_STATUS_LVL(_v) ((_v) << __HOSTFN1_INT_STATUS_LVL_SH) +#define __HOSTFN1_INT_STATUS_P_MK 0x000f0000 +#define __HOSTFN1_INT_STATUS_P_SH 16 +#define __HOSTFN1_INT_STATUS_P(_v) ((_v) << __HOSTFN1_INT_STATUS_P_SH) +#define __HOSTFN1_INT_STATUS_F 0x0000ffff +#define HOSTFN1_INT_MSK 0x00014104 +#define HOST_PAGE_NUM_FN1 0x00014108 +#define HOST_MSIX_ERR_INDEX_FN1 0x0001410c +#define APP_PLL_425_CTL_REG 0x00014204 +#define __P_425_PLL_LOCK 0x80000000 +#define __APP_PLL_425_SRAM_USE_100MHZ 0x00100000 +#define __APP_PLL_425_RESET_TIMER_MK 0x000e0000 +#define __APP_PLL_425_RESET_TIMER_SH 17 +#define __APP_PLL_425_RESET_TIMER(_v) ((_v) << __APP_PLL_425_RESET_TIMER_SH) +#define __APP_PLL_425_LOGIC_SOFT_RESET 0x00010000 +#define __APP_PLL_425_CNTLMT0_1_MK 0x0000c000 +#define __APP_PLL_425_CNTLMT0_1_SH 14 +#define __APP_PLL_425_CNTLMT0_1(_v) ((_v) << __APP_PLL_425_CNTLMT0_1_SH) +#define __APP_PLL_425_JITLMT0_1_MK 0x00003000 +#define __APP_PLL_425_JITLMT0_1_SH 12 +#define __APP_PLL_425_JITLMT0_1(_v) ((_v) << __APP_PLL_425_JITLMT0_1_SH) +#define __APP_PLL_425_HREF 0x00000800 +#define __APP_PLL_425_HDIV 0x00000400 +#define __APP_PLL_425_P0_1_MK 0x00000300 +#define __APP_PLL_425_P0_1_SH 8 +#define __APP_PLL_425_P0_1(_v) ((_v) << __APP_PLL_425_P0_1_SH) +#define __APP_PLL_425_Z0_2_MK 0x000000e0 +#define __APP_PLL_425_Z0_2_SH 5 +#define __APP_PLL_425_Z0_2(_v) ((_v) << __APP_PLL_425_Z0_2_SH) +#define __APP_PLL_425_RSEL200500 0x00000010 +#define __APP_PLL_425_ENARST 0x00000008 +#define __APP_PLL_425_BYPASS 0x00000004 +#define __APP_PLL_425_LRESETN 0x00000002 +#define __APP_PLL_425_ENABLE 0x00000001 +#define APP_PLL_312_CTL_REG 0x00014208 +#define __P_312_PLL_LOCK 0x80000000 +#define __ENABLE_MAC_AHB_1 0x00800000 +#define __ENABLE_MAC_AHB_0 0x00400000 +#define __ENABLE_MAC_1 0x00200000 +#define __ENABLE_MAC_0 0x00100000 +#define __APP_PLL_312_RESET_TIMER_MK 0x000e0000 +#define __APP_PLL_312_RESET_TIMER_SH 17 +#define __APP_PLL_312_RESET_TIMER(_v) ((_v) << __APP_PLL_312_RESET_TIMER_SH) +#define __APP_PLL_312_LOGIC_SOFT_RESET 0x00010000 +#define __APP_PLL_312_CNTLMT0_1_MK 0x0000c000 +#define __APP_PLL_312_CNTLMT0_1_SH 14 +#define __APP_PLL_312_CNTLMT0_1(_v) ((_v) << __APP_PLL_312_CNTLMT0_1_SH) +#define __APP_PLL_312_JITLMT0_1_MK 0x00003000 +#define __APP_PLL_312_JITLMT0_1_SH 12 +#define __APP_PLL_312_JITLMT0_1(_v) ((_v) << __APP_PLL_312_JITLMT0_1_SH) +#define __APP_PLL_312_HREF 0x00000800 +#define __APP_PLL_312_HDIV 0x00000400 +#define __APP_PLL_312_P0_1_MK 0x00000300 +#define __APP_PLL_312_P0_1_SH 8 +#define __APP_PLL_312_P0_1(_v) ((_v) << __APP_PLL_312_P0_1_SH) +#define __APP_PLL_312_Z0_2_MK 0x000000e0 +#define __APP_PLL_312_Z0_2_SH 5 +#define __APP_PLL_312_Z0_2(_v) ((_v) << __APP_PLL_312_Z0_2_SH) +#define __APP_PLL_312_RSEL200500 0x00000010 +#define __APP_PLL_312_ENARST 0x00000008 +#define __APP_PLL_312_BYPASS 0x00000004 +#define __APP_PLL_312_LRESETN 0x00000002 +#define __APP_PLL_312_ENABLE 0x00000001 +#define MBIST_CTL_REG 0x00014220 +#define __EDRAM_BISTR_START 0x00000004 +#define __MBIST_RESET 0x00000002 +#define __MBIST_START 0x00000001 +#define MBIST_STAT_REG 0x00014224 +#define __EDRAM_BISTR_STATUS 0x00000008 +#define __EDRAM_BISTR_DONE 0x00000004 +#define __MEM_BIT_STATUS 0x00000002 +#define __MBIST_DONE 0x00000001 +#define HOST_SEM0_REG 0x00014230 +#define __HOST_SEMAPHORE 0x00000001 +#define HOST_SEM1_REG 0x00014234 +#define HOST_SEM2_REG 0x00014238 +#define HOST_SEM3_REG 0x0001423c +#define HOST_SEM0_INFO_REG 0x00014240 +#define HOST_SEM1_INFO_REG 0x00014244 +#define HOST_SEM2_INFO_REG 0x00014248 +#define HOST_SEM3_INFO_REG 0x0001424c +#define ETH_MAC_SER_REG 0x00014288 +#define __APP_EMS_CKBUFAMPIN 0x00000020 +#define __APP_EMS_REFCLKSEL 0x00000010 +#define __APP_EMS_CMLCKSEL 0x00000008 +#define __APP_EMS_REFCKBUFEN2 0x00000004 +#define __APP_EMS_REFCKBUFEN1 0x00000002 +#define __APP_EMS_CHANNEL_SEL 0x00000001 +#define HOSTFN2_INT_STATUS 0x00014300 +#define __HOSTFN2_HALT_OCCURRED 0x01000000 +#define __HOSTFN2_INT_STATUS_LVL_MK 0x00f00000 +#define __HOSTFN2_INT_STATUS_LVL_SH 20 +#define __HOSTFN2_INT_STATUS_LVL(_v) ((_v) << __HOSTFN2_INT_STATUS_LVL_SH) +#define __HOSTFN2_INT_STATUS_P_MK 0x000f0000 +#define __HOSTFN2_INT_STATUS_P_SH 16 +#define __HOSTFN2_INT_STATUS_P(_v) ((_v) << __HOSTFN2_INT_STATUS_P_SH) +#define __HOSTFN2_INT_STATUS_F 0x0000ffff +#define HOSTFN2_INT_MSK 0x00014304 +#define HOST_PAGE_NUM_FN2 0x00014308 +#define HOST_MSIX_ERR_INDEX_FN2 0x0001430c +#define HOSTFN3_INT_STATUS 0x00014400 +#define __HALT_OCCURRED 0x01000000 +#define __HOSTFN3_INT_STATUS_LVL_MK 0x00f00000 +#define __HOSTFN3_INT_STATUS_LVL_SH 20 +#define __HOSTFN3_INT_STATUS_LVL(_v) ((_v) << __HOSTFN3_INT_STATUS_LVL_SH) +#define __HOSTFN3_INT_STATUS_P_MK 0x000f0000 +#define __HOSTFN3_INT_STATUS_P_SH 16 +#define __HOSTFN3_INT_STATUS_P(_v) ((_v) << __HOSTFN3_INT_STATUS_P_SH) +#define __HOSTFN3_INT_STATUS_F 0x0000ffff +#define HOSTFN3_INT_MSK 0x00014404 +#define HOST_PAGE_NUM_FN3 0x00014408 +#define HOST_MSIX_ERR_INDEX_FN3 0x0001440c +#define FNC_ID_REG 0x00014600 +#define __FUNCTION_NUMBER 0x00000007 +#define FNC_PERS_REG 0x00014604 +#define __F3_FUNCTION_ACTIVE 0x80000000 +#define __F3_FUNCTION_MODE 0x40000000 +#define __F3_PORT_MAP_MK 0x30000000 +#define __F3_PORT_MAP_SH 28 +#define __F3_PORT_MAP(_v) ((_v) << __F3_PORT_MAP_SH) +#define __F3_VM_MODE 0x08000000 +#define __F3_INTX_STATUS_MK 0x07000000 +#define __F3_INTX_STATUS_SH 24 +#define __F3_INTX_STATUS(_v) ((_v) << __F3_INTX_STATUS_SH) +#define __F2_FUNCTION_ACTIVE 0x00800000 +#define __F2_FUNCTION_MODE 0x00400000 +#define __F2_PORT_MAP_MK 0x00300000 +#define __F2_PORT_MAP_SH 20 +#define __F2_PORT_MAP(_v) ((_v) << __F2_PORT_MAP_SH) +#define __F2_VM_MODE 0x00080000 +#define __F2_INTX_STATUS_MK 0x00070000 +#define __F2_INTX_STATUS_SH 16 +#define __F2_INTX_STATUS(_v) ((_v) << __F2_INTX_STATUS_SH) +#define __F1_FUNCTION_ACTIVE 0x00008000 +#define __F1_FUNCTION_MODE 0x00004000 +#define __F1_PORT_MAP_MK 0x00003000 +#define __F1_PORT_MAP_SH 12 +#define __F1_PORT_MAP(_v) ((_v) << __F1_PORT_MAP_SH) +#define __F1_VM_MODE 0x00000800 +#define __F1_INTX_STATUS_MK 0x00000700 +#define __F1_INTX_STATUS_SH 8 +#define __F1_INTX_STATUS(_v) ((_v) << __F1_INTX_STATUS_SH) +#define __F0_FUNCTION_ACTIVE 0x00000080 +#define __F0_FUNCTION_MODE 0x00000040 +#define __F0_PORT_MAP_MK 0x00000030 +#define __F0_PORT_MAP_SH 4 +#define __F0_PORT_MAP(_v) ((_v) << __F0_PORT_MAP_SH) +#define __F0_VM_MODE 0x00000008 +#define __F0_INTX_STATUS 0x00000007 +enum { + __F0_INTX_STATUS_MSIX = 0x0, + __F0_INTX_STATUS_INTA = 0x1, + __F0_INTX_STATUS_INTB = 0x2, + __F0_INTX_STATUS_INTC = 0x3, + __F0_INTX_STATUS_INTD = 0x4, +}; +#define OP_MODE 0x0001460c +#define __APP_ETH_CLK_LOWSPEED 0x00000004 +#define __GLOBAL_CORECLK_HALFSPEED 0x00000002 +#define __GLOBAL_FCOE_MODE 0x00000001 +#define HOST_SEM4_REG 0x00014610 +#define HOST_SEM5_REG 0x00014614 +#define HOST_SEM6_REG 0x00014618 +#define HOST_SEM7_REG 0x0001461c +#define HOST_SEM4_INFO_REG 0x00014620 +#define HOST_SEM5_INFO_REG 0x00014624 +#define HOST_SEM6_INFO_REG 0x00014628 +#define HOST_SEM7_INFO_REG 0x0001462c +#define HOSTFN0_LPU0_MBOX0_CMD_STAT 0x00019000 +#define __HOSTFN0_LPU0_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN0_LPU0_MBOX0_INFO_SH 1 +#define __HOSTFN0_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN0_LPU0_MBOX0_INFO_SH) +#define __HOSTFN0_LPU0_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN0_LPU1_MBOX0_CMD_STAT 0x00019004 +#define __HOSTFN0_LPU1_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN0_LPU1_MBOX0_INFO_SH 1 +#define __HOSTFN0_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN0_LPU1_MBOX0_INFO_SH) +#define __HOSTFN0_LPU1_MBOX0_CMD_STATUS 0x00000001 +#define LPU0_HOSTFN0_MBOX0_CMD_STAT 0x00019008 +#define __LPU0_HOSTFN0_MBOX0_INFO_MK 0xfffffffe +#define __LPU0_HOSTFN0_MBOX0_INFO_SH 1 +#define __LPU0_HOSTFN0_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN0_MBOX0_INFO_SH) +#define __LPU0_HOSTFN0_MBOX0_CMD_STATUS 0x00000001 +#define LPU1_HOSTFN0_MBOX0_CMD_STAT 0x0001900c +#define __LPU1_HOSTFN0_MBOX0_INFO_MK 0xfffffffe +#define __LPU1_HOSTFN0_MBOX0_INFO_SH 1 +#define __LPU1_HOSTFN0_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN0_MBOX0_INFO_SH) +#define __LPU1_HOSTFN0_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN1_LPU0_MBOX0_CMD_STAT 0x00019010 +#define __HOSTFN1_LPU0_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN1_LPU0_MBOX0_INFO_SH 1 +#define __HOSTFN1_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN1_LPU0_MBOX0_INFO_SH) +#define __HOSTFN1_LPU0_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN1_LPU1_MBOX0_CMD_STAT 0x00019014 +#define __HOSTFN1_LPU1_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN1_LPU1_MBOX0_INFO_SH 1 +#define __HOSTFN1_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN1_LPU1_MBOX0_INFO_SH) +#define __HOSTFN1_LPU1_MBOX0_CMD_STATUS 0x00000001 +#define LPU0_HOSTFN1_MBOX0_CMD_STAT 0x00019018 +#define __LPU0_HOSTFN1_MBOX0_INFO_MK 0xfffffffe +#define __LPU0_HOSTFN1_MBOX0_INFO_SH 1 +#define __LPU0_HOSTFN1_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN1_MBOX0_INFO_SH) +#define __LPU0_HOSTFN1_MBOX0_CMD_STATUS 0x00000001 +#define LPU1_HOSTFN1_MBOX0_CMD_STAT 0x0001901c +#define __LPU1_HOSTFN1_MBOX0_INFO_MK 0xfffffffe +#define __LPU1_HOSTFN1_MBOX0_INFO_SH 1 +#define __LPU1_HOSTFN1_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN1_MBOX0_INFO_SH) +#define __LPU1_HOSTFN1_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN2_LPU0_MBOX0_CMD_STAT 0x00019150 +#define __HOSTFN2_LPU0_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN2_LPU0_MBOX0_INFO_SH 1 +#define __HOSTFN2_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN2_LPU0_MBOX0_INFO_SH) +#define __HOSTFN2_LPU0_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN2_LPU1_MBOX0_CMD_STAT 0x00019154 +#define __HOSTFN2_LPU1_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN2_LPU1_MBOX0_INFO_SH 1 +#define __HOSTFN2_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN2_LPU1_MBOX0_INFO_SH) +#define __HOSTFN2_LPU1_MBOX0BOX0_CMD_STATUS 0x00000001 +#define LPU0_HOSTFN2_MBOX0_CMD_STAT 0x00019158 +#define __LPU0_HOSTFN2_MBOX0_INFO_MK 0xfffffffe +#define __LPU0_HOSTFN2_MBOX0_INFO_SH 1 +#define __LPU0_HOSTFN2_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN2_MBOX0_INFO_SH) +#define __LPU0_HOSTFN2_MBOX0_CMD_STATUS 0x00000001 +#define LPU1_HOSTFN2_MBOX0_CMD_STAT 0x0001915c +#define __LPU1_HOSTFN2_MBOX0_INFO_MK 0xfffffffe +#define __LPU1_HOSTFN2_MBOX0_INFO_SH 1 +#define __LPU1_HOSTFN2_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN2_MBOX0_INFO_SH) +#define __LPU1_HOSTFN2_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN3_LPU0_MBOX0_CMD_STAT 0x00019160 +#define __HOSTFN3_LPU0_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN3_LPU0_MBOX0_INFO_SH 1 +#define __HOSTFN3_LPU0_MBOX0_INFO(_v) ((_v) << __HOSTFN3_LPU0_MBOX0_INFO_SH) +#define __HOSTFN3_LPU0_MBOX0_CMD_STATUS 0x00000001 +#define HOSTFN3_LPU1_MBOX0_CMD_STAT 0x00019164 +#define __HOSTFN3_LPU1_MBOX0_INFO_MK 0xfffffffe +#define __HOSTFN3_LPU1_MBOX0_INFO_SH 1 +#define __HOSTFN3_LPU1_MBOX0_INFO(_v) ((_v) << __HOSTFN3_LPU1_MBOX0_INFO_SH) +#define __HOSTFN3_LPU1_MBOX0_CMD_STATUS 0x00000001 +#define LPU0_HOSTFN3_MBOX0_CMD_STAT 0x00019168 +#define __LPU0_HOSTFN3_MBOX0_INFO_MK 0xfffffffe +#define __LPU0_HOSTFN3_MBOX0_INFO_SH 1 +#define __LPU0_HOSTFN3_MBOX0_INFO(_v) ((_v) << __LPU0_HOSTFN3_MBOX0_INFO_SH) +#define __LPU0_HOSTFN3_MBOX0_CMD_STATUS 0x00000001 +#define LPU1_HOSTFN3_MBOX0_CMD_STAT 0x0001916c +#define __LPU1_HOSTFN3_MBOX0_INFO_MK 0xfffffffe +#define __LPU1_HOSTFN3_MBOX0_INFO_SH 1 +#define __LPU1_HOSTFN3_MBOX0_INFO(_v) ((_v) << __LPU1_HOSTFN3_MBOX0_INFO_SH) +#define __LPU1_HOSTFN3_MBOX0_CMD_STATUS 0x00000001 +#define FW_INIT_HALT_P0 0x000191ac +#define __FW_INIT_HALT_P 0x00000001 +#define FW_INIT_HALT_P1 0x000191bc +#define CPE_PI_PTR_Q0 0x00038000 +#define __CPE_PI_UNUSED_MK 0xffff0000 +#define __CPE_PI_UNUSED_SH 16 +#define __CPE_PI_UNUSED(_v) ((_v) << __CPE_PI_UNUSED_SH) +#define __CPE_PI_PTR 0x0000ffff +#define CPE_PI_PTR_Q1 0x00038040 +#define CPE_CI_PTR_Q0 0x00038004 +#define __CPE_CI_UNUSED_MK 0xffff0000 +#define __CPE_CI_UNUSED_SH 16 +#define __CPE_CI_UNUSED(_v) ((_v) << __CPE_CI_UNUSED_SH) +#define __CPE_CI_PTR 0x0000ffff +#define CPE_CI_PTR_Q1 0x00038044 +#define CPE_DEPTH_Q0 0x00038008 +#define __CPE_DEPTH_UNUSED_MK 0xf8000000 +#define __CPE_DEPTH_UNUSED_SH 27 +#define __CPE_DEPTH_UNUSED(_v) ((_v) << __CPE_DEPTH_UNUSED_SH) +#define __CPE_MSIX_VEC_INDEX_MK 0x07ff0000 +#define __CPE_MSIX_VEC_INDEX_SH 16 +#define __CPE_MSIX_VEC_INDEX(_v) ((_v) << __CPE_MSIX_VEC_INDEX_SH) +#define __CPE_DEPTH 0x0000ffff +#define CPE_DEPTH_Q1 0x00038048 +#define CPE_QCTRL_Q0 0x0003800c +#define __CPE_CTRL_UNUSED30_MK 0xfc000000 +#define __CPE_CTRL_UNUSED30_SH 26 +#define __CPE_CTRL_UNUSED30(_v) ((_v) << __CPE_CTRL_UNUSED30_SH) +#define __CPE_FUNC_INT_CTRL_MK 0x03000000 +#define __CPE_FUNC_INT_CTRL_SH 24 +#define __CPE_FUNC_INT_CTRL(_v) ((_v) << __CPE_FUNC_INT_CTRL_SH) +enum { + __CPE_FUNC_INT_CTRL_DISABLE = 0x0, + __CPE_FUNC_INT_CTRL_F2NF = 0x1, + __CPE_FUNC_INT_CTRL_3QUART = 0x2, + __CPE_FUNC_INT_CTRL_HALF = 0x3, +}; +#define __CPE_CTRL_UNUSED20_MK 0x00f00000 +#define __CPE_CTRL_UNUSED20_SH 20 +#define __CPE_CTRL_UNUSED20(_v) ((_v) << __CPE_CTRL_UNUSED20_SH) +#define __CPE_SCI_TH_MK 0x000f0000 +#define __CPE_SCI_TH_SH 16 +#define __CPE_SCI_TH(_v) ((_v) << __CPE_SCI_TH_SH) +#define __CPE_CTRL_UNUSED10_MK 0x0000c000 +#define __CPE_CTRL_UNUSED10_SH 14 +#define __CPE_CTRL_UNUSED10(_v) ((_v) << __CPE_CTRL_UNUSED10_SH) +#define __CPE_ACK_PENDING 0x00002000 +#define __CPE_CTRL_UNUSED40_MK 0x00001c00 +#define __CPE_CTRL_UNUSED40_SH 10 +#define __CPE_CTRL_UNUSED40(_v) ((_v) << __CPE_CTRL_UNUSED40_SH) +#define __CPE_PCIEID_MK 0x00000300 +#define __CPE_PCIEID_SH 8 +#define __CPE_PCIEID(_v) ((_v) << __CPE_PCIEID_SH) +#define __CPE_CTRL_UNUSED00_MK 0x000000fe +#define __CPE_CTRL_UNUSED00_SH 1 +#define __CPE_CTRL_UNUSED00(_v) ((_v) << __CPE_CTRL_UNUSED00_SH) +#define __CPE_ESIZE 0x00000001 +#define CPE_QCTRL_Q1 0x0003804c +#define __CPE_CTRL_UNUSED31_MK 0xfc000000 +#define __CPE_CTRL_UNUSED31_SH 26 +#define __CPE_CTRL_UNUSED31(_v) ((_v) << __CPE_CTRL_UNUSED31_SH) +#define __CPE_CTRL_UNUSED21_MK 0x00f00000 +#define __CPE_CTRL_UNUSED21_SH 20 +#define __CPE_CTRL_UNUSED21(_v) ((_v) << __CPE_CTRL_UNUSED21_SH) +#define __CPE_CTRL_UNUSED11_MK 0x0000c000 +#define __CPE_CTRL_UNUSED11_SH 14 +#define __CPE_CTRL_UNUSED11(_v) ((_v) << __CPE_CTRL_UNUSED11_SH) +#define __CPE_CTRL_UNUSED41_MK 0x00001c00 +#define __CPE_CTRL_UNUSED41_SH 10 +#define __CPE_CTRL_UNUSED41(_v) ((_v) << __CPE_CTRL_UNUSED41_SH) +#define __CPE_CTRL_UNUSED01_MK 0x000000fe +#define __CPE_CTRL_UNUSED01_SH 1 +#define __CPE_CTRL_UNUSED01(_v) ((_v) << __CPE_CTRL_UNUSED01_SH) +#define RME_PI_PTR_Q0 0x00038020 +#define __LATENCY_TIME_STAMP_MK 0xffff0000 +#define __LATENCY_TIME_STAMP_SH 16 +#define __LATENCY_TIME_STAMP(_v) ((_v) << __LATENCY_TIME_STAMP_SH) +#define __RME_PI_PTR 0x0000ffff +#define RME_PI_PTR_Q1 0x00038060 +#define RME_CI_PTR_Q0 0x00038024 +#define __DELAY_TIME_STAMP_MK 0xffff0000 +#define __DELAY_TIME_STAMP_SH 16 +#define __DELAY_TIME_STAMP(_v) ((_v) << __DELAY_TIME_STAMP_SH) +#define __RME_CI_PTR 0x0000ffff +#define RME_CI_PTR_Q1 0x00038064 +#define RME_DEPTH_Q0 0x00038028 +#define __RME_DEPTH_UNUSED_MK 0xf8000000 +#define __RME_DEPTH_UNUSED_SH 27 +#define __RME_DEPTH_UNUSED(_v) ((_v) << __RME_DEPTH_UNUSED_SH) +#define __RME_MSIX_VEC_INDEX_MK 0x07ff0000 +#define __RME_MSIX_VEC_INDEX_SH 16 +#define __RME_MSIX_VEC_INDEX(_v) ((_v) << __RME_MSIX_VEC_INDEX_SH) +#define __RME_DEPTH 0x0000ffff +#define RME_DEPTH_Q1 0x00038068 +#define RME_QCTRL_Q0 0x0003802c +#define __RME_INT_LATENCY_TIMER_MK 0xff000000 +#define __RME_INT_LATENCY_TIMER_SH 24 +#define __RME_INT_LATENCY_TIMER(_v) ((_v) << __RME_INT_LATENCY_TIMER_SH) +#define __RME_INT_DELAY_TIMER_MK 0x00ff0000 +#define __RME_INT_DELAY_TIMER_SH 16 +#define __RME_INT_DELAY_TIMER(_v) ((_v) << __RME_INT_DELAY_TIMER_SH) +#define __RME_INT_DELAY_DISABLE 0x00008000 +#define __RME_DLY_DELAY_DISABLE 0x00004000 +#define __RME_ACK_PENDING 0x00002000 +#define __RME_FULL_INTERRUPT_DISABLE 0x00001000 +#define __RME_CTRL_UNUSED10_MK 0x00000c00 +#define __RME_CTRL_UNUSED10_SH 10 +#define __RME_CTRL_UNUSED10(_v) ((_v) << __RME_CTRL_UNUSED10_SH) +#define __RME_PCIEID_MK 0x00000300 +#define __RME_PCIEID_SH 8 +#define __RME_PCIEID(_v) ((_v) << __RME_PCIEID_SH) +#define __RME_CTRL_UNUSED00_MK 0x000000fe +#define __RME_CTRL_UNUSED00_SH 1 +#define __RME_CTRL_UNUSED00(_v) ((_v) << __RME_CTRL_UNUSED00_SH) +#define __RME_ESIZE 0x00000001 +#define RME_QCTRL_Q1 0x0003806c +#define __RME_CTRL_UNUSED11_MK 0x00000c00 +#define __RME_CTRL_UNUSED11_SH 10 +#define __RME_CTRL_UNUSED11(_v) ((_v) << __RME_CTRL_UNUSED11_SH) +#define __RME_CTRL_UNUSED01_MK 0x000000fe +#define __RME_CTRL_UNUSED01_SH 1 +#define __RME_CTRL_UNUSED01(_v) ((_v) << __RME_CTRL_UNUSED01_SH) +#define PSS_CTL_REG 0x00018800 +#define __PSS_I2C_CLK_DIV_MK 0x007f0000 +#define __PSS_I2C_CLK_DIV_SH 16 +#define __PSS_I2C_CLK_DIV(_v) ((_v) << __PSS_I2C_CLK_DIV_SH) +#define __PSS_LMEM_INIT_DONE 0x00001000 +#define __PSS_LMEM_RESET 0x00000200 +#define __PSS_LMEM_INIT_EN 0x00000100 +#define __PSS_LPU1_RESET 0x00000002 +#define __PSS_LPU0_RESET 0x00000001 +#define HQM_QSET0_RXQ_DRBL_P0 0x00038000 +#define __RXQ0_ADD_VECTORS_P 0x80000000 +#define __RXQ0_STOP_P 0x40000000 +#define __RXQ0_PRD_PTR_P 0x0000ffff +#define HQM_QSET1_RXQ_DRBL_P0 0x00038080 +#define __RXQ1_ADD_VECTORS_P 0x80000000 +#define __RXQ1_STOP_P 0x40000000 +#define __RXQ1_PRD_PTR_P 0x0000ffff +#define HQM_QSET0_RXQ_DRBL_P1 0x0003c000 +#define HQM_QSET1_RXQ_DRBL_P1 0x0003c080 +#define HQM_QSET0_TXQ_DRBL_P0 0x00038020 +#define __TXQ0_ADD_VECTORS_P 0x80000000 +#define __TXQ0_STOP_P 0x40000000 +#define __TXQ0_PRD_PTR_P 0x0000ffff +#define HQM_QSET1_TXQ_DRBL_P0 0x000380a0 +#define __TXQ1_ADD_VECTORS_P 0x80000000 +#define __TXQ1_STOP_P 0x40000000 +#define __TXQ1_PRD_PTR_P 0x0000ffff +#define HQM_QSET0_TXQ_DRBL_P1 0x0003c020 +#define HQM_QSET1_TXQ_DRBL_P1 0x0003c0a0 +#define HQM_QSET0_IB_DRBL_1_P0 0x00038040 +#define __IB1_0_ACK_P 0x80000000 +#define __IB1_0_DISABLE_P 0x40000000 +#define __IB1_0_NUM_OF_ACKED_EVENTS_P 0x0000ffff +#define HQM_QSET1_IB_DRBL_1_P0 0x000380c0 +#define __IB1_1_ACK_P 0x80000000 +#define __IB1_1_DISABLE_P 0x40000000 +#define __IB1_1_NUM_OF_ACKED_EVENTS_P 0x0000ffff +#define HQM_QSET0_IB_DRBL_1_P1 0x0003c040 +#define HQM_QSET1_IB_DRBL_1_P1 0x0003c0c0 +#define HQM_QSET0_IB_DRBL_2_P0 0x00038060 +#define __IB2_0_ACK_P 0x80000000 +#define __IB2_0_DISABLE_P 0x40000000 +#define __IB2_0_NUM_OF_ACKED_EVENTS_P 0x0000ffff +#define HQM_QSET1_IB_DRBL_2_P0 0x000380e0 +#define __IB2_1_ACK_P 0x80000000 +#define __IB2_1_DISABLE_P 0x40000000 +#define __IB2_1_NUM_OF_ACKED_EVENTS_P 0x0000ffff +#define HQM_QSET0_IB_DRBL_2_P1 0x0003c060 +#define HQM_QSET1_IB_DRBL_2_P1 0x0003c0e0 + + +/* + * These definitions are either in error/missing in spec. Its auto-generated + * from hard coded values in regparse.pl. + */ +#define __EMPHPOST_AT_4G_MK_FIX 0x0000001c +#define __EMPHPOST_AT_4G_SH_FIX 0x00000002 +#define __EMPHPRE_AT_4G_FIX 0x00000003 +#define __SFP_TXRATE_EN_FIX 0x00000100 +#define __SFP_RXRATE_EN_FIX 0x00000080 + + +/* + * These register definitions are auto-generated from hard coded values + * in regparse.pl. + */ + + +/* + * These register mapping definitions are auto-generated from mapping tables + * in regparse.pl. + */ +#define BFA_IOC0_HBEAT_REG HOST_SEM0_INFO_REG +#define BFA_IOC0_STATE_REG HOST_SEM1_INFO_REG +#define BFA_IOC1_HBEAT_REG HOST_SEM2_INFO_REG +#define BFA_IOC1_STATE_REG HOST_SEM3_INFO_REG +#define BFA_FW_USE_COUNT HOST_SEM4_INFO_REG + +#define CPE_DEPTH_Q(__n) \ + (CPE_DEPTH_Q0 + (__n) * (CPE_DEPTH_Q1 - CPE_DEPTH_Q0)) +#define CPE_QCTRL_Q(__n) \ + (CPE_QCTRL_Q0 + (__n) * (CPE_QCTRL_Q1 - CPE_QCTRL_Q0)) +#define CPE_PI_PTR_Q(__n) \ + (CPE_PI_PTR_Q0 + (__n) * (CPE_PI_PTR_Q1 - CPE_PI_PTR_Q0)) +#define CPE_CI_PTR_Q(__n) \ + (CPE_CI_PTR_Q0 + (__n) * (CPE_CI_PTR_Q1 - CPE_CI_PTR_Q0)) +#define RME_DEPTH_Q(__n) \ + (RME_DEPTH_Q0 + (__n) * (RME_DEPTH_Q1 - RME_DEPTH_Q0)) +#define RME_QCTRL_Q(__n) \ + (RME_QCTRL_Q0 + (__n) * (RME_QCTRL_Q1 - RME_QCTRL_Q0)) +#define RME_PI_PTR_Q(__n) \ + (RME_PI_PTR_Q0 + (__n) * (RME_PI_PTR_Q1 - RME_PI_PTR_Q0)) +#define RME_CI_PTR_Q(__n) \ + (RME_CI_PTR_Q0 + (__n) * (RME_CI_PTR_Q1 - RME_CI_PTR_Q0)) +#define HQM_QSET_RXQ_DRBL_P0(__n) \ + (HQM_QSET0_RXQ_DRBL_P0 + (__n) * (HQM_QSET1_RXQ_DRBL_P0 - \ + HQM_QSET0_RXQ_DRBL_P0)) +#define HQM_QSET_TXQ_DRBL_P0(__n) \ + (HQM_QSET0_TXQ_DRBL_P0 + (__n) * (HQM_QSET1_TXQ_DRBL_P0 - \ + HQM_QSET0_TXQ_DRBL_P0)) +#define HQM_QSET_IB_DRBL_1_P0(__n) \ + (HQM_QSET0_IB_DRBL_1_P0 + (__n) * (HQM_QSET1_IB_DRBL_1_P0 - \ + HQM_QSET0_IB_DRBL_1_P0)) +#define HQM_QSET_IB_DRBL_2_P0(__n) \ + (HQM_QSET0_IB_DRBL_2_P0 + (__n) * (HQM_QSET1_IB_DRBL_2_P0 - \ + HQM_QSET0_IB_DRBL_2_P0)) +#define HQM_QSET_RXQ_DRBL_P1(__n) \ + (HQM_QSET0_RXQ_DRBL_P1 + (__n) * (HQM_QSET1_RXQ_DRBL_P1 - \ + HQM_QSET0_RXQ_DRBL_P1)) +#define HQM_QSET_TXQ_DRBL_P1(__n) \ + (HQM_QSET0_TXQ_DRBL_P1 + (__n) * (HQM_QSET1_TXQ_DRBL_P1 - \ + HQM_QSET0_TXQ_DRBL_P1)) +#define HQM_QSET_IB_DRBL_1_P1(__n) \ + (HQM_QSET0_IB_DRBL_1_P1 + (__n) * (HQM_QSET1_IB_DRBL_1_P1 - \ + HQM_QSET0_IB_DRBL_1_P1)) +#define HQM_QSET_IB_DRBL_2_P1(__n) \ + (HQM_QSET0_IB_DRBL_2_P1 + (__n) * (HQM_QSET1_IB_DRBL_2_P1 - \ + HQM_QSET0_IB_DRBL_2_P1)) + +#define CPE_Q_NUM(__fn, __q) (((__fn) << 2) + (__q)) +#define RME_Q_NUM(__fn, __q) (((__fn) << 2) + (__q)) +#define CPE_Q_MASK(__q) ((__q) & 0x3) +#define RME_Q_MASK(__q) ((__q) & 0x3) + + +/* + * PCI MSI-X vector defines + */ +enum { + BFA_MSIX_CPE_Q0 = 0, + BFA_MSIX_CPE_Q1 = 1, + BFA_MSIX_CPE_Q2 = 2, + BFA_MSIX_CPE_Q3 = 3, + BFA_MSIX_RME_Q0 = 4, + BFA_MSIX_RME_Q1 = 5, + BFA_MSIX_RME_Q2 = 6, + BFA_MSIX_RME_Q3 = 7, + BFA_MSIX_LPU_ERR = 8, + BFA_MSIX_CT_MAX = 9, +}; + +/* + * And corresponding host interrupt status bit field defines + */ +#define __HFN_INT_CPE_Q0 0x00000001U +#define __HFN_INT_CPE_Q1 0x00000002U +#define __HFN_INT_CPE_Q2 0x00000004U +#define __HFN_INT_CPE_Q3 0x00000008U +#define __HFN_INT_CPE_Q4 0x00000010U +#define __HFN_INT_CPE_Q5 0x00000020U +#define __HFN_INT_CPE_Q6 0x00000040U +#define __HFN_INT_CPE_Q7 0x00000080U +#define __HFN_INT_RME_Q0 0x00000100U +#define __HFN_INT_RME_Q1 0x00000200U +#define __HFN_INT_RME_Q2 0x00000400U +#define __HFN_INT_RME_Q3 0x00000800U +#define __HFN_INT_RME_Q4 0x00001000U +#define __HFN_INT_RME_Q5 0x00002000U +#define __HFN_INT_RME_Q6 0x00004000U +#define __HFN_INT_RME_Q7 0x00008000U +#define __HFN_INT_ERR_EMC 0x00010000U +#define __HFN_INT_ERR_LPU0 0x00020000U +#define __HFN_INT_ERR_LPU1 0x00040000U +#define __HFN_INT_ERR_PSS 0x00080000U +#define __HFN_INT_MBOX_LPU0 0x00100000U +#define __HFN_INT_MBOX_LPU1 0x00200000U +#define __HFN_INT_MBOX1_LPU0 0x00400000U +#define __HFN_INT_MBOX1_LPU1 0x00800000U +#define __HFN_INT_CPE_MASK 0x000000ffU +#define __HFN_INT_RME_MASK 0x0000ff00U + + +/* + * catapult memory map. + */ +#define LL_PGN_HQM0 0x0096 +#define LL_PGN_HQM1 0x0097 +#define PSS_SMEM_PAGE_START 0x8000 +#define PSS_SMEM_PGNUM(_pg0, _ma) ((_pg0) + ((_ma) >> 15)) +#define PSS_SMEM_PGOFF(_ma) ((_ma) & 0x7fff) + +/* + * End of catapult memory map + */ + + +#endif /* __BFI_CTREG_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_fabric.h b/drivers/scsi/bfa/include/bfi/bfi_fabric.h new file mode 100644 index 000000000000..c0669ed41078 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_fabric.h @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#ifndef __BFI_FABRIC_H__ +#define __BFI_FABRIC_H__ + +#include + +#pragma pack(1) + +enum bfi_fabric_h2i_msgs { + BFI_FABRIC_H2I_CREATE_REQ = 1, + BFI_FABRIC_H2I_DELETE_REQ = 2, + BFI_FABRIC_H2I_SETAUTH = 3, +}; + +enum bfi_fabric_i2h_msgs { + BFI_FABRIC_I2H_CREATE_RSP = BFA_I2HM(1), + BFI_FABRIC_I2H_DELETE_RSP = BFA_I2HM(2), + BFI_FABRIC_I2H_SETAUTH_RSP = BFA_I2HM(3), + BFI_FABRIC_I2H_ONLINE = BFA_I2HM(4), + BFI_FABRIC_I2H_OFFLINE = BFA_I2HM(5), +}; + +struct bfi_fabric_create_req_s { + bfi_mhdr_t mh; /* common msg header */ + u8 vf_en; /* virtual fabric enable */ + u8 rsvd; + u16 vf_id; /* virtual fabric ID */ + wwn_t pwwn; /* port name */ + wwn_t nwwn; /* node name */ +}; + +struct bfi_fabric_create_rsp_s { + bfi_mhdr_t mh; /* common msg header */ + u16 bfa_handle; /* host fabric handle */ + u8 status; /* fabric create status */ + u8 rsvd; +}; + +struct bfi_fabric_delete_req_s { + bfi_mhdr_t mh; /* common msg header */ + u16 fw_handle; /* firmware fabric handle */ + u16 rsvd; +}; + +struct bfi_fabric_delete_rsp_s { + bfi_mhdr_t mh; /* common msg header */ + u16 bfa_handle; /* host fabric handle */ + u8 status; /* fabric deletion status */ + u8 rsvd; +}; + +#define BFI_FABRIC_AUTHSECRET_LEN 64 +struct bfi_fabric_setauth_req_s { + bfi_mhdr_t mh; /* common msg header */ + u16 fw_handle; /* f/w handle of fabric */ + u8 algorithm; + u8 group; + u8 secret[BFI_FABRIC_AUTHSECRET_LEN]; +}; + +union bfi_fabric_h2i_msg_u { + bfi_msg_t *msg; + struct bfi_fabric_create_req_s *create_req; + struct bfi_fabric_delete_req_s *delete_req; +}; + +union bfi_fabric_i2h_msg_u { + bfi_msg_t *msg; + struct bfi_fabric_create_rsp_s *create_rsp; + struct bfi_fabric_delete_rsp_s *delete_rsp; +}; + +#pragma pack() + +#endif /* __BFI_FABRIC_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_fcpim.h b/drivers/scsi/bfa/include/bfi/bfi_fcpim.h new file mode 100644 index 000000000000..52c059fb4c3a --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_fcpim.h @@ -0,0 +1,301 @@ +/* + * 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. + */ + +#ifndef __BFI_FCPIM_H__ +#define __BFI_FCPIM_H__ + +#include "bfi.h" +#include + +#pragma pack(1) + +/* + * Initiator mode I-T nexus interface defines. + */ + +enum bfi_itnim_h2i { + BFI_ITNIM_H2I_CREATE_REQ = 1, /* i-t nexus creation */ + BFI_ITNIM_H2I_DELETE_REQ = 2, /* i-t nexus deletion */ +}; + +enum bfi_itnim_i2h { + BFI_ITNIM_I2H_CREATE_RSP = BFA_I2HM(1), + BFI_ITNIM_I2H_DELETE_RSP = BFA_I2HM(2), + BFI_ITNIM_I2H_SLER_EVENT = BFA_I2HM(3), +}; + +struct bfi_itnim_create_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 fw_handle; /* f/w handle for itnim */ + u8 class; /* FC class for IO */ + u8 seq_rec; /* sequence recovery support */ + u8 msg_no; /* seq id of the msg */ +}; + +struct bfi_itnim_create_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* bfa handle for itnim */ + u8 status; /* fcp request status */ + u8 seq_id; /* seq id of the msg */ +}; + +struct bfi_itnim_delete_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 fw_handle; /* f/w itnim handle */ + u8 seq_id; /* seq id of the msg */ + u8 rsvd; +}; + +struct bfi_itnim_delete_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* bfa handle for itnim */ + u8 status; /* fcp request status */ + u8 seq_id; /* seq id of the msg */ +}; + +struct bfi_itnim_sler_event_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* bfa handle for itnim */ + u16 rsvd; +}; + +union bfi_itnim_h2i_msg_u { + struct bfi_itnim_create_req_s *create_req; + struct bfi_itnim_delete_req_s *delete_req; + struct bfi_msg_s *msg; +}; + +union bfi_itnim_i2h_msg_u { + struct bfi_itnim_create_rsp_s *create_rsp; + struct bfi_itnim_delete_rsp_s *delete_rsp; + struct bfi_itnim_sler_event_s *sler_event; + struct bfi_msg_s *msg; +}; + +/* + * Initiator mode IO interface defines. + */ + +enum bfi_ioim_h2i { + BFI_IOIM_H2I_IOABORT_REQ = 1, /* IO abort request */ + BFI_IOIM_H2I_IOCLEANUP_REQ = 2, /* IO cleanup request */ +}; + +enum bfi_ioim_i2h { + BFI_IOIM_I2H_IO_RSP = BFA_I2HM(1), /* non-fp IO response */ + BFI_IOIM_I2H_IOABORT_RSP = BFA_I2HM(2),/* ABORT rsp */ +}; + +/** + * IO command DIF info + */ +struct bfi_ioim_dif_s { + u32 dif_info[4]; +}; + +/** + * FCP IO messages overview + * + * @note + * - Max CDB length supported is 64 bytes. + * - SCSI Linked commands and SCSI bi-directional Commands not + * supported. + * + */ +struct bfi_ioim_req_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 io_tag; /* I/O tag */ + u16 rport_hdl; /* itnim/rport firmware handle */ + struct fcp_cmnd_s cmnd; /* IO request info */ + + /** + * SG elements array within the IO request must be double word + * aligned. This aligment is required to optimize SGM setup for the IO. + */ + struct bfi_sge_s sges[BFI_SGE_INLINE_MAX]; + u8 io_timeout; + u8 dif_en; + u8 rsvd_a[2]; + struct bfi_ioim_dif_s dif; +}; + +/** + * This table shows various IO status codes from firmware and their + * meaning. Host driver can use these status codes to further process + * IO completions. + * + * BFI_IOIM_STS_OK : IO completed with error free SCSI & + * transport status. + * - io-tag can be reused. + * + * BFA_IOIM_STS_SCSI_ERR : IO completed with scsi error. + * - io-tag can be reused. + * + * BFI_IOIM_STS_HOST_ABORTED : IO was aborted successfully due to + * host request. + * - io-tag cannot be reused yet. + * + * BFI_IOIM_STS_ABORTED : IO was aborted successfully + * internally by f/w. + * - io-tag cannot be reused yet. + * + * BFI_IOIM_STS_TIMEDOUT : IO timedout and ABTS/RRQ is happening + * in the firmware and + * - io-tag cannot be reused yet. + * + * BFI_IOIM_STS_SQER_NEEDED : Firmware could not recover the IO + * with sequence level error + * logic and hence host needs to retry + * this IO with a different IO tag + * - io-tag cannot be used yet. + * + * BFI_IOIM_STS_NEXUS_ABORT : Second Level Error Recovery from host + * is required because 2 consecutive ABTS + * timedout and host needs logout and + * re-login with the target + * - io-tag cannot be used yet. + * + * BFI_IOIM_STS_UNDERRUN : IO completed with SCSI status good, + * but the data tranferred is less than + * the fcp data length in the command. + * ex. SCSI INQUIRY where transferred + * data length and residue count in FCP + * response accounts for total fcp-dl + * - io-tag can be reused. + * + * BFI_IOIM_STS_OVERRUN : IO completed with SCSI status good, + * but the data transerred is more than + * fcp data length in the command. ex. + * TAPE IOs where blocks can of unequal + * lengths. + * - io-tag can be reused. + * + * BFI_IOIM_STS_RES_FREE : Firmware has completed using io-tag + * during abort process + * - io-tag can be reused. + * + * BFI_IOIM_STS_PROTO_ERR : Firmware detected a protocol error. + * ex target sent more data than + * requested, or there was data frame + * loss and other reasons + * - io-tag cannot be used yet. + * + * BFI_IOIM_STS_DIF_ERR : Firwmare detected DIF error. ex: DIF + * CRC err or Ref Tag err or App tag err. + * - io-tag can be reused. + * + * BFA_IOIM_STS_TSK_MGT_ABORT : IO was aborted because of Task + * Management command from the host + * - io-tag can be reused. + * + * BFI_IOIM_STS_UTAG : Firmware does not know about this + * io_tag. + * - io-tag can be reused. + */ +enum bfi_ioim_status { + BFI_IOIM_STS_OK = 0, + BFI_IOIM_STS_HOST_ABORTED = 1, + BFI_IOIM_STS_ABORTED = 2, + BFI_IOIM_STS_TIMEDOUT = 3, + BFI_IOIM_STS_RES_FREE = 4, + BFI_IOIM_STS_SQER_NEEDED = 5, + BFI_IOIM_STS_PROTO_ERR = 6, + BFI_IOIM_STS_UTAG = 7, + BFI_IOIM_STS_PATHTOV = 8, +}; + +#define BFI_IOIM_SNSLEN (256) +/** + * I/O response message + */ +struct bfi_ioim_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 io_tag; /* completed IO tag */ + u16 bfa_rport_hndl; /* releated rport handle */ + u8 io_status; /* IO completion status */ + u8 reuse_io_tag; /* IO tag can be reused */ + u16 abort_tag; /* host abort request tag */ + u8 scsi_status; /* scsi status from target */ + u8 sns_len; /* scsi sense length */ + u8 resid_flags; /* IO residue flags */ + u8 rsvd_a; + u32 residue; /* IO residual length in bytes */ + u32 rsvd_b[3]; +}; + +struct bfi_ioim_abort_req_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 io_tag; /* I/O tag */ + u16 abort_tag; /* unique request tag */ +}; + +/* + * Initiator mode task management command interface defines. + */ + +enum bfi_tskim_h2i { + BFI_TSKIM_H2I_TM_REQ = 1, /* task-mgmt command */ + BFI_TSKIM_H2I_ABORT_REQ = 2, /* task-mgmt command */ +}; + +enum bfi_tskim_i2h { + BFI_TSKIM_I2H_TM_RSP = BFA_I2HM(1), +}; + +struct bfi_tskim_req_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 tsk_tag; /* task management tag */ + u16 itn_fhdl; /* itn firmware handle */ + lun_t lun; /* LU number */ + u8 tm_flags; /* see fcp_tm_cmnd_t */ + u8 t_secs; /* Timeout value in seconds */ + u8 rsvd[2]; +}; + +struct bfi_tskim_abortreq_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 tsk_tag; /* task management tag */ + u16 rsvd; +}; + +enum bfi_tskim_status { + /* + * Following are FCP-4 spec defined status codes, + * **DO NOT CHANGE THEM ** + */ + BFI_TSKIM_STS_OK = 0, + BFI_TSKIM_STS_NOT_SUPP = 4, + BFI_TSKIM_STS_FAILED = 5, + + /** + * Defined by BFA + */ + BFI_TSKIM_STS_TIMEOUT = 10, /* TM request timedout */ + BFI_TSKIM_STS_ABORTED = 11, /* Aborted on host request */ +}; + +struct bfi_tskim_rsp_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 tsk_tag; /* task mgmt cmnd tag */ + u8 tsk_status; /* @ref bfi_tskim_status */ + u8 rsvd; +}; + +#pragma pack() + +#endif /* __BFI_FCPIM_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_fcxp.h b/drivers/scsi/bfa/include/bfi/bfi_fcxp.h new file mode 100644 index 000000000000..e0e995a32828 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_fcxp.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef __BFI_FCXP_H__ +#define __BFI_FCXP_H__ + +#include "bfi.h" + +#pragma pack(1) + +enum bfi_fcxp_h2i { + BFI_FCXP_H2I_SEND_REQ = 1, +}; + +enum bfi_fcxp_i2h { + BFI_FCXP_I2H_SEND_RSP = BFA_I2HM(1), +}; + +#define BFA_FCXP_MAX_SGES 2 + +/** + * FCXP send request structure + */ +struct bfi_fcxp_send_req_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 fcxp_tag; /* driver request tag */ + u16 max_frmsz; /* max send frame size */ + u16 vf_id; /* vsan tag if applicable */ + u16 rport_fw_hndl; /* FW Handle for the remote port */ + u8 class; /* FC class used for req/rsp */ + u8 rsp_timeout; /* timeout in secs, 0-no response */ + u8 cts; /* continue sequence */ + u8 lp_tag; /* lport tag */ + struct fchs_s fchs; /* request FC header structure */ + u32 req_len; /* request payload length */ + u32 rsp_maxlen; /* max response length expected */ + struct bfi_sge_s req_sge[BFA_FCXP_MAX_SGES]; /* request buf */ + struct bfi_sge_s rsp_sge[BFA_FCXP_MAX_SGES]; /* response buf */ +}; + +/** + * FCXP send response structure + */ +struct bfi_fcxp_send_rsp_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 fcxp_tag; /* send request tag */ + u8 req_status; /* request status */ + u8 rsvd; + u32 rsp_len; /* actual response length */ + u32 residue_len; /* residual response length */ + struct fchs_s fchs; /* response FC header structure */ +}; + +#pragma pack() + +#endif /* __BFI_FCXP_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_ioc.h b/drivers/scsi/bfa/include/bfi/bfi_ioc.h new file mode 100644 index 000000000000..026e9c06ae97 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_ioc.h @@ -0,0 +1,202 @@ +/* + * 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. + */ + +#ifndef __BFI_IOC_H__ +#define __BFI_IOC_H__ + +#include "bfi.h" +#include + +#pragma pack(1) + +enum bfi_ioc_h2i_msgs { + BFI_IOC_H2I_ENABLE_REQ = 1, + BFI_IOC_H2I_DISABLE_REQ = 2, + BFI_IOC_H2I_GETATTR_REQ = 3, + BFI_IOC_H2I_DBG_SYNC = 4, + BFI_IOC_H2I_DBG_DUMP = 5, +}; + +enum bfi_ioc_i2h_msgs { + BFI_IOC_I2H_ENABLE_REPLY = BFA_I2HM(1), + BFI_IOC_I2H_DISABLE_REPLY = BFA_I2HM(2), + BFI_IOC_I2H_GETATTR_REPLY = BFA_I2HM(3), + BFI_IOC_I2H_READY_EVENT = BFA_I2HM(4), + BFI_IOC_I2H_HBEAT = BFA_I2HM(5), +}; + +/** + * BFI_IOC_H2I_GETATTR_REQ message + */ +struct bfi_ioc_getattr_req_s { + struct bfi_mhdr_s mh; + union bfi_addr_u attr_addr; +}; + +struct bfi_ioc_attr_s { + wwn_t mfg_wwn; + mac_t mfg_mac; + u16 rsvd_a; + char brcd_serialnum[STRSZ(BFA_MFG_SERIALNUM_SIZE)]; + u8 pcie_gen; + u8 pcie_lanes_orig; + u8 pcie_lanes; + u8 rx_bbcredit; /* receive buffer credits */ + u32 adapter_prop; /* adapter properties */ + u16 maxfrsize; /* max receive frame size */ + char asic_rev; + u8 rsvd_b; + char fw_version[BFA_VERSION_LEN]; + char optrom_version[BFA_VERSION_LEN]; + struct bfa_mfg_vpd_s vpd; +}; + +/** + * BFI_IOC_I2H_GETATTR_REPLY message + */ +struct bfi_ioc_getattr_reply_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u8 status; /* cfg reply status */ + u8 rsvd[3]; +}; + +/** + * Firmware memory page offsets + */ +#define BFI_IOC_SMEM_PG0_CB (0x40) +#define BFI_IOC_SMEM_PG0_CT (0x180) + +/** + * Firmware trace offset + */ +#define BFI_IOC_TRC_OFF (0x4b00) +#define BFI_IOC_TRC_ENTS 256 + +#define BFI_IOC_FW_SIGNATURE (0xbfadbfad) +#define BFI_IOC_MD5SUM_SZ 4 +struct bfi_ioc_image_hdr_s { + u32 signature; /* constant signature */ + u32 rsvd_a; + u32 exec; /* exec vector */ + u32 param; /* parameters */ + u32 rsvd_b[4]; + u32 md5sum[BFI_IOC_MD5SUM_SZ]; +}; + +/** + * BFI_IOC_I2H_READY_EVENT message + */ +struct bfi_ioc_rdy_event_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 init_status; /* init event status */ + u8 rsvd[3]; +}; + +struct bfi_ioc_hbeat_s { + struct bfi_mhdr_s mh; /* common msg header */ + u32 hb_count; /* current heart beat count */ +}; + +/** + * IOC hardware/firmware state + */ +enum bfi_ioc_state { + BFI_IOC_UNINIT = 0, /* not initialized */ + BFI_IOC_INITING = 1, /* h/w is being initialized */ + BFI_IOC_HWINIT = 2, /* h/w is initialized */ + BFI_IOC_CFG = 3, /* IOC configuration in progress */ + BFI_IOC_OP = 4, /* IOC is operational */ + BFI_IOC_DISABLING = 5, /* IOC is being disabled */ + BFI_IOC_DISABLED = 6, /* IOC is disabled */ + BFI_IOC_CFG_DISABLED = 7, /* IOC is being disabled;transient */ + BFI_IOC_HBFAIL = 8, /* IOC heart-beat failure */ + BFI_IOC_MEMTEST = 9, /* IOC is doing memtest */ +}; + +#define BFI_IOC_ENDIAN_SIG 0x12345678 + +enum { + BFI_ADAPTER_TYPE_FC = 0x01, /* FC adapters */ + BFI_ADAPTER_TYPE_MK = 0x0f0000, /* adapter type mask */ + BFI_ADAPTER_TYPE_SH = 16, /* adapter type shift */ + BFI_ADAPTER_NPORTS_MK = 0xff00, /* number of ports mask */ + BFI_ADAPTER_NPORTS_SH = 8, /* number of ports shift */ + BFI_ADAPTER_SPEED_MK = 0xff, /* adapter speed mask */ + BFI_ADAPTER_SPEED_SH = 0, /* adapter speed shift */ + BFI_ADAPTER_PROTO = 0x100000, /* prototype adapaters */ + BFI_ADAPTER_TTV = 0x200000, /* TTV debug capable */ + BFI_ADAPTER_UNSUPP = 0x400000, /* unknown adapter type */ +}; + +#define BFI_ADAPTER_GETP(__prop,__adap_prop) \ + (((__adap_prop) & BFI_ADAPTER_ ## __prop ## _MK) >> \ + BFI_ADAPTER_ ## __prop ## _SH) +#define BFI_ADAPTER_SETP(__prop, __val) \ + ((__val) << BFI_ADAPTER_ ## __prop ## _SH) +#define BFI_ADAPTER_IS_PROTO(__adap_type) \ + ((__adap_type) & BFI_ADAPTER_PROTO) +#define BFI_ADAPTER_IS_TTV(__adap_type) \ + ((__adap_type) & BFI_ADAPTER_TTV) +#define BFI_ADAPTER_IS_UNSUPP(__adap_type) \ + ((__adap_type) & BFI_ADAPTER_UNSUPP) +#define BFI_ADAPTER_IS_SPECIAL(__adap_type) \ + ((__adap_type) & (BFI_ADAPTER_TTV | BFI_ADAPTER_PROTO | \ + BFI_ADAPTER_UNSUPP)) + +/** + * BFI_IOC_H2I_ENABLE_REQ & BFI_IOC_H2I_DISABLE_REQ messages + */ +struct bfi_ioc_ctrl_req_s { + struct bfi_mhdr_s mh; + u8 ioc_class; + u8 rsvd[3]; +}; + +/** + * BFI_IOC_I2H_ENABLE_REPLY & BFI_IOC_I2H_DISABLE_REPLY messages + */ +struct bfi_ioc_ctrl_reply_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u8 status; /* enable/disable status */ + u8 rsvd[3]; +}; + +#define BFI_IOC_MSGSZ 8 +/** + * H2I Messages + */ +union bfi_ioc_h2i_msg_u { + struct bfi_mhdr_s mh; + struct bfi_ioc_ctrl_req_s enable_req; + struct bfi_ioc_ctrl_req_s disable_req; + struct bfi_ioc_getattr_req_s getattr_req; + u32 mboxmsg[BFI_IOC_MSGSZ]; +}; + +/** + * I2H Messages + */ +union bfi_ioc_i2h_msg_u { + struct bfi_mhdr_s mh; + struct bfi_ioc_rdy_event_s rdy_event; + u32 mboxmsg[BFI_IOC_MSGSZ]; +}; + +#pragma pack() + +#endif /* __BFI_IOC_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_iocfc.h b/drivers/scsi/bfa/include/bfi/bfi_iocfc.h new file mode 100644 index 000000000000..c3760df72575 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_iocfc.h @@ -0,0 +1,177 @@ +/* + * 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. + */ + +#ifndef __BFI_IOCFC_H__ +#define __BFI_IOCFC_H__ + +#include "bfi.h" +#include +#include +#include + +#pragma pack(1) + +enum bfi_iocfc_h2i_msgs { + BFI_IOCFC_H2I_CFG_REQ = 1, + BFI_IOCFC_H2I_GET_STATS_REQ = 2, + BFI_IOCFC_H2I_CLEAR_STATS_REQ = 3, + BFI_IOCFC_H2I_SET_INTR_REQ = 4, + BFI_IOCFC_H2I_UPDATEQ_REQ = 5, +}; + +enum bfi_iocfc_i2h_msgs { + BFI_IOCFC_I2H_CFG_REPLY = BFA_I2HM(1), + BFI_IOCFC_I2H_GET_STATS_RSP = BFA_I2HM(2), + BFI_IOCFC_I2H_CLEAR_STATS_RSP = BFA_I2HM(3), + BFI_IOCFC_I2H_UPDATEQ_RSP = BFA_I2HM(5), +}; + +struct bfi_iocfc_cfg_s { + u8 num_cqs; /* Number of CQs to be used */ + u8 sense_buf_len; /* SCSI sense length */ + u8 trunk_enabled; /* port trunking enabled */ + u8 trunk_ports; /* trunk ports bit map */ + u32 endian_sig; /* endian signature of host */ + + /** + * Request and response circular queue base addresses, size and + * shadow index pointers. + */ + union bfi_addr_u req_cq_ba[BFI_IOC_MAX_CQS]; + union bfi_addr_u req_shadow_ci[BFI_IOC_MAX_CQS]; + u16 req_cq_elems[BFI_IOC_MAX_CQS]; + union bfi_addr_u rsp_cq_ba[BFI_IOC_MAX_CQS]; + union bfi_addr_u rsp_shadow_pi[BFI_IOC_MAX_CQS]; + u16 rsp_cq_elems[BFI_IOC_MAX_CQS]; + + union bfi_addr_u stats_addr; /* DMA-able address for stats */ + union bfi_addr_u cfgrsp_addr; /* config response dma address */ + union bfi_addr_u ioim_snsbase; /* IO sense buffer base address */ + struct bfa_iocfc_intr_attr_s intr_attr; /* IOC interrupt attributes */ +}; + +/** + * Boot target wwn information for this port. This contains either the stored + * or discovered boot target port wwns for the port. + */ +struct bfi_iocfc_bootwwns { + wwn_t wwn[BFA_BOOT_BOOTLUN_MAX]; + u8 nwwns; + u8 rsvd[7]; +}; + +struct bfi_iocfc_cfgrsp_s { + struct bfa_iocfc_fwcfg_s fwcfg; + struct bfa_iocfc_intr_attr_s intr_attr; + struct bfi_iocfc_bootwwns bootwwns; +}; + +/** + * BFI_IOCFC_H2I_CFG_REQ message + */ +struct bfi_iocfc_cfg_req_s { + struct bfi_mhdr_s mh; + union bfi_addr_u ioc_cfg_dma_addr; +}; + +/** + * BFI_IOCFC_I2H_CFG_REPLY message + */ +struct bfi_iocfc_cfg_reply_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u8 cfg_success; /* cfg reply status */ + u8 lpu_bm; /* LPUs assigned for this IOC */ + u8 rsvd[2]; +}; + +/** + * BFI_IOCFC_H2I_GET_STATS_REQ & BFI_IOCFC_H2I_CLEAR_STATS_REQ messages + */ +struct bfi_iocfc_stats_req_s { + struct bfi_mhdr_s mh; /* msg header */ + u32 msgtag; /* msgtag for reply */ +}; + +/** + * BFI_IOCFC_I2H_GET_STATS_RSP & BFI_IOCFC_I2H_CLEAR_STATS_RSP messages + */ +struct bfi_iocfc_stats_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 status; /* reply status */ + u8 rsvd[3]; + u32 msgtag; /* msgtag for reply */ +}; + +/** + * BFI_IOCFC_H2I_SET_INTR_REQ message + */ +struct bfi_iocfc_set_intr_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 coalesce; /* enable intr coalescing*/ + u8 rsvd[3]; + u16 delay; /* delay timer 0..1125us */ + u16 latency; /* latency timer 0..225us */ +}; + +/** + * BFI_IOCFC_H2I_UPDATEQ_REQ message + */ +struct bfi_iocfc_updateq_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u32 reqq_ba; /* reqq base addr */ + u32 rspq_ba; /* rspq base addr */ + u32 reqq_sci; /* reqq shadow ci */ + u32 rspq_spi; /* rspq shadow pi */ +}; + +/** + * BFI_IOCFC_I2H_UPDATEQ_RSP message + */ +struct bfi_iocfc_updateq_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 status; /* updateq status */ + u8 rsvd[3]; +}; + +/** + * H2I Messages + */ +union bfi_iocfc_h2i_msg_u { + struct bfi_mhdr_s mh; + struct bfi_iocfc_cfg_req_s cfg_req; + struct bfi_iocfc_stats_req_s stats_get; + struct bfi_iocfc_stats_req_s stats_clr; + struct bfi_iocfc_updateq_req_s updateq_req; + u32 mboxmsg[BFI_IOC_MSGSZ]; +}; + +/** + * I2H Messages + */ +union bfi_iocfc_i2h_msg_u { + struct bfi_mhdr_s mh; + struct bfi_iocfc_cfg_reply_s cfg_reply; + struct bfi_iocfc_stats_rsp_s stats_get_rsp; + struct bfi_iocfc_stats_rsp_s stats_clr_rsp; + struct bfi_iocfc_updateq_rsp_s updateq_rsp; + u32 mboxmsg[BFI_IOC_MSGSZ]; +}; + +#pragma pack() + +#endif /* __BFI_IOCFC_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_lport.h b/drivers/scsi/bfa/include/bfi/bfi_lport.h new file mode 100644 index 000000000000..29010614bac9 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_lport.h @@ -0,0 +1,89 @@ +/* + * 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. + */ + +#ifndef __BFI_LPORT_H__ +#define __BFI_LPORT_H__ + +#include + +#pragma pack(1) + +enum bfi_lport_h2i_msgs { + BFI_LPORT_H2I_CREATE_REQ = 1, + BFI_LPORT_H2I_DELETE_REQ = 2, +}; + +enum bfi_lport_i2h_msgs { + BFI_LPORT_I2H_CREATE_RSP = BFA_I2HM(1), + BFI_LPORT_I2H_DELETE_RSP = BFA_I2HM(2), + BFI_LPORT_I2H_ONLINE = BFA_I2HM(3), + BFI_LPORT_I2H_OFFLINE = BFA_I2HM(4), +}; + +#define BFI_LPORT_MAX_SYNNAME 64 + +enum bfi_lport_role_e { + BFI_LPORT_ROLE_FCPIM = 1, + BFI_LPORT_ROLE_FCPTM = 2, + BFI_LPORT_ROLE_IPFC = 4, +}; + +struct bfi_lport_create_req_s { + bfi_mhdr_t mh; /* common msg header */ + u16 fabric_fwhdl; /* parent fabric instance */ + u8 roles; /* lport FC-4 roles */ + u8 rsvd; + wwn_t pwwn; /* port name */ + wwn_t nwwn; /* node name */ + u8 symname[BFI_LPORT_MAX_SYNNAME]; +}; + +struct bfi_lport_create_rsp_s { + bfi_mhdr_t mh; /* common msg header */ + u8 status; /* lport creation status */ + u8 rsvd[3]; +}; + +struct bfi_lport_delete_req_s { + bfi_mhdr_t mh; /* common msg header */ + u16 fw_handle; /* firmware lport handle */ + u16 rsvd; +}; + +struct bfi_lport_delete_rsp_s { + bfi_mhdr_t mh; /* common msg header */ + u16 bfa_handle; /* host lport handle */ + u8 status; /* lport deletion status */ + u8 rsvd; +}; + +union bfi_lport_h2i_msg_u { + bfi_msg_t *msg; + struct bfi_lport_create_req_s *create_req; + struct bfi_lport_delete_req_s *delete_req; +}; + +union bfi_lport_i2h_msg_u { + bfi_msg_t *msg; + struct bfi_lport_create_rsp_s *create_rsp; + struct bfi_lport_delete_rsp_s *delete_rsp; +}; + +#pragma pack() + +#endif /* __BFI_LPORT_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_lps.h b/drivers/scsi/bfa/include/bfi/bfi_lps.h new file mode 100644 index 000000000000..414b0e30f6ef --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_lps.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#ifndef __BFI_LPS_H__ +#define __BFI_LPS_H__ + +#include + +#pragma pack(1) + +enum bfi_lps_h2i_msgs { + BFI_LPS_H2I_LOGIN_REQ = 1, + BFI_LPS_H2I_LOGOUT_REQ = 2, +}; + +enum bfi_lps_i2h_msgs { + BFI_LPS_H2I_LOGIN_RSP = BFA_I2HM(1), + BFI_LPS_H2I_LOGOUT_RSP = BFA_I2HM(2), +}; + +struct bfi_lps_login_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 lp_tag; + u8 alpa; + u16 pdu_size; + wwn_t pwwn; + wwn_t nwwn; + u8 fdisc; + u8 auth_en; + u8 rsvd[2]; +}; + +struct bfi_lps_login_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 lp_tag; + u8 status; + u8 lsrjt_rsn; + u8 lsrjt_expl; + wwn_t port_name; + wwn_t node_name; + u16 bb_credit; + u8 f_port; + u8 npiv_en; + u32 lp_pid : 24; + u32 auth_req : 8; + mac_t lp_mac; + mac_t fcf_mac; + u8 ext_status; + u8 brcd_switch;/* attached peer is brcd switch */ +}; + +struct bfi_lps_logout_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 lp_tag; + u8 rsvd[3]; + wwn_t port_name; +}; + +struct bfi_lps_logout_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 lp_tag; + u8 status; + u8 rsvd[2]; +}; + +union bfi_lps_h2i_msg_u { + struct bfi_mhdr_s *msg; + struct bfi_lps_login_req_s *login_req; + struct bfi_lps_logout_req_s *logout_req; +}; + +union bfi_lps_i2h_msg_u { + struct bfi_msg_s *msg; + struct bfi_lps_login_rsp_s *login_rsp; + struct bfi_lps_logout_rsp_s *logout_rsp; +}; + +#pragma pack() + +#endif /* __BFI_LPS_H__ */ + + diff --git a/drivers/scsi/bfa/include/bfi/bfi_port.h b/drivers/scsi/bfa/include/bfi/bfi_port.h new file mode 100644 index 000000000000..3ec3bea110ba --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_port.h @@ -0,0 +1,115 @@ +/* + * 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. + */ +#ifndef __BFI_PORT_H__ +#define __BFI_PORT_H__ + +#include +#include + +#pragma pack(1) + +enum bfi_port_h2i { + BFI_PORT_H2I_ENABLE_REQ = (1), + BFI_PORT_H2I_DISABLE_REQ = (2), + BFI_PORT_H2I_GET_STATS_REQ = (3), + BFI_PORT_H2I_CLEAR_STATS_REQ = (4), +}; + +enum bfi_port_i2h { + BFI_PORT_I2H_ENABLE_RSP = BFA_I2HM(1), + BFI_PORT_I2H_DISABLE_RSP = BFA_I2HM(2), + BFI_PORT_I2H_GET_STATS_RSP = BFA_I2HM(3), + BFI_PORT_I2H_CLEAR_STATS_RSP = BFA_I2HM(4), +}; + +/** + * Generic REQ type + */ +struct bfi_port_generic_req_s { + struct bfi_mhdr_s mh; /* msg header */ + u32 msgtag; /* msgtag for reply */ + u32 rsvd; +}; + +/** + * Generic RSP type + */ +struct bfi_port_generic_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 status; /* port enable status */ + u8 rsvd[3]; + u32 msgtag; /* msgtag for reply */ +}; + +/** + * @todo + * BFI_PORT_H2I_ENABLE_REQ + */ + +/** + * @todo + * BFI_PORT_I2H_ENABLE_RSP + */ + +/** + * BFI_PORT_H2I_DISABLE_REQ + */ + +/** + * BFI_PORT_I2H_DISABLE_RSP + */ + +/** + * BFI_PORT_H2I_GET_STATS_REQ + */ +struct bfi_port_get_stats_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + union bfi_addr_u dma_addr; +}; + +/** + * BFI_PORT_I2H_GET_STATS_RSP + */ + +/** + * BFI_PORT_H2I_CLEAR_STATS_REQ + */ + +/** + * BFI_PORT_I2H_CLEAR_STATS_RSP + */ + +union bfi_port_h2i_msg_u { + struct bfi_mhdr_s mh; + struct bfi_port_generic_req_s enable_req; + struct bfi_port_generic_req_s disable_req; + struct bfi_port_get_stats_req_s getstats_req; + struct bfi_port_generic_req_s clearstats_req; +}; + +union bfi_port_i2h_msg_u { + struct bfi_mhdr_s mh; + struct bfi_port_generic_rsp_s enable_rsp; + struct bfi_port_generic_rsp_s disable_rsp; + struct bfi_port_generic_rsp_s getstats_rsp; + struct bfi_port_generic_rsp_s clearstats_rsp; +}; + +#pragma pack() + +#endif /* __BFI_PORT_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_pport.h b/drivers/scsi/bfa/include/bfi/bfi_pport.h new file mode 100644 index 000000000000..c96d246851af --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_pport.h @@ -0,0 +1,184 @@ +/* + * 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. + */ +#ifndef __BFI_PPORT_H__ +#define __BFI_PPORT_H__ + +#include +#include + +#pragma pack(1) + +enum bfi_pport_h2i { + BFI_PPORT_H2I_ENABLE_REQ = (1), + BFI_PPORT_H2I_DISABLE_REQ = (2), + BFI_PPORT_H2I_GET_STATS_REQ = (3), + BFI_PPORT_H2I_CLEAR_STATS_REQ = (4), + BFI_PPORT_H2I_SET_SVC_PARAMS_REQ = (5), + BFI_PPORT_H2I_ENABLE_RX_VF_TAG_REQ = (6), + BFI_PPORT_H2I_ENABLE_TX_VF_TAG_REQ = (7), + BFI_PPORT_H2I_GET_QOS_STATS_REQ = (8), + BFI_PPORT_H2I_CLEAR_QOS_STATS_REQ = (9), +}; + +enum bfi_pport_i2h { + BFI_PPORT_I2H_ENABLE_RSP = BFA_I2HM(1), + BFI_PPORT_I2H_DISABLE_RSP = BFA_I2HM(2), + BFI_PPORT_I2H_GET_STATS_RSP = BFA_I2HM(3), + BFI_PPORT_I2H_CLEAR_STATS_RSP = BFA_I2HM(4), + BFI_PPORT_I2H_SET_SVC_PARAMS_RSP = BFA_I2HM(5), + BFI_PPORT_I2H_ENABLE_RX_VF_TAG_RSP = BFA_I2HM(6), + BFI_PPORT_I2H_ENABLE_TX_VF_TAG_RSP = BFA_I2HM(7), + BFI_PPORT_I2H_EVENT = BFA_I2HM(8), + BFI_PPORT_I2H_GET_QOS_STATS_RSP = BFA_I2HM(9), + BFI_PPORT_I2H_CLEAR_QOS_STATS_RSP = BFA_I2HM(10), +}; + +/** + * Generic REQ type + */ +struct bfi_pport_generic_req_s { + struct bfi_mhdr_s mh; /* msg header */ + u32 msgtag; /* msgtag for reply */ +}; + +/** + * Generic RSP type + */ +struct bfi_pport_generic_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 status; /* port enable status */ + u8 rsvd[3]; + u32 msgtag; /* msgtag for reply */ +}; + +/** + * BFI_PPORT_H2I_ENABLE_REQ + */ +struct bfi_pport_enable_req_s { + struct bfi_mhdr_s mh; /* msg header */ + u32 rsvd1; + wwn_t nwwn; /* node wwn of physical port */ + wwn_t pwwn; /* port wwn of physical port */ + struct bfa_pport_cfg_s port_cfg; /* port configuration */ + union bfi_addr_u stats_dma_addr; /* DMA address for stats */ + u32 msgtag; /* msgtag for reply */ + u32 rsvd2; +}; + +/** + * BFI_PPORT_I2H_ENABLE_RSP + */ +#define bfi_pport_enable_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_DISABLE_REQ + */ +#define bfi_pport_disable_req_t struct bfi_pport_generic_req_s + +/** + * BFI_PPORT_I2H_DISABLE_RSP + */ +#define bfi_pport_disable_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_GET_STATS_REQ + */ +#define bfi_pport_get_stats_req_t struct bfi_pport_generic_req_s + +/** + * BFI_PPORT_I2H_GET_STATS_RSP + */ +#define bfi_pport_get_stats_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_CLEAR_STATS_REQ + */ +#define bfi_pport_clear_stats_req_t struct bfi_pport_generic_req_s + +/** + * BFI_PPORT_I2H_CLEAR_STATS_RSP + */ +#define bfi_pport_clear_stats_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_GET_QOS_STATS_REQ + */ +#define bfi_pport_get_qos_stats_req_t struct bfi_pport_generic_req_s + +/** + * BFI_PPORT_H2I_GET_QOS_STATS_RSP + */ +#define bfi_pport_get_qos_stats_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_CLEAR_QOS_STATS_REQ + */ +#define bfi_pport_clear_qos_stats_req_t struct bfi_pport_generic_req_s + +/** + * BFI_PPORT_H2I_CLEAR_QOS_STATS_RSP + */ +#define bfi_pport_clear_qos_stats_rsp_t struct bfi_pport_generic_rsp_s + +/** + * BFI_PPORT_H2I_SET_SVC_PARAMS_REQ + */ +struct bfi_pport_set_svc_params_req_s { + struct bfi_mhdr_s mh; /* msg header */ + u16 tx_bbcredit; /* Tx credits */ + u16 rsvd; +}; + +/** + * BFI_PPORT_I2H_SET_SVC_PARAMS_RSP + */ + +/** + * BFI_PPORT_I2H_EVENT + */ +struct bfi_pport_event_s { + struct bfi_mhdr_s mh; /* common msg header */ + struct bfa_pport_link_s link_state; +}; + +union bfi_pport_h2i_msg_u { + struct bfi_mhdr_s *mhdr; + struct bfi_pport_enable_req_s *penable; + struct bfi_pport_generic_req_s *pdisable; + struct bfi_pport_generic_req_s *pgetstats; + struct bfi_pport_generic_req_s *pclearstats; + struct bfi_pport_set_svc_params_req_s *psetsvcparams; + struct bfi_pport_get_qos_stats_req_s *pgetqosstats; + struct bfi_pport_generic_req_s *pclearqosstats; +}; + +union bfi_pport_i2h_msg_u { + struct bfi_msg_s *msg; + struct bfi_pport_generic_rsp_s *enable_rsp; + struct bfi_pport_disable_rsp_s *disable_rsp; + struct bfi_pport_generic_rsp_s *getstats_rsp; + struct bfi_pport_clear_stats_rsp_s *clearstats_rsp; + struct bfi_pport_set_svc_params_rsp_s *setsvcparasm_rsp; + struct bfi_pport_get_qos_stats_rsp_s *getqosstats_rsp; + struct bfi_pport_clear_qos_stats_rsp_s *clearqosstats_rsp; + struct bfi_pport_event_s *event; +}; + +#pragma pack() + +#endif /* __BFI_PPORT_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_rport.h b/drivers/scsi/bfa/include/bfi/bfi_rport.h new file mode 100644 index 000000000000..3520f55f09d7 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_rport.h @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#ifndef __BFI_RPORT_H__ +#define __BFI_RPORT_H__ + +#include + +#pragma pack(1) + +enum bfi_rport_h2i_msgs { + BFI_RPORT_H2I_CREATE_REQ = 1, + BFI_RPORT_H2I_DELETE_REQ = 2, + BFI_RPORT_H2I_SET_SPEED_REQ = 3, +}; + +enum bfi_rport_i2h_msgs { + BFI_RPORT_I2H_CREATE_RSP = BFA_I2HM(1), + BFI_RPORT_I2H_DELETE_RSP = BFA_I2HM(2), + BFI_RPORT_I2H_QOS_SCN = BFA_I2HM(3), +}; + +struct bfi_rport_create_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* host rport handle */ + u16 max_frmsz; /* max rcv pdu size */ + u32 pid : 24, /* remote port ID */ + lp_tag : 8; /* local port tag */ + u32 local_pid : 24, /* local port ID */ + cisc : 8; + u8 fc_class; /* supported FC classes */ + u8 vf_en; /* virtual fabric enable */ + u16 vf_id; /* virtual fabric ID */ +}; + +struct bfi_rport_create_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u8 status; /* rport creation status */ + u8 rsvd[3]; + u16 bfa_handle; /* host rport handle */ + u16 fw_handle; /* firmware rport handle */ + struct bfa_rport_qos_attr_s qos_attr; /* QoS Attributes */ +}; + +struct bfa_rport_speed_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 fw_handle; /* firmware rport handle */ + u8 speed; /*! rport's speed via RPSC */ + u8 rsvd; +}; + +struct bfi_rport_delete_req_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 fw_handle; /* firmware rport handle */ + u16 rsvd; +}; + +struct bfi_rport_delete_rsp_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* host rport handle */ + u8 status; /* rport deletion status */ + u8 rsvd; +}; + +struct bfi_rport_qos_scn_s { + struct bfi_mhdr_s mh; /* common msg header */ + u16 bfa_handle; /* host rport handle */ + u16 rsvd; + struct bfa_rport_qos_attr_s old_qos_attr; /* Old QoS Attributes */ + struct bfa_rport_qos_attr_s new_qos_attr; /* New QoS Attributes */ +}; + +union bfi_rport_h2i_msg_u { + struct bfi_msg_s *msg; + struct bfi_rport_create_req_s *create_req; + struct bfi_rport_delete_req_s *delete_req; + struct bfi_rport_speed_req_s *speed_req; +}; + +union bfi_rport_i2h_msg_u { + struct bfi_msg_s *msg; + struct bfi_rport_create_rsp_s *create_rsp; + struct bfi_rport_delete_rsp_s *delete_rsp; + struct bfi_rport_qos_scn_s *qos_scn_evt; +}; + +#pragma pack() + +#endif /* __BFI_RPORT_H__ */ + diff --git a/drivers/scsi/bfa/include/bfi/bfi_uf.h b/drivers/scsi/bfa/include/bfi/bfi_uf.h new file mode 100644 index 000000000000..f328a9e7e622 --- /dev/null +++ b/drivers/scsi/bfa/include/bfi/bfi_uf.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#ifndef __BFI_UF_H__ +#define __BFI_UF_H__ + +#include "bfi.h" + +#pragma pack(1) + +enum bfi_uf_h2i { + BFI_UF_H2I_BUF_POST = 1, +}; + +enum bfi_uf_i2h { + BFI_UF_I2H_FRM_RCVD = BFA_I2HM(1), +}; + +#define BFA_UF_MAX_SGES 2 + +struct bfi_uf_buf_post_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 buf_tag; /* buffer tag */ + u16 buf_len; /* total buffer length */ + struct bfi_sge_s sge[BFA_UF_MAX_SGES]; /* buffer DMA SGEs */ +}; + +struct bfi_uf_frm_rcvd_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u16 buf_tag; /* buffer tag */ + u16 rsvd; + u16 frm_len; /* received frame length */ + u16 xfr_len; /* tranferred length */ +}; + +#pragma pack() + +#endif /* __BFI_UF_H__ */ diff --git a/drivers/scsi/bfa/include/cna/bfa_cna_trcmod.h b/drivers/scsi/bfa/include/cna/bfa_cna_trcmod.h new file mode 100644 index 000000000000..43ba7064e81a --- /dev/null +++ b/drivers/scsi/bfa/include/cna/bfa_cna_trcmod.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +/** + * bfa_cna_trcmod.h CNA trace modules + */ + +#ifndef __BFA_CNA_TRCMOD_H__ +#define __BFA_CNA_TRCMOD_H__ + +#include + +/* + * !!! Only append to the enums defined here to avoid any versioning + * !!! needed between trace utility and driver version + */ +enum { + BFA_TRC_CNA_CEE = 1, + BFA_TRC_CNA_PORT = 2, +}; + +#endif /* __BFA_CNA_TRCMOD_H__ */ diff --git a/drivers/scsi/bfa/include/cna/cee/bfa_cee.h b/drivers/scsi/bfa/include/cna/cee/bfa_cee.h new file mode 100644 index 000000000000..77f297f68046 --- /dev/null +++ b/drivers/scsi/bfa/include/cna/cee/bfa_cee.h @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#ifndef __BFA_CEE_H__ +#define __BFA_CEE_H__ + +#include +#include +#include +#include + +typedef void (*bfa_cee_get_attr_cbfn_t) (void *dev, bfa_status_t status); +typedef void (*bfa_cee_get_stats_cbfn_t) (void *dev, bfa_status_t status); +typedef void (*bfa_cee_reset_stats_cbfn_t) (void *dev, bfa_status_t status); +typedef void (*bfa_cee_hbfail_cbfn_t) (void *dev, bfa_status_t status); + +struct bfa_cee_cbfn_s { + bfa_cee_get_attr_cbfn_t get_attr_cbfn; + void *get_attr_cbarg; + bfa_cee_get_stats_cbfn_t get_stats_cbfn; + void *get_stats_cbarg; + bfa_cee_reset_stats_cbfn_t reset_stats_cbfn; + void *reset_stats_cbarg; +}; + +struct bfa_cee_s { + void *dev; + bfa_boolean_t get_attr_pending; + bfa_boolean_t get_stats_pending; + bfa_boolean_t reset_stats_pending; + bfa_status_t get_attr_status; + bfa_status_t get_stats_status; + bfa_status_t reset_stats_status; + struct bfa_cee_cbfn_s cbfn; + struct bfa_ioc_hbfail_notify_s hbfail; + struct bfa_trc_mod_s *trcmod; + struct bfa_log_mod_s *logmod; + struct bfa_cee_attr_s *attr; + struct bfa_cee_stats_s *stats; + struct bfa_dma_s attr_dma; + struct bfa_dma_s stats_dma; + struct bfa_ioc_s *ioc; + struct bfa_mbox_cmd_s get_cfg_mb; + struct bfa_mbox_cmd_s get_stats_mb; + struct bfa_mbox_cmd_s reset_stats_mb; +}; + +u32 bfa_cee_meminfo(void); +void bfa_cee_mem_claim(struct bfa_cee_s *cee, u8 *dma_kva, + u64 dma_pa); +void bfa_cee_attach(struct bfa_cee_s *cee, struct bfa_ioc_s *ioc, void *dev, + struct bfa_trc_mod_s *trcmod, + struct bfa_log_mod_s *logmod); +void bfa_cee_detach(struct bfa_cee_s *cee); +bfa_status_t bfa_cee_get_attr(struct bfa_cee_s *cee, + struct bfa_cee_attr_s *attr, + bfa_cee_get_attr_cbfn_t cbfn, void *cbarg); +bfa_status_t bfa_cee_get_stats(struct bfa_cee_s *cee, + struct bfa_cee_stats_s *stats, + bfa_cee_get_stats_cbfn_t cbfn, void *cbarg); +bfa_status_t bfa_cee_reset_stats(struct bfa_cee_s *cee, + bfa_cee_reset_stats_cbfn_t cbfn, void *cbarg); +#endif /* __BFA_CEE_H__ */ diff --git a/drivers/scsi/bfa/include/cna/port/bfa_port.h b/drivers/scsi/bfa/include/cna/port/bfa_port.h new file mode 100644 index 000000000000..7cbf17d3141b --- /dev/null +++ b/drivers/scsi/bfa/include/cna/port/bfa_port.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#ifndef __BFA_PORT_H__ +#define __BFA_PORT_H__ + +#include +#include +#include +#include + +typedef void (*bfa_port_stats_cbfn_t) (void *dev, bfa_status_t status); +typedef void (*bfa_port_endis_cbfn_t) (void *dev, bfa_status_t status); + +struct bfa_port_s { + void *dev; + struct bfa_ioc_s *ioc; + struct bfa_trc_mod_s *trcmod; + struct bfa_log_mod_s *logmod; + u32 msgtag; + bfa_boolean_t stats_busy; + struct bfa_mbox_cmd_s stats_mb; + bfa_port_stats_cbfn_t stats_cbfn; + void *stats_cbarg; + bfa_status_t stats_status; + union bfa_pport_stats_u *stats; + struct bfa_dma_s stats_dma; + bfa_boolean_t endis_pending; + struct bfa_mbox_cmd_s endis_mb; + bfa_port_endis_cbfn_t endis_cbfn; + void *endis_cbarg; + bfa_status_t endis_status; + struct bfa_ioc_hbfail_notify_s hbfail; +}; + +void bfa_port_attach(struct bfa_port_s *port, struct bfa_ioc_s *ioc, + void *dev, struct bfa_trc_mod_s *trcmod, + struct bfa_log_mod_s *logmod); +void bfa_port_detach(struct bfa_port_s *port); +void bfa_port_hbfail(void *arg); + +bfa_status_t bfa_port_get_stats(struct bfa_port_s *port, + union bfa_pport_stats_u *stats, + bfa_port_stats_cbfn_t cbfn, void *cbarg); +bfa_status_t bfa_port_clear_stats(struct bfa_port_s *port, + bfa_port_stats_cbfn_t cbfn, void *cbarg); +bfa_status_t bfa_port_enable(struct bfa_port_s *port, + bfa_port_endis_cbfn_t cbfn, void *cbarg); +bfa_status_t bfa_port_disable(struct bfa_port_s *port, + bfa_port_endis_cbfn_t cbfn, void *cbarg); +u32 bfa_port_meminfo(void); +void bfa_port_mem_claim(struct bfa_port_s *port, u8 *dma_kva, + u64 dma_pa); + +#endif /* __BFA_PORT_H__ */ diff --git a/drivers/scsi/bfa/include/cna/pstats/ethport_defs.h b/drivers/scsi/bfa/include/cna/pstats/ethport_defs.h new file mode 100644 index 000000000000..1563ee512218 --- /dev/null +++ b/drivers/scsi/bfa/include/cna/pstats/ethport_defs.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved. + * + * 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. + */ + +#ifndef __ETHPORT_DEFS_H__ +#define __ETHPORT_DEFS_H__ + +struct bnad_drv_stats { + u64 netif_queue_stop; + u64 netif_queue_wakeup; + u64 tso4; + u64 tso6; + u64 tso_err; + u64 tcpcsum_offload; + u64 udpcsum_offload; + u64 csum_help; + u64 csum_help_err; + + u64 hw_stats_updates; + u64 netif_rx_schedule; + u64 netif_rx_complete; + u64 netif_rx_dropped; +}; +#endif diff --git a/drivers/scsi/bfa/include/cna/pstats/phyport_defs.h b/drivers/scsi/bfa/include/cna/pstats/phyport_defs.h new file mode 100644 index 000000000000..eb7548030d0f --- /dev/null +++ b/drivers/scsi/bfa/include/cna/pstats/phyport_defs.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved. + * + * 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. + */ + +#ifndef __PHYPORT_DEFS_H__ +#define __PHYPORT_DEFS_H__ + +#define BNA_TXF_ID_MAX 64 +#define BNA_RXF_ID_MAX 64 + +/* + * Statistics + */ + +/* + * TxF Frame Statistics + */ +struct bna_stats_txf { + u64 ucast_octets; + u64 ucast; + u64 ucast_vlan; + + u64 mcast_octets; + u64 mcast; + u64 mcast_vlan; + + u64 bcast_octets; + u64 bcast; + u64 bcast_vlan; + + u64 errors; + u64 filter_vlan; /* frames filtered due to VLAN */ + u64 filter_mac_sa; /* frames filtered due to SA check */ +}; + +/* + * RxF Frame Statistics + */ +struct bna_stats_rxf { + u64 ucast_octets; + u64 ucast; + u64 ucast_vlan; + + u64 mcast_octets; + u64 mcast; + u64 mcast_vlan; + + u64 bcast_octets; + u64 bcast; + u64 bcast_vlan; + u64 frame_drops; +}; + +/* + * FC Tx Frame Statistics + */ +struct bna_stats_fc_tx { + u64 txf_ucast_octets; + u64 txf_ucast; + u64 txf_ucast_vlan; + + u64 txf_mcast_octets; + u64 txf_mcast; + u64 txf_mcast_vlan; + + u64 txf_bcast_octets; + u64 txf_bcast; + u64 txf_bcast_vlan; + + u64 txf_parity_errors; + u64 txf_timeout; + u64 txf_fid_parity_errors; +}; + +/* + * FC Rx Frame Statistics + */ +struct bna_stats_fc_rx { + u64 rxf_ucast_octets; + u64 rxf_ucast; + u64 rxf_ucast_vlan; + + u64 rxf_mcast_octets; + u64 rxf_mcast; + u64 rxf_mcast_vlan; + + u64 rxf_bcast_octets; + u64 rxf_bcast; + u64 rxf_bcast_vlan; +}; + +/* + * RAD Frame Statistics + */ +struct cna_stats_rad { + u64 rx_frames; + u64 rx_octets; + u64 rx_vlan_frames; + + u64 rx_ucast; + u64 rx_ucast_octets; + u64 rx_ucast_vlan; + + u64 rx_mcast; + u64 rx_mcast_octets; + u64 rx_mcast_vlan; + + u64 rx_bcast; + u64 rx_bcast_octets; + u64 rx_bcast_vlan; + + u64 rx_drops; +}; + +/* + * BPC Tx Registers + */ +struct cna_stats_bpc_tx { + u64 tx_pause[8]; + u64 tx_zero_pause[8]; /* Pause cancellation */ + u64 tx_first_pause[8]; /* Pause initiation rather + *than retention */ +}; + +/* + * BPC Rx Registers + */ +struct cna_stats_bpc_rx { + u64 rx_pause[8]; + u64 rx_zero_pause[8]; /* Pause cancellation */ + u64 rx_first_pause[8]; /* Pause initiation rather + *than retention */ +}; + +/* + * MAC Rx Statistics + */ +struct cna_stats_mac_rx { + u64 frame_64; /* both rx and tx counter */ + u64 frame_65_127; /* both rx and tx counter */ + u64 frame_128_255; /* both rx and tx counter */ + u64 frame_256_511; /* both rx and tx counter */ + u64 frame_512_1023; /* both rx and tx counter */ + u64 frame_1024_1518; /* both rx and tx counter */ + u64 frame_1518_1522; /* both rx and tx counter */ + u64 rx_bytes; + u64 rx_packets; + u64 rx_fcs_error; + u64 rx_multicast; + u64 rx_broadcast; + u64 rx_control_frames; + u64 rx_pause; + u64 rx_unknown_opcode; + u64 rx_alignment_error; + u64 rx_frame_length_error; + u64 rx_code_error; + u64 rx_carrier_sense_error; + u64 rx_undersize; + u64 rx_oversize; + u64 rx_fragments; + u64 rx_jabber; + u64 rx_drop; +}; + +/* + * MAC Tx Statistics + */ +struct cna_stats_mac_tx { + u64 tx_bytes; + u64 tx_packets; + u64 tx_multicast; + u64 tx_broadcast; + u64 tx_pause; + u64 tx_deferral; + u64 tx_excessive_deferral; + u64 tx_single_collision; + u64 tx_muliple_collision; + u64 tx_late_collision; + u64 tx_excessive_collision; + u64 tx_total_collision; + u64 tx_pause_honored; + u64 tx_drop; + u64 tx_jabber; + u64 tx_fcs_error; + u64 tx_control_frame; + u64 tx_oversize; + u64 tx_undersize; + u64 tx_fragments; +}; + +/* + * Complete statistics + */ +struct bna_stats { + struct cna_stats_mac_rx mac_rx_stats; + struct cna_stats_bpc_rx bpc_rx_stats; + struct cna_stats_rad rad_stats; + struct bna_stats_fc_rx fc_rx_stats; + struct cna_stats_mac_tx mac_tx_stats; + struct cna_stats_bpc_tx bpc_tx_stats; + struct bna_stats_fc_tx fc_tx_stats; + struct bna_stats_rxf rxf_stats[BNA_TXF_ID_MAX]; + struct bna_stats_txf txf_stats[BNA_RXF_ID_MAX]; +}; + +#endif diff --git a/drivers/scsi/bfa/include/cs/bfa_checksum.h b/drivers/scsi/bfa/include/cs/bfa_checksum.h new file mode 100644 index 000000000000..af8c1d533ba8 --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_checksum.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +/** + * bfa_checksum.h BFA checksum utilities + */ + +#ifndef __BFA_CHECKSUM_H__ +#define __BFA_CHECKSUM_H__ + +static inline u32 +bfa_checksum_u32(u32 *buf, int sz) +{ + int i, m = sz >> 2; + u32 sum = 0; + + for (i = 0; i < m; i++) + sum ^= buf[i]; + + return (sum); +} + +static inline u16 +bfa_checksum_u16(u16 *buf, int sz) +{ + int i, m = sz >> 1; + u16 sum = 0; + + for (i = 0; i < m; i++) + sum ^= buf[i]; + + return (sum); +} + +static inline u8 +bfa_checksum_u8(u8 *buf, int sz) +{ + int i; + u8 sum = 0; + + for (i = 0; i < sz; i++) + sum ^= buf[i]; + + return (sum); +} +#endif diff --git a/drivers/scsi/bfa/include/cs/bfa_debug.h b/drivers/scsi/bfa/include/cs/bfa_debug.h new file mode 100644 index 000000000000..441be86b1b0f --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_debug.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +/** + * bfa_debug.h BFA debug interfaces + */ + +#ifndef __BFA_DEBUG_H__ +#define __BFA_DEBUG_H__ + +#define bfa_assert(__cond) do { \ + if (!(__cond)) \ + bfa_panic(__LINE__, __FILE__, #__cond); \ +} while (0) + +#define bfa_sm_fault(__mod, __event) do { \ + bfa_sm_panic((__mod)->logm, __LINE__, __FILE__, __event); \ +} while (0) + +#ifndef BFA_PERF_BUILD +#define bfa_assert_fp(__cond) bfa_assert(__cond) +#else +#define bfa_assert_fp(__cond) +#endif + +struct bfa_log_mod_s; +void bfa_panic(int line, char *file, char *panicstr); +void bfa_sm_panic(struct bfa_log_mod_s *logm, int line, char *file, int event); + +#endif /* __BFA_DEBUG_H__ */ diff --git a/drivers/scsi/bfa/include/cs/bfa_log.h b/drivers/scsi/bfa/include/cs/bfa_log.h new file mode 100644 index 000000000000..761cbe22130a --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_log.h @@ -0,0 +1,184 @@ +/* + * 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. + */ + +/** + * bfa_log.h BFA log library data structure and function definition + */ + +#ifndef __BFA_LOG_H__ +#define __BFA_LOG_H__ + +#include +#include +#include + +/* + * BFA log module definition + * + * To create a new module id: + * Add a #define at the end of the list below. Select a value for your + * definition so that it is one (1) greater than the previous + * definition. Modify the definition of BFA_LOG_MODULE_ID_MAX to become + * your new definition. + * Should have no gaps in between the values because this is used in arrays. + * IMPORTANT: AEN_IDs must be at the begining, otherwise update bfa_defs_aen.h + */ + +enum bfa_log_module_id { + BFA_LOG_UNUSED_ID = 0, + + /* AEN defs begin */ + BFA_LOG_AEN_MIN = BFA_LOG_UNUSED_ID, + + BFA_LOG_AEN_ID_ADAPTER = BFA_LOG_AEN_MIN + BFA_AEN_CAT_ADAPTER,/* 1 */ + BFA_LOG_AEN_ID_PORT = BFA_LOG_AEN_MIN + BFA_AEN_CAT_PORT, /* 2 */ + BFA_LOG_AEN_ID_LPORT = BFA_LOG_AEN_MIN + BFA_AEN_CAT_LPORT, /* 3 */ + BFA_LOG_AEN_ID_RPORT = BFA_LOG_AEN_MIN + BFA_AEN_CAT_RPORT, /* 4 */ + BFA_LOG_AEN_ID_ITNIM = BFA_LOG_AEN_MIN + BFA_AEN_CAT_ITNIM, /* 5 */ + BFA_LOG_AEN_ID_TIN = BFA_LOG_AEN_MIN + BFA_AEN_CAT_TIN, /* 6 */ + BFA_LOG_AEN_ID_IPFC = BFA_LOG_AEN_MIN + BFA_AEN_CAT_IPFC, /* 7 */ + BFA_LOG_AEN_ID_AUDIT = BFA_LOG_AEN_MIN + BFA_AEN_CAT_AUDIT, /* 8 */ + BFA_LOG_AEN_ID_IOC = BFA_LOG_AEN_MIN + BFA_AEN_CAT_IOC, /* 9 */ + BFA_LOG_AEN_ID_ETHPORT = BFA_LOG_AEN_MIN + BFA_AEN_CAT_ETHPORT,/* 10 */ + + BFA_LOG_AEN_MAX = BFA_LOG_AEN_ID_ETHPORT, + /* AEN defs end */ + + BFA_LOG_MODULE_ID_MIN = BFA_LOG_AEN_MAX, + + BFA_LOG_FW_ID = BFA_LOG_MODULE_ID_MIN + 1, + BFA_LOG_HAL_ID = BFA_LOG_MODULE_ID_MIN + 2, + BFA_LOG_FCS_ID = BFA_LOG_MODULE_ID_MIN + 3, + BFA_LOG_WDRV_ID = BFA_LOG_MODULE_ID_MIN + 4, + BFA_LOG_LINUX_ID = BFA_LOG_MODULE_ID_MIN + 5, + BFA_LOG_SOLARIS_ID = BFA_LOG_MODULE_ID_MIN + 6, + + BFA_LOG_MODULE_ID_MAX = BFA_LOG_SOLARIS_ID, + + /* Not part of any arrays */ + BFA_LOG_MODULE_ID_ALL = BFA_LOG_MODULE_ID_MAX + 1, + BFA_LOG_AEN_ALL = BFA_LOG_MODULE_ID_MAX + 2, + BFA_LOG_DRV_ALL = BFA_LOG_MODULE_ID_MAX + 3, +}; + +/* + * BFA log catalog name + */ +#define BFA_LOG_CAT_NAME "BFA" + +/* + * bfa log severity values + */ +enum bfa_log_severity { + BFA_LOG_INVALID = 0, + BFA_LOG_CRITICAL = 1, + BFA_LOG_ERROR = 2, + BFA_LOG_WARNING = 3, + BFA_LOG_INFO = 4, + BFA_LOG_NONE = 5, + BFA_LOG_LEVEL_MAX = BFA_LOG_NONE +}; + +#define BFA_LOG_MODID_OFFSET 16 + + +struct bfa_log_msgdef_s { + u32 msg_id; /* message id */ + int attributes; /* attributes */ + int severity; /* severity level */ + char *msg_value; + /* msg string */ + char *message; + /* msg format string */ + int arg_type; /* argument type */ + int arg_num; /* number of argument */ +}; + +/* + * supported argument type + */ +enum bfa_log_arg_type { + BFA_LOG_S = 0, /* string */ + BFA_LOG_D, /* decimal */ + BFA_LOG_I, /* integer */ + BFA_LOG_O, /* oct number */ + BFA_LOG_U, /* unsigned integer */ + BFA_LOG_X, /* hex number */ + BFA_LOG_F, /* floating */ + BFA_LOG_C, /* character */ + BFA_LOG_L, /* double */ + BFA_LOG_P /* pointer */ +}; + +#define BFA_LOG_ARG_TYPE 2 +#define BFA_LOG_ARG0 (0 * BFA_LOG_ARG_TYPE) +#define BFA_LOG_ARG1 (1 * BFA_LOG_ARG_TYPE) +#define BFA_LOG_ARG2 (2 * BFA_LOG_ARG_TYPE) +#define BFA_LOG_ARG3 (3 * BFA_LOG_ARG_TYPE) + +#define BFA_LOG_GET_MOD_ID(msgid) ((msgid >> BFA_LOG_MODID_OFFSET) & 0xff) +#define BFA_LOG_GET_MSG_IDX(msgid) (msgid & 0xffff) +#define BFA_LOG_GET_MSG_ID(msgdef) ((msgdef)->msg_id) +#define BFA_LOG_GET_MSG_FMT_STRING(msgdef) ((msgdef)->message) +#define BFA_LOG_GET_SEVERITY(msgdef) ((msgdef)->severity) + +/* + * Event attributes + */ +#define BFA_LOG_ATTR_NONE 0 +#define BFA_LOG_ATTR_AUDIT 1 +#define BFA_LOG_ATTR_LOG 2 +#define BFA_LOG_ATTR_FFDC 4 + +#define BFA_LOG_CREATE_ID(msw, lsw) \ + (((u32)msw << BFA_LOG_MODID_OFFSET) | lsw) + +struct bfa_log_mod_s; + +/** + * callback function + */ +typedef void (*bfa_log_cb_t)(struct bfa_log_mod_s *log_mod, u32 msg_id, + const char *format, ...); + + +struct bfa_log_mod_s { + char instance_info[16]; /* instance info */ + int log_level[BFA_LOG_MODULE_ID_MAX + 1]; + /* log level for modules */ + bfa_log_cb_t cbfn; /* callback function */ +}; + +extern int bfa_log_init(struct bfa_log_mod_s *log_mod, + char *instance_name, bfa_log_cb_t cbfn); +extern int bfa_log(struct bfa_log_mod_s *log_mod, u32 msg_id, ...); +extern bfa_status_t bfa_log_set_level(struct bfa_log_mod_s *log_mod, + int mod_id, enum bfa_log_severity log_level); +extern bfa_status_t bfa_log_set_level_all(struct bfa_log_mod_s *log_mod, + enum bfa_log_severity log_level); +extern bfa_status_t bfa_log_set_level_aen(struct bfa_log_mod_s *log_mod, + enum bfa_log_severity log_level); +extern enum bfa_log_severity bfa_log_get_level(struct bfa_log_mod_s *log_mod, + int mod_id); +extern enum bfa_log_severity bfa_log_get_msg_level( + struct bfa_log_mod_s *log_mod, u32 msg_id); +/* + * array of messages generated from xml files + */ +extern struct bfa_log_msgdef_s bfa_log_msg_array[]; + +#endif diff --git a/drivers/scsi/bfa/include/cs/bfa_perf.h b/drivers/scsi/bfa/include/cs/bfa_perf.h new file mode 100644 index 000000000000..45aa5f978ff5 --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_perf.h @@ -0,0 +1,34 @@ +/* + * 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. + */ +#ifndef __BFAD_PERF_H__ +#define __BFAD_PERF_H__ + +#ifdef BFAD_PERF_BUILD + +#undef bfa_trc +#undef bfa_trc32 +#undef bfa_assert +#undef BFA_TRC_FILE + +#define bfa_trc(_trcp, _data) +#define bfa_trc32(_trcp, _data) +#define bfa_assert(__cond) +#define BFA_TRC_FILE(__mod, __submod) + +#endif + +#endif /* __BFAD_PERF_H__ */ diff --git a/drivers/scsi/bfa/include/cs/bfa_plog.h b/drivers/scsi/bfa/include/cs/bfa_plog.h new file mode 100644 index 000000000000..670f86e5fc6e --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_plog.h @@ -0,0 +1,162 @@ +/* + * 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. + */ +#ifndef __BFA_PORTLOG_H__ +#define __BFA_PORTLOG_H__ + +#include "protocol/fc.h" +#include + +#define BFA_PL_NLOG_ENTS 256 +#define BFA_PL_LOG_REC_INCR(_x) ((_x)++, (_x) %= BFA_PL_NLOG_ENTS) + +#define BFA_PL_STRING_LOG_SZ 32 /* number of chars in string log */ +#define BFA_PL_INT_LOG_SZ 8 /* number of integers in the integer log */ + +enum bfa_plog_log_type { + BFA_PL_LOG_TYPE_INVALID = 0, + BFA_PL_LOG_TYPE_INT = 1, + BFA_PL_LOG_TYPE_STRING = 2, +}; + +/* + * the (fixed size) record format for each entry in the portlog + */ +struct bfa_plog_rec_s { + u32 tv; /* Filled by the portlog driver when the * + * entry is added to the circular log. */ + u8 port; /* Source port that logged this entry. CM + * entities will use 0xFF */ + u8 mid; /* Integer value to be used by all entities * + * while logging. The module id to string * + * conversion will be done by BFAL. See + * enum bfa_plog_mid */ + u8 eid; /* indicates Rx, Tx, IOCTL, etc. See + * enum bfa_plog_eid */ + u8 log_type; /* indicates string log or integer log. + * see bfa_plog_log_type_t */ + u8 log_num_ints; + /* + * interpreted only if log_type is INT_LOG. indicates number of + * integers in the int_log[] (0-PL_INT_LOG_SZ). + */ + u8 rsvd; + u16 misc; /* can be used to indicate fc frame length, + *etc.. */ + union { + char string_log[BFA_PL_STRING_LOG_SZ]; + u32 int_log[BFA_PL_INT_LOG_SZ]; + } log_entry; + +}; + +/* + * the following #defines will be used by the logging entities to indicate + * their module id. BFAL will convert the integer value to string format + * +* process to be used while changing the following #defines: + * - Always add new entries at the end + * - define corresponding string in BFAL + * - Do not remove any entry or rearrange the order. + */ +enum bfa_plog_mid { + BFA_PL_MID_INVALID = 0, + BFA_PL_MID_DEBUG = 1, + BFA_PL_MID_DRVR = 2, + BFA_PL_MID_HAL = 3, + BFA_PL_MID_HAL_FCXP = 4, + BFA_PL_MID_HAL_UF = 5, + BFA_PL_MID_FCS = 6, + BFA_PL_MID_MAX = 7 +}; + +#define BFA_PL_MID_STRLEN 8 +struct bfa_plog_mid_strings_s { + char m_str[BFA_PL_MID_STRLEN]; +}; + +/* + * the following #defines will be used by the logging entities to indicate + * their event type. BFAL will convert the integer value to string format + * +* process to be used while changing the following #defines: + * - Always add new entries at the end + * - define corresponding string in BFAL + * - Do not remove any entry or rearrange the order. + */ +enum bfa_plog_eid { + BFA_PL_EID_INVALID = 0, + BFA_PL_EID_IOC_DISABLE = 1, + BFA_PL_EID_IOC_ENABLE = 2, + BFA_PL_EID_PORT_DISABLE = 3, + BFA_PL_EID_PORT_ENABLE = 4, + BFA_PL_EID_PORT_ST_CHANGE = 5, + BFA_PL_EID_TX = 6, + BFA_PL_EID_TX_ACK1 = 7, + BFA_PL_EID_TX_RJT = 8, + BFA_PL_EID_TX_BSY = 9, + BFA_PL_EID_RX = 10, + BFA_PL_EID_RX_ACK1 = 11, + BFA_PL_EID_RX_RJT = 12, + BFA_PL_EID_RX_BSY = 13, + BFA_PL_EID_CT_IN = 14, + BFA_PL_EID_CT_OUT = 15, + BFA_PL_EID_DRIVER_START = 16, + BFA_PL_EID_RSCN = 17, + BFA_PL_EID_DEBUG = 18, + BFA_PL_EID_MISC = 19, + BFA_PL_EID_MAX = 20 +}; + +#define BFA_PL_ENAME_STRLEN 8 +struct bfa_plog_eid_strings_s { + char e_str[BFA_PL_ENAME_STRLEN]; +}; + +#define BFA_PL_SIG_LEN 8 +#define BFA_PL_SIG_STR "12pl123" + +/* + * per port circular log buffer + */ +struct bfa_plog_s { + char plog_sig[BFA_PL_SIG_LEN]; /* Start signature */ + u8 plog_enabled; + u8 rsvd[7]; + u32 ticks; + u16 head; + u16 tail; + struct bfa_plog_rec_s plog_recs[BFA_PL_NLOG_ENTS]; +}; + +void bfa_plog_init(struct bfa_plog_s *plog); +void bfa_plog_str(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, u16 misc, char *log_str); +void bfa_plog_intarr(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, u16 misc, + u32 *intarr, u32 num_ints); +void bfa_plog_fchdr(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, u16 misc, + struct fchs_s *fchdr); +void bfa_plog_fchdr_and_pl(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, u16 misc, + struct fchs_s *fchdr, u32 pld_w0); +void bfa_plog_clear(struct bfa_plog_s *plog); +void bfa_plog_enable(struct bfa_plog_s *plog); +void bfa_plog_disable(struct bfa_plog_s *plog); +bfa_boolean_t bfa_plog_get_setting(struct bfa_plog_s *plog); + +#endif /* __BFA_PORTLOG_H__ */ diff --git a/drivers/scsi/bfa/include/cs/bfa_q.h b/drivers/scsi/bfa/include/cs/bfa_q.h new file mode 100644 index 000000000000..ea895facedbc --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_q.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +/** + * bfa_q.h Circular queue definitions. + */ + +#ifndef __BFA_Q_H__ +#define __BFA_Q_H__ + +#define bfa_q_first(_q) ((void *)(((struct list_head *) (_q))->next)) +#define bfa_q_next(_qe) (((struct list_head *) (_qe))->next) +#define bfa_q_prev(_qe) (((struct list_head *) (_qe))->prev) + +/* + * bfa_q_qe_init - to initialize a queue element + */ +#define bfa_q_qe_init(_qe) { \ + bfa_q_next(_qe) = (struct list_head *) NULL; \ + bfa_q_prev(_qe) = (struct list_head *) NULL; \ +} + +/* + * bfa_q_deq - dequeue an element from head of the queue + */ +#define bfa_q_deq(_q, _qe) { \ + if (!list_empty(_q)) { \ + (*((struct list_head **) (_qe))) = bfa_q_next(_q); \ + bfa_q_prev(bfa_q_next(*((struct list_head **) _qe))) = \ + (struct list_head *) (_q); \ + bfa_q_next(_q) = bfa_q_next(*((struct list_head **) _qe)); \ + BFA_Q_DBG_INIT(*((struct list_head **) _qe)); \ + } else { \ + *((struct list_head **) (_qe)) = (struct list_head *) NULL; \ + } \ +} + +/* + * bfa_q_deq_tail - dequeue an element from tail of the queue + */ +#define bfa_q_deq_tail(_q, _qe) { \ + if (!list_empty(_q)) { \ + *((struct list_head **) (_qe)) = bfa_q_prev(_q); \ + bfa_q_next(bfa_q_prev(*((struct list_head **) _qe))) = \ + (struct list_head *) (_q); \ + bfa_q_prev(_q) = bfa_q_prev(*(struct list_head **) _qe); \ + BFA_Q_DBG_INIT(*((struct list_head **) _qe)); \ + } else { \ + *((struct list_head **) (_qe)) = (struct list_head *) NULL; \ + } \ +} + +/* + * #ifdef BFA_DEBUG (Using bfa_assert to check for debug_build is not + * consistent across modules) + */ +#ifndef BFA_PERF_BUILD +#define BFA_Q_DBG_INIT(_qe) bfa_q_qe_init(_qe) +#else +#define BFA_Q_DBG_INIT(_qe) +#endif + +#define bfa_q_is_on_q(_q, _qe) \ + bfa_q_is_on_q_func(_q, (struct list_head *)(_qe)) +extern int bfa_q_is_on_q_func(struct list_head *q, struct list_head *qe); + +#endif diff --git a/drivers/scsi/bfa/include/cs/bfa_sm.h b/drivers/scsi/bfa/include/cs/bfa_sm.h new file mode 100644 index 000000000000..9877066680a6 --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_sm.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +/** + * bfasm.h State machine defines + */ + +#ifndef __BFA_SM_H__ +#define __BFA_SM_H__ + +typedef void (*bfa_sm_t)(void *sm, int event); + +#define bfa_sm_set_state(_sm, _state) (_sm)->sm = (bfa_sm_t)(_state) +#define bfa_sm_send_event(_sm, _event) (_sm)->sm((_sm), (_event)) +#define bfa_sm_get_state(_sm) ((_sm)->sm) +#define bfa_sm_cmp_state(_sm, _state) ((_sm)->sm == (bfa_sm_t)(_state)) + +/** + * For converting from state machine function to state encoding. + */ +struct bfa_sm_table_s { + bfa_sm_t sm; /* state machine function */ + int state; /* state machine encoding */ + char *name; /* state name for display */ +}; +#define BFA_SM(_sm) ((bfa_sm_t)(_sm)) + +int bfa_sm_to_state(struct bfa_sm_table_s *smt, bfa_sm_t sm); + +/** + * State machine with entry actions. + */ +typedef void (*bfa_fsm_t)(void *fsm, int event); + +/** + * oc - object class eg. bfa_ioc + * st - state, eg. reset + * otype - object type, eg. struct bfa_ioc_s + * etype - object type, eg. enum ioc_event + */ +#define bfa_fsm_state_decl(oc, st, otype, etype) \ + static void oc ## _sm_ ## st(otype * fsm, etype event); \ + static void oc ## _sm_ ## st ## _entry(otype * fsm) + +#define bfa_fsm_set_state(_fsm, _state) do { \ + (_fsm)->fsm = (bfa_fsm_t)(_state); \ + _state ## _entry(_fsm); \ +} while (0) + +#define bfa_fsm_send_event(_fsm, _event) \ + (_fsm)->fsm((_fsm), (_event)) +#define bfa_fsm_cmp_state(_fsm, _state) \ + ((_fsm)->fsm == (bfa_fsm_t)(_state)) + +#endif diff --git a/drivers/scsi/bfa/include/cs/bfa_trc.h b/drivers/scsi/bfa/include/cs/bfa_trc.h new file mode 100644 index 000000000000..3e743928c74c --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_trc.h @@ -0,0 +1,176 @@ +/* + * 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. + */ +#ifndef __BFA_TRC_H__ +#define __BFA_TRC_H__ + +#include + +#ifndef BFA_TRC_MAX +#define BFA_TRC_MAX (4 * 1024) +#endif + +#ifndef BFA_TRC_TS +#define BFA_TRC_TS(_trcm) ((_trcm)->ticks ++) +#endif + +struct bfa_trc_s { +#ifdef __BIGENDIAN + u16 fileno; + u16 line; +#else + u16 line; + u16 fileno; +#endif + u32 timestamp; + union { + struct { + u32 rsvd; + u32 u32; + } u32; + u64 u64; + } data; +}; + + +struct bfa_trc_mod_s { + u32 head; + u32 tail; + u32 ntrc; + u32 stopped; + u32 ticks; + u32 rsvd[3]; + struct bfa_trc_s trc[BFA_TRC_MAX]; +}; + + +enum { + BFA_TRC_FW = 1, /* firmware modules */ + BFA_TRC_HAL = 2, /* BFA modules */ + BFA_TRC_FCS = 3, /* BFA FCS modules */ + BFA_TRC_LDRV = 4, /* Linux driver modules */ + BFA_TRC_SDRV = 5, /* Solaris driver modules */ + BFA_TRC_VDRV = 6, /* vmware driver modules */ + BFA_TRC_WDRV = 7, /* windows driver modules */ + BFA_TRC_AEN = 8, /* AEN module */ + BFA_TRC_BIOS = 9, /* bios driver modules */ + BFA_TRC_EFI = 10, /* EFI driver modules */ + BNA_TRC_WDRV = 11, /* BNA windows driver modules */ + BNA_TRC_VDRV = 12, /* BNA vmware driver modules */ + BNA_TRC_SDRV = 13, /* BNA Solaris driver modules */ + BNA_TRC_LDRV = 14, /* BNA Linux driver modules */ + BNA_TRC_HAL = 15, /* BNA modules */ + BFA_TRC_CNA = 16, /* Common modules */ + BNA_TRC_IMDRV = 17 /* BNA windows intermediate driver modules */ +}; +#define BFA_TRC_MOD_SH 10 +#define BFA_TRC_MOD(__mod) ((BFA_TRC_ ## __mod) << BFA_TRC_MOD_SH) + +/** + * Define a new tracing file (module). Module should match one defined above. + */ +#define BFA_TRC_FILE(__mod, __submod) \ + static int __trc_fileno = ((BFA_TRC_ ## __mod ## _ ## __submod) | \ + BFA_TRC_MOD(__mod)) + + +#define bfa_trc32(_trcp, _data) \ + __bfa_trc((_trcp)->trcmod, __trc_fileno, __LINE__, (u32)_data) + + +#ifndef BFA_BOOT_BUILD +#define bfa_trc(_trcp, _data) \ + __bfa_trc((_trcp)->trcmod, __trc_fileno, __LINE__, (u64)_data) +#else +void bfa_boot_trc(struct bfa_trc_mod_s *trcmod, u16 fileno, + u16 line, u32 data); +#define bfa_trc(_trcp, _data) \ + bfa_boot_trc((_trcp)->trcmod, __trc_fileno, __LINE__, (u32)_data) +#endif + + +static inline void +bfa_trc_init(struct bfa_trc_mod_s *trcm) +{ + trcm->head = trcm->tail = trcm->stopped = 0; + trcm->ntrc = BFA_TRC_MAX; +} + + +static inline void +bfa_trc_stop(struct bfa_trc_mod_s *trcm) +{ + trcm->stopped = 1; +} + +#ifdef FWTRC +extern void dc_flush(void *data); +#else +#define dc_flush(data) +#endif + + +static inline void +__bfa_trc(struct bfa_trc_mod_s *trcm, int fileno, int line, u64 data) +{ + int tail = trcm->tail; + struct bfa_trc_s *trc = &trcm->trc[tail]; + + if (trcm->stopped) + return; + + trc->fileno = (u16) fileno; + trc->line = (u16) line; + trc->data.u64 = data; + trc->timestamp = BFA_TRC_TS(trcm); + dc_flush(trc); + + trcm->tail = (trcm->tail + 1) & (BFA_TRC_MAX - 1); + if (trcm->tail == trcm->head) + trcm->head = (trcm->head + 1) & (BFA_TRC_MAX - 1); + dc_flush(trcm); +} + + +static inline void +__bfa_trc32(struct bfa_trc_mod_s *trcm, int fileno, int line, u32 data) +{ + int tail = trcm->tail; + struct bfa_trc_s *trc = &trcm->trc[tail]; + + if (trcm->stopped) + return; + + trc->fileno = (u16) fileno; + trc->line = (u16) line; + trc->data.u32.u32 = data; + trc->timestamp = BFA_TRC_TS(trcm); + dc_flush(trc); + + trcm->tail = (trcm->tail + 1) & (BFA_TRC_MAX - 1); + if (trcm->tail == trcm->head) + trcm->head = (trcm->head + 1) & (BFA_TRC_MAX - 1); + dc_flush(trcm); +} + +#ifndef BFA_PERF_BUILD +#define bfa_trc_fp(_trcp, _data) bfa_trc(_trcp, _data) +#else +#define bfa_trc_fp(_trcp, _data) +#endif + +#endif /* __BFA_TRC_H__ */ + diff --git a/drivers/scsi/bfa/include/cs/bfa_wc.h b/drivers/scsi/bfa/include/cs/bfa_wc.h new file mode 100644 index 000000000000..0460bd4fc7c4 --- /dev/null +++ b/drivers/scsi/bfa/include/cs/bfa_wc.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +/** + * bfa_wc.h Generic wait counter. + */ + +#ifndef __BFA_WC_H__ +#define __BFA_WC_H__ + +typedef void (*bfa_wc_resume_t) (void *cbarg); + +struct bfa_wc_s { + bfa_wc_resume_t wc_resume; + void *wc_cbarg; + int wc_count; +}; + +static inline void +bfa_wc_up(struct bfa_wc_s *wc) +{ + wc->wc_count++; +} + +static inline void +bfa_wc_down(struct bfa_wc_s *wc) +{ + wc->wc_count--; + if (wc->wc_count == 0) + wc->wc_resume(wc->wc_cbarg); +} + +/** + * Initialize a waiting counter. + */ +static inline void +bfa_wc_init(struct bfa_wc_s *wc, bfa_wc_resume_t wc_resume, void *wc_cbarg) +{ + wc->wc_resume = wc_resume; + wc->wc_cbarg = wc_cbarg; + wc->wc_count = 0; + bfa_wc_up(wc); +} + +/** + * Wait for counter to reach zero + */ +static inline void +bfa_wc_wait(struct bfa_wc_s *wc) +{ + bfa_wc_down(wc); +} + +#endif diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_adapter.h b/drivers/scsi/bfa/include/defs/bfa_defs_adapter.h new file mode 100644 index 000000000000..8c208fc8e329 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_adapter.h @@ -0,0 +1,82 @@ +/* + * 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. + */ +#ifndef __BFA_DEFS_ADAPTER_H__ +#define __BFA_DEFS_ADAPTER_H__ + +#include +#include +#include + +/** + * BFA adapter level attributes. + */ +enum { + BFA_ADAPTER_SERIAL_NUM_LEN = STRSZ(BFA_MFG_SERIALNUM_SIZE), + /* + *!< adapter serial num length + */ + BFA_ADAPTER_MODEL_NAME_LEN = 16, /* model name length */ + BFA_ADAPTER_MODEL_DESCR_LEN = 128, /* model description length */ + BFA_ADAPTER_MFG_NAME_LEN = 8, /* manufacturer name length */ + BFA_ADAPTER_SYM_NAME_LEN = 64, /* adapter symbolic name length */ + BFA_ADAPTER_OS_TYPE_LEN = 64, /* adapter os type length */ +}; + +struct bfa_adapter_attr_s { + char manufacturer[BFA_ADAPTER_MFG_NAME_LEN]; + char serial_num[BFA_ADAPTER_SERIAL_NUM_LEN]; + u32 rsvd1; + char model[BFA_ADAPTER_MODEL_NAME_LEN]; + char model_descr[BFA_ADAPTER_MODEL_DESCR_LEN]; + wwn_t pwwn; + char node_symname[FC_SYMNAME_MAX]; + char hw_ver[BFA_VERSION_LEN]; + char fw_ver[BFA_VERSION_LEN]; + char optrom_ver[BFA_VERSION_LEN]; + char os_type[BFA_ADAPTER_OS_TYPE_LEN]; + struct bfa_mfg_vpd_s vpd; + struct mac_s mac; + + u8 nports; + u8 max_speed; + u8 prototype; + char asic_rev; + + u8 pcie_gen; + u8 pcie_lanes_orig; + u8 pcie_lanes; + u8 cna_capable; +}; + +/** + * BFA adapter level events + * Arguments below are in BFAL context from Mgmt + * BFA_PORT_AEN_ADD: [in]: None [out]: serial_num, pwwn, nports + * BFA_PORT_AEN_REMOVE: [in]: pwwn [out]: serial_num, pwwn, nports + */ +enum bfa_adapter_aen_event { + BFA_ADAPTER_AEN_ADD = 1, /* New Adapter found event */ + BFA_ADAPTER_AEN_REMOVE = 2, /* Adapter removed event */ +}; + +struct bfa_adapter_aen_data_s { + char serial_num[BFA_ADAPTER_SERIAL_NUM_LEN]; + u32 nports; /* Number of NPorts */ + wwn_t pwwn; /* WWN of one of its physical port */ +}; + +#endif /* __BFA_DEFS_ADAPTER_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_aen.h b/drivers/scsi/bfa/include/defs/bfa_defs_aen.h new file mode 100644 index 000000000000..4c81a613db3d --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_aen.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_AEN_H__ +#define __BFA_DEFS_AEN_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum bfa_aen_category { + BFA_AEN_CAT_ADAPTER = 1, + BFA_AEN_CAT_PORT = 2, + BFA_AEN_CAT_LPORT = 3, + BFA_AEN_CAT_RPORT = 4, + BFA_AEN_CAT_ITNIM = 5, + BFA_AEN_CAT_TIN = 6, + BFA_AEN_CAT_IPFC = 7, + BFA_AEN_CAT_AUDIT = 8, + BFA_AEN_CAT_IOC = 9, + BFA_AEN_CAT_ETHPORT = 10, + BFA_AEN_MAX_CAT = 10 +}; + +#pragma pack(1) +union bfa_aen_data_u { + struct bfa_adapter_aen_data_s adapter; + struct bfa_port_aen_data_s port; + struct bfa_lport_aen_data_s lport; + struct bfa_rport_aen_data_s rport; + struct bfa_itnim_aen_data_s itnim; + struct bfa_audit_aen_data_s audit; + struct bfa_ioc_aen_data_s ioc; + struct bfa_ethport_aen_data_s ethport; +}; + +struct bfa_aen_entry_s { + enum bfa_aen_category aen_category; + int aen_type; + union bfa_aen_data_u aen_data; + struct bfa_timeval_s aen_tv; + s32 seq_num; + s32 bfad_num; + s32 rsvd[1]; +}; + +#pragma pack() + +#define bfa_aen_event_t int + +#endif /* __BFA_DEFS_AEN_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_audit.h b/drivers/scsi/bfa/include/defs/bfa_defs_audit.h new file mode 100644 index 000000000000..8e3a962bf20c --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_audit.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_AUDIT_H__ +#define __BFA_DEFS_AUDIT_H__ + +#include + +/** + * BFA audit events + */ +enum bfa_audit_aen_event { + BFA_AUDIT_AEN_AUTH_ENABLE = 1, + BFA_AUDIT_AEN_AUTH_DISABLE = 2, +}; + +/** + * audit event data + */ +struct bfa_audit_aen_data_s { + wwn_t pwwn; +}; + +#endif /* __BFA_DEFS_AUDIT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_auth.h b/drivers/scsi/bfa/include/defs/bfa_defs_auth.h new file mode 100644 index 000000000000..dd19c83aba58 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_auth.h @@ -0,0 +1,112 @@ +/* + * 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. + */ +#ifndef __BFA_DEFS_AUTH_H__ +#define __BFA_DEFS_AUTH_H__ + +#include + +#define PUBLIC_KEY 15409 +#define PRIVATE_KEY 19009 +#define KEY_LEN 32399 +#define BFA_AUTH_SECRET_STRING_LEN 256 +#define BFA_AUTH_FAIL_TIMEOUT 0xFF + +/** + * Authentication status + */ +enum bfa_auth_status { + BFA_AUTH_STATUS_NONE = 0, /* no authentication */ + BFA_AUTH_UNINIT = 1, /* state - uninit */ + BFA_AUTH_NEG_SEND = 2, /* state - negotiate send */ + BFA_AUTH_CHAL_WAIT = 3, /* state - challenge wait */ + BFA_AUTH_NEG_RETRY = 4, /* state - negotiate retry */ + BFA_AUTH_REPLY_SEND = 5, /* state - reply send */ + BFA_AUTH_STATUS_WAIT = 6, /* state - status wait */ + BFA_AUTH_SUCCESS = 7, /* state - success */ + BFA_AUTH_FAILED = 8, /* state - failed */ + BFA_AUTH_STATUS_UNKNOWN = 9, /* authentication status unknown */ +}; + +struct auth_proto_stats_s { + u32 auth_rjts; + u32 auth_negs; + u32 auth_dones; + + u32 dhchap_challenges; + u32 dhchap_replies; + u32 dhchap_successes; +}; + +/** + * Authentication related statistics + */ +struct bfa_auth_stats_s { + u32 auth_failures; /* authentication failures */ + u32 auth_successes; /* authentication successes*/ + struct auth_proto_stats_s auth_rx_stats; /* Rx protocol stats */ + struct auth_proto_stats_s auth_tx_stats; /* Tx protocol stats */ +}; + +/** + * Authentication hash function algorithms + */ +enum bfa_auth_algo { + BFA_AUTH_ALGO_MD5 = 1, /* Message-Digest algorithm 5 */ + BFA_AUTH_ALGO_SHA1 = 2, /* Secure Hash Algorithm 1 */ + BFA_AUTH_ALGO_MS = 3, /* MD5, then SHA-1 */ + BFA_AUTH_ALGO_SM = 4, /* SHA-1, then MD5 */ +}; + +/** + * DH Groups + * + * Current value could be combination of one or more of the following values + */ +enum bfa_auth_group { + BFA_AUTH_GROUP_DHNULL = 0, /* DH NULL (value == 0) */ + BFA_AUTH_GROUP_DH768 = 1, /* DH group 768 (value == 1) */ + BFA_AUTH_GROUP_DH1024 = 2, /* DH group 1024 (value == 2) */ + BFA_AUTH_GROUP_DH1280 = 4, /* DH group 1280 (value == 3) */ + BFA_AUTH_GROUP_DH1536 = 8, /* DH group 1536 (value == 4) */ + + BFA_AUTH_GROUP_ALL = 256 /* Use default DH group order + * 0, 1, 2, 3, 4 */ +}; + +/** + * Authentication secret sources + */ +enum bfa_auth_secretsource { + BFA_AUTH_SECSRC_LOCAL = 1, /* locally configured */ + BFA_AUTH_SECSRC_RADIUS = 2, /* use radius server */ + BFA_AUTH_SECSRC_TACACS = 3, /* TACACS server */ +}; + +/** + * Authentication attributes + */ +struct bfa_auth_attr_s { + enum bfa_auth_status status; + enum bfa_auth_algo algo; + enum bfa_auth_group dh_grp; + u16 rjt_code; + u16 rjt_code_exp; + u8 secret_set; + u8 resv[7]; +}; + +#endif /* __BFA_DEFS_AUTH_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_boot.h b/drivers/scsi/bfa/include/defs/bfa_defs_boot.h new file mode 100644 index 000000000000..6f4aa5283545 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_boot.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_BOOT_H__ +#define __BFA_DEFS_BOOT_H__ + +#include +#include +#include + +enum { + BFA_BOOT_BOOTLUN_MAX = 4, /* maximum boot lun per IOC */ +}; + +#define BOOT_CFG_REV1 1 + +/** + * Boot options setting. Boot options setting determines from where + * to get the boot lun information + */ +enum bfa_boot_bootopt { + BFA_BOOT_AUTO_DISCOVER = 0, /* Boot from blun provided by fabric */ + BFA_BOOT_STORED_BLUN = 1, /* Boot from bluns stored in flash */ + BFA_BOOT_FIRST_LUN = 2, /* Boot from first discovered blun */ +}; + +/** + * Boot lun information. + */ +struct bfa_boot_bootlun_s { + wwn_t pwwn; /* port wwn of target */ + lun_t lun; /* 64-bit lun */ +}; + +/** + * BOOT boot configuraton + */ +struct bfa_boot_cfg_s { + u8 version; + u8 rsvd1; + u16 chksum; + + u8 enable; /* enable/disable SAN boot */ + u8 speed; /* boot speed settings */ + u8 topology; /* boot topology setting */ + u8 bootopt; /* bfa_boot_bootopt_t */ + + u32 nbluns; /* number of boot luns */ + + u32 rsvd2; + + struct bfa_boot_bootlun_s blun[BFA_BOOT_BOOTLUN_MAX]; + struct bfa_boot_bootlun_s blun_disc[BFA_BOOT_BOOTLUN_MAX]; +}; + + +#endif /* __BFA_DEFS_BOOT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_cee.h b/drivers/scsi/bfa/include/defs/bfa_defs_cee.h new file mode 100644 index 000000000000..520a22f52dd1 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_cee.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. + * All rights reserved + * www.brocade.com + * + * bfa_defs_cee.h Interface declarations between host based + * BFAL and DCBX/LLDP module in Firmware + * + * 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. + */ +#ifndef __BFA_DEFS_CEE_H__ +#define __BFA_DEFS_CEE_H__ + +#include +#include +#include + +#pragma pack(1) + +#define BFA_CEE_LLDP_MAX_STRING_LEN (128) + + +/* FIXME: this is coming from the protocol spec. Can the host & apps share the + protocol .h files ? + */ +#define BFA_CEE_LLDP_SYS_CAP_OTHER 0x0001 +#define BFA_CEE_LLDP_SYS_CAP_REPEATER 0x0002 +#define BFA_CEE_LLDP_SYS_CAP_MAC_BRIDGE 0x0004 +#define BFA_CEE_LLDP_SYS_CAP_WLAN_AP 0x0008 +#define BFA_CEE_LLDP_SYS_CAP_ROUTER 0x0010 +#define BFA_CEE_LLDP_SYS_CAP_TELEPHONE 0x0020 +#define BFA_CEE_LLDP_SYS_CAP_DOCSIS_CD 0x0040 +#define BFA_CEE_LLDP_SYS_CAP_STATION 0x0080 +#define BFA_CEE_LLDP_SYS_CAP_CVLAN 0x0100 +#define BFA_CEE_LLDP_SYS_CAP_SVLAN 0x0200 +#define BFA_CEE_LLDP_SYS_CAP_TPMR 0x0400 + + +/* LLDP string type */ +struct bfa_cee_lldp_str_s { + u8 sub_type; + u8 len; + u8 rsvd[2]; + u8 value[BFA_CEE_LLDP_MAX_STRING_LEN]; +}; + + +/* LLDP paramters */ +struct bfa_cee_lldp_cfg_s { + struct bfa_cee_lldp_str_s chassis_id; + struct bfa_cee_lldp_str_s port_id; + struct bfa_cee_lldp_str_s port_desc; + struct bfa_cee_lldp_str_s sys_name; + struct bfa_cee_lldp_str_s sys_desc; + struct bfa_cee_lldp_str_s mgmt_addr; + u16 time_to_interval; + u16 enabled_system_cap; +}; + +enum bfa_cee_dcbx_version_e { + DCBX_PROTOCOL_PRECEE = 1, + DCBX_PROTOCOL_CEE = 2, +}; + +enum bfa_cee_lls_e { + CEE_LLS_DOWN_NO_TLV = 0, /* LLS is down because the TLV not sent by + * the peer */ + CEE_LLS_DOWN = 1, /* LLS is down as advertised by the peer */ + CEE_LLS_UP = 2, +}; + +/* CEE/DCBX parameters */ +struct bfa_cee_dcbx_cfg_s { + u8 pgid[8]; + u8 pg_percentage[8]; + u8 pfc_enabled; /* bitmap of priorties with PFC enabled */ + u8 fcoe_user_priority; /* bitmap of priorities used for FcoE + * traffic */ + u8 dcbx_version; /* operating version:CEE or preCEE */ + u8 lls_fcoe; /* FCoE Logical Link Status */ + u8 lls_lan; /* LAN Logical Link Status */ + u8 rsvd[3]; +}; + +/* CEE status */ +/* Making this to tri-state for the benefit of port list command */ +enum bfa_cee_status_e { + CEE_PHY_DOWN = 0, + CEE_PHY_UP = 1, + CEE_UP = 2, +}; + +/* CEE Query */ +struct bfa_cee_attr_s { + u8 cee_status; + u8 error_reason; + struct bfa_cee_lldp_cfg_s lldp_remote; + struct bfa_cee_dcbx_cfg_s dcbx_remote; + mac_t src_mac; + u8 link_speed; + u8 filler[3]; +}; + + + + +/* LLDP/DCBX/CEE Statistics */ + +struct bfa_cee_lldp_stats_s { + u32 frames_transmitted; + u32 frames_aged_out; + u32 frames_discarded; + u32 frames_in_error; + u32 frames_rcvd; + u32 tlvs_discarded; + u32 tlvs_unrecognized; +}; + +struct bfa_cee_dcbx_stats_s { + u32 subtlvs_unrecognized; + u32 negotiation_failed; + u32 remote_cfg_changed; + u32 tlvs_received; + u32 tlvs_invalid; + u32 seqno; + u32 ackno; + u32 recvd_seqno; + u32 recvd_ackno; +}; + +struct bfa_cee_cfg_stats_s { + u32 cee_status_down; + u32 cee_status_up; + u32 cee_hw_cfg_changed; + u32 recvd_invalid_cfg; +}; + + +struct bfa_cee_stats_s { + struct bfa_cee_lldp_stats_s lldp_stats; + struct bfa_cee_dcbx_stats_s dcbx_stats; + struct bfa_cee_cfg_stats_s cfg_stats; +}; + +#pragma pack() + + +#endif /* __BFA_DEFS_CEE_H__ */ + + diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_driver.h b/drivers/scsi/bfa/include/defs/bfa_defs_driver.h new file mode 100644 index 000000000000..57049805762b --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_driver.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_DRIVER_H__ +#define __BFA_DEFS_DRIVER_H__ + +/** + * Driver statistics + */ + u16 tm_io_abort; + u16 tm_io_abort_comp; + u16 tm_lun_reset; + u16 tm_lun_reset_comp; + u16 tm_target_reset; + u16 tm_bus_reset; + u16 ioc_restart; /* IOC restart count */ + u16 io_pending; /* outstanding io count per-IOC */ + u64 control_req; + u64 input_req; + u64 output_req; + u64 input_words; + u64 output_words; +} bfa_driver_stats_t; + + +#endif /* __BFA_DEFS_DRIVER_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_ethport.h b/drivers/scsi/bfa/include/defs/bfa_defs_ethport.h new file mode 100644 index 000000000000..79f9b3e146f7 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_ethport.h @@ -0,0 +1,98 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_ETHPORT_H__ +#define __BFA_DEFS_ETHPORT_H__ + +#include +#include +#include +#include + +struct bna_tx_info_s { + u32 miniport_state; + u32 adapter_state; + u64 tx_count; + u64 tx_wi; + u64 tx_sg; + u64 tx_tcp_chksum; + u64 tx_udp_chksum; + u64 tx_ip_chksum; + u64 tx_lsov1; + u64 tx_lsov2; + u64 tx_max_sg_len ; +}; + +struct bna_rx_queue_info_s { + u16 q_id ; + u16 buf_size ; + u16 buf_count ; + u16 rsvd ; + u64 rx_count ; + u64 rx_dropped ; + u64 rx_unsupported ; + u64 rx_internal_err ; + u64 rss_count ; + u64 vlan_count ; + u64 rx_tcp_chksum ; + u64 rx_udp_chksum ; + u64 rx_ip_chksum ; + u64 rx_hds ; +}; + +struct bna_rx_q_set_s { + u16 q_set_type; + u32 miniport_state; + u32 adapter_state; + struct bna_rx_queue_info_s rx_queue[2]; +}; + +struct bna_port_stats_s { + struct bna_tx_info_s tx_stats; + u16 qset_count ; + struct bna_rx_q_set_s rx_qset[8]; +}; + +struct bfa_ethport_stats_s { + struct bna_stats_txf txf_stats[1]; + struct bna_stats_rxf rxf_stats[1]; + struct bnad_drv_stats drv_stats; +}; + +/** + * Ethernet port events + * Arguments below are in BFAL context from Mgmt + * BFA_PORT_AEN_ETH_LINKUP: [in]: mac [out]: mac + * BFA_PORT_AEN_ETH_LINKDOWN: [in]: mac [out]: mac + * BFA_PORT_AEN_ETH_ENABLE: [in]: mac [out]: mac + * BFA_PORT_AEN_ETH_DISABLE: [in]: mac [out]: mac + * + */ +enum bfa_ethport_aen_event { + BFA_ETHPORT_AEN_LINKUP = 1, /* Base Port Ethernet link up event */ + BFA_ETHPORT_AEN_LINKDOWN = 2, /* Base Port Ethernet link down event */ + BFA_ETHPORT_AEN_ENABLE = 3, /* Base Port Ethernet link enable event */ + BFA_ETHPORT_AEN_DISABLE = 4, /* Base Port Ethernet link disable + * event */ +}; + +struct bfa_ethport_aen_data_s { + mac_t mac; /* MAC address of the physical port */ +}; + + +#endif /* __BFA_DEFS_ETHPORT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_fcpim.h b/drivers/scsi/bfa/include/defs/bfa_defs_fcpim.h new file mode 100644 index 000000000000..c08f4f5026ac --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_fcpim.h @@ -0,0 +1,45 @@ +/* + * 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. + */ +#ifndef __BFA_DEFS_FCPIM_H__ +#define __BFA_DEFS_FCPIM_H__ + +struct bfa_fcpim_stats_s { + u32 total_ios; /* Total IO count */ + u32 qresumes; /* IO waiting for CQ space */ + u32 no_iotags; /* NO IO contexts */ + u32 io_aborts; /* IO abort requests */ + u32 no_tskims; /* NO task management contexts */ + u32 iocomp_ok; /* IO completions with OK status */ + u32 iocomp_underrun; /* IO underrun (good) */ + u32 iocomp_overrun; /* IO overrun (good) */ + u32 iocomp_aborted; /* Aborted IO requests */ + u32 iocomp_timedout; /* IO timeouts */ + u32 iocom_nexus_abort; /* IO selection timeouts */ + u32 iocom_proto_err; /* IO protocol errors */ + u32 iocom_dif_err; /* IO SBC-3 protection errors */ + u32 iocom_tm_abort; /* IO aborted by TM requests */ + u32 iocom_sqer_needed; /* IO retry for SQ error + *recovery */ + u32 iocom_res_free; /* Delayed freeing of IO resources */ + u32 iocomp_scsierr; /* IO with non-good SCSI status */ + u32 iocom_hostabrts; /* Host IO abort requests */ + u32 iocom_utags; /* IO comp with unknown tags */ + u32 io_cleanups; /* IO implicitly aborted */ + u32 io_tmaborts; /* IO aborted due to TM commands */ + u32 rsvd; +}; +#endif /*__BFA_DEFS_FCPIM_H__*/ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_im_common.h b/drivers/scsi/bfa/include/defs/bfa_defs_im_common.h new file mode 100644 index 000000000000..9ccf53bef65a --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_im_common.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_IM_COMMON_H__ +#define __BFA_DEFS_IM_COMMON_H__ + +#define BFA_ADAPTER_NAME_LEN 256 +#define BFA_ADAPTER_GUID_LEN 256 +#define RESERVED_VLAN_NAME L"PORT VLAN" +#define PASSTHRU_VLAN_NAME L"PASSTHRU VLAN" + + u64 tx_pkt_cnt; + u64 rx_pkt_cnt; + u32 duration; + u8 status; +} bfa_im_stats_t, *pbfa_im_stats_t; + +#endif /* __BFA_DEFS_IM_COMMON_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_im_team.h b/drivers/scsi/bfa/include/defs/bfa_defs_im_team.h new file mode 100644 index 000000000000..a486a7eb81d6 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_im_team.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_IM_TEAM_H__ +#define __BFA_DEFS_IM_TEAM_H__ + +#include + +#define BFA_TEAM_MAX_PORTS 8 +#define BFA_TEAM_NAME_LEN 256 +#define BFA_MAX_NUM_TEAMS 16 +#define BFA_TEAM_INVALID_DELAY -1 + + BFA_LACP_RATE_SLOW = 1, + BFA_LACP_RATE_FAST +} bfa_im_lacp_rate_t; + + BFA_TEAM_MODE_FAIL_OVER = 1, + BFA_TEAM_MODE_FAIL_BACK, + BFA_TEAM_MODE_LACP, + BFA_TEAM_MODE_NONE +} bfa_im_team_mode_t; + + BFA_XMIT_POLICY_L2 = 1, + BFA_XMIT_POLICY_L3_L4 +} bfa_im_xmit_policy_t; + + bfa_im_team_mode_t team_mode; + bfa_im_lacp_rate_t lacp_rate; + bfa_im_xmit_policy_t xmit_policy; + int delay; + wchar_t primary[BFA_ADAPTER_NAME_LEN]; + wchar_t preferred_primary[BFA_ADAPTER_NAME_LEN]; + mac_t mac; + u16 num_ports; + u16 num_vlans; + u16 vlan_list[BFA_MAX_VLANS_PER_PORT]; + wchar_t team_guid_list[BFA_TEAM_MAX_PORTS][BFA_ADAPTER_GUID_LEN]; + wchar_t ioc_name_list[BFA_TEAM_MAX_PORTS][BFA_ADAPTER_NAME_LEN]; +} bfa_im_team_attr_t; + + wchar_t team_name[BFA_TEAM_NAME_LEN]; + bfa_im_xmit_policy_t xmit_policy; + int delay; + wchar_t primary[BFA_ADAPTER_NAME_LEN]; + wchar_t preferred_primary[BFA_ADAPTER_NAME_LEN]; +} bfa_im_team_edit_t, *pbfa_im_team_edit_t; + + wchar_t team_name[BFA_TEAM_NAME_LEN]; + bfa_im_team_mode_t team_mode; + mac_t mac; +} bfa_im_team_info_t; + + bfa_im_team_info_t team_info[BFA_MAX_NUM_TEAMS]; + u16 num_teams; +} bfa_im_team_list_t, *pbfa_im_team_list_t; + +#endif /* __BFA_DEFS_IM_TEAM_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_ioc.h b/drivers/scsi/bfa/include/defs/bfa_defs_ioc.h new file mode 100644 index 000000000000..b1d532da3a9d --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_ioc.h @@ -0,0 +1,152 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_IOC_H__ +#define __BFA_DEFS_IOC_H__ + +#include +#include +#include +#include +#include + +enum { + BFA_IOC_DRIVER_LEN = 16, + BFA_IOC_CHIP_REV_LEN = 8, +}; + +/** + * Driver and firmware versions. + */ +struct bfa_ioc_driver_attr_s { + char driver[BFA_IOC_DRIVER_LEN]; /* driver name */ + char driver_ver[BFA_VERSION_LEN]; /* driver version */ + char fw_ver[BFA_VERSION_LEN]; /* firmware version*/ + char bios_ver[BFA_VERSION_LEN]; /* bios version */ + char efi_ver[BFA_VERSION_LEN]; /* EFI version */ + char ob_ver[BFA_VERSION_LEN]; /* openboot version*/ +}; + +/** + * IOC PCI device attributes + */ +struct bfa_ioc_pci_attr_s { + u16 vendor_id; /* PCI vendor ID */ + u16 device_id; /* PCI device ID */ + u16 ssid; /* subsystem ID */ + u16 ssvid; /* subsystem vendor ID */ + u32 pcifn; /* PCI device function */ + u32 rsvd; /* padding */ + u8 chip_rev[BFA_IOC_CHIP_REV_LEN]; /* chip revision */ +}; + +/** + * IOC states + */ +enum bfa_ioc_state { + BFA_IOC_RESET = 1, /* IOC is in reset state */ + BFA_IOC_SEMWAIT = 2, /* Waiting for IOC hardware semaphore */ + BFA_IOC_HWINIT = 3, /* IOC hardware is being initialized */ + BFA_IOC_GETATTR = 4, /* IOC is being configured */ + BFA_IOC_OPERATIONAL = 5, /* IOC is operational */ + BFA_IOC_INITFAIL = 6, /* IOC hardware failure */ + BFA_IOC_HBFAIL = 7, /* IOC heart-beat failure */ + BFA_IOC_DISABLING = 8, /* IOC is being disabled */ + BFA_IOC_DISABLED = 9, /* IOC is disabled */ + BFA_IOC_FWMISMATCH = 10, /* IOC firmware different from drivers */ +}; + +/** + * IOC firmware stats + */ +struct bfa_fw_ioc_stats_s { + u32 hb_count; + u32 cfg_reqs; + u32 enable_reqs; + u32 disable_reqs; + u32 stats_reqs; + u32 clrstats_reqs; + u32 unknown_reqs; + u32 ic_reqs; /* interrupt coalesce reqs */ +}; + +/** + * IOC driver stats + */ +struct bfa_ioc_drv_stats_s { + u32 ioc_isrs; + u32 ioc_enables; + u32 ioc_disables; + u32 ioc_hbfails; + u32 ioc_boots; + u32 stats_tmos; + u32 hb_count; + u32 disable_reqs; + u32 enable_reqs; + u32 disable_replies; + u32 enable_replies; +}; + +/** + * IOC statistics + */ +struct bfa_ioc_stats_s { + struct bfa_ioc_drv_stats_s drv_stats; /* driver IOC stats */ + struct bfa_fw_ioc_stats_s fw_stats; /* firmware IOC stats */ +}; + + +enum bfa_ioc_type_e { + BFA_IOC_TYPE_FC = 1, + BFA_IOC_TYPE_FCoE = 2, + BFA_IOC_TYPE_LL = 3, +}; + +/** + * IOC attributes returned in queries + */ +struct bfa_ioc_attr_s { + enum bfa_ioc_type_e ioc_type; + enum bfa_ioc_state state; /* IOC state */ + struct bfa_adapter_attr_s adapter_attr; /* HBA attributes */ + struct bfa_ioc_driver_attr_s driver_attr; /* driver attr */ + struct bfa_ioc_pci_attr_s pci_attr; + u8 port_id; /* port number */ +}; + +/** + * BFA IOC level events + */ +enum bfa_ioc_aen_event { + BFA_IOC_AEN_HBGOOD = 1, /* Heart Beat restore event */ + BFA_IOC_AEN_HBFAIL = 2, /* Heart Beat failure event */ + BFA_IOC_AEN_ENABLE = 3, /* IOC enabled event */ + BFA_IOC_AEN_DISABLE = 4, /* IOC disabled event */ + BFA_IOC_AEN_FWMISMATCH = 5, /* IOC firmware mismatch */ +}; + +/** + * BFA IOC level event data, now just a place holder + */ +struct bfa_ioc_aen_data_s { + enum bfa_ioc_type_e ioc_type; + wwn_t pwwn; + mac_t mac; +}; + +#endif /* __BFA_DEFS_IOC_H__ */ + diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_iocfc.h b/drivers/scsi/bfa/include/defs/bfa_defs_iocfc.h new file mode 100644 index 000000000000..d76bcbd9820f --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_iocfc.h @@ -0,0 +1,310 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_IOCFC_H__ +#define __BFA_DEFS_IOCFC_H__ + +#include +#include +#include +#include +#include + +#define BFA_IOCFC_INTR_DELAY 1125 +#define BFA_IOCFC_INTR_LATENCY 225 + +/** + * Interrupt coalescing configuration. + */ +struct bfa_iocfc_intr_attr_s { + bfa_boolean_t coalesce; /* enable/disable coalescing */ + u16 latency; /* latency in microseconds */ + u16 delay; /* delay in microseconds */ +}; + +/** + * IOC firmware configuraton + */ +struct bfa_iocfc_fwcfg_s { + u16 num_fabrics; /* number of fabrics */ + u16 num_lports; /* number of local lports */ + u16 num_rports; /* number of remote ports */ + u16 num_ioim_reqs; /* number of IO reqs */ + u16 num_tskim_reqs; /* task management requests */ + u16 num_iotm_reqs; /* number of TM IO reqs */ + u16 num_tsktm_reqs; /* TM task management requests*/ + u16 num_fcxp_reqs; /* unassisted FC exchanges */ + u16 num_uf_bufs; /* unsolicited recv buffers */ + u8 num_cqs; + u8 rsvd; +}; + +struct bfa_iocfc_drvcfg_s { + u16 num_reqq_elems; /* number of req queue elements */ + u16 num_rspq_elems; /* number of rsp queue elements */ + u16 num_sgpgs; /* number of total SG pages */ + u16 num_sboot_tgts; /* number of SAN boot targets */ + u16 num_sboot_luns; /* number of SAN boot luns */ + u16 ioc_recover; /* IOC recovery mode */ + u16 min_cfg; /* minimum configuration */ + u16 path_tov; /* device path timeout */ + bfa_boolean_t delay_comp; /* delay completion of + failed inflight IOs */ + u32 rsvd; +}; +/** + * IOC configuration + */ +struct bfa_iocfc_cfg_s { + struct bfa_iocfc_fwcfg_s fwcfg; /* firmware side config */ + struct bfa_iocfc_drvcfg_s drvcfg; /* driver side config */ +}; + +/** + * IOC firmware IO stats + */ +struct bfa_fw_io_stats_s { + u32 host_abort; /* IO aborted by host driver*/ + u32 host_cleanup; /* IO clean up by host driver */ + + u32 fw_io_timeout; /* IOs timedout */ + u32 fw_frm_parse; /* frame parsed by f/w */ + u32 fw_frm_data; /* fcp_data frame parsed by f/w */ + u32 fw_frm_rsp; /* fcp_rsp frame parsed by f/w */ + u32 fw_frm_xfer_rdy; /* xfer_rdy frame parsed by f/w */ + u32 fw_frm_bls_acc; /* BLS ACC frame parsed by f/w */ + u32 fw_frm_tgt_abort; /* target ABTS parsed by f/w */ + u32 fw_frm_unknown; /* unknown parsed by f/w */ + u32 fw_data_dma; /* f/w DMA'ed the data frame */ + u32 fw_frm_drop; /* f/w drop the frame */ + + u32 rec_timeout; /* FW rec timed out */ + u32 error_rec; /* FW sending rec on + * an error condition*/ + u32 wait_for_si; /* FW wait for SI */ + u32 rec_rsp_inval; /* REC rsp invalid */ + u32 seqr_io_abort; /* target does not know cmd so abort */ + u32 seqr_io_retry; /* SEQR failed so retry IO */ + + u32 itn_cisc_upd_rsp; /* ITN cisc updated on fcp_rsp */ + u32 itn_cisc_upd_data; /* ITN cisc updated on fcp_data */ + u32 itn_cisc_upd_xfer_rdy; /* ITN cisc updated on fcp_data */ + + u32 fcp_data_lost; /* fcp data lost */ + + u32 ro_set_in_xfer_rdy; /* Target set RO in Xfer_rdy frame */ + u32 xfer_rdy_ooo_err; /* Out of order Xfer_rdy received */ + u32 xfer_rdy_unknown_err; /* unknown error in xfer_rdy frame */ + + u32 io_abort_timeout; /* ABTS timedout */ + u32 sler_initiated; /* SLER initiated */ + + u32 unexp_fcp_rsp; /* fcp response in wrong state */ + + u32 fcp_rsp_under_run; /* fcp rsp IO underrun */ + u32 fcp_rsp_under_run_wr; /* fcp rsp IO underrun for write */ + u32 fcp_rsp_under_run_err; /* fcp rsp IO underrun error */ + u32 fcp_rsp_resid_inval; /* invalid residue */ + u32 fcp_rsp_over_run; /* fcp rsp IO overrun */ + u32 fcp_rsp_over_run_err; /* fcp rsp IO overrun error */ + u32 fcp_rsp_proto_err; /* protocol error in fcp rsp */ + u32 fcp_rsp_sense_err; /* error in sense info in fcp rsp */ + u32 fcp_conf_req; /* FCP conf requested */ + + u32 tgt_aborted_io; /* target initiated abort */ + + u32 ioh_edtov_timeout_event;/* IOH edtov timer popped */ + u32 ioh_fcp_rsp_excp_event; /* IOH FCP_RSP exception */ + u32 ioh_fcp_conf_event; /* IOH FCP_CONF */ + u32 ioh_mult_frm_rsp_event; /* IOH multi_frame FCP_RSP */ + u32 ioh_hit_class2_event; /* IOH hit class2 */ + u32 ioh_miss_other_event; /* IOH miss other */ + u32 ioh_seq_cnt_err_event; /* IOH seq cnt error */ + u32 ioh_len_err_event; /* IOH len error - fcp_dl != + * bytes xfered */ + u32 ioh_seq_len_err_event; /* IOH seq len error */ + u32 ioh_data_oor_event; /* Data out of range */ + u32 ioh_ro_ooo_event; /* Relative offset out of range */ + u32 ioh_cpu_owned_event; /* IOH hit -iost owned by f/w */ + u32 ioh_unexp_frame_event; /* unexpected frame recieved + * count */ + u32 ioh_err_int; /* IOH error int during data-phase + * for scsi write + */ +}; + +/** + * IOC port firmware stats + */ + +struct bfa_fw_port_fpg_stats_s { + u32 intr_evt; + u32 intr; + u32 intr_excess; + u32 intr_cause0; + u32 intr_other; + u32 intr_other_ign; + u32 sig_lost; + u32 sig_regained; + u32 sync_lost; + u32 sync_to; + u32 sync_regained; + u32 div2_overflow; + u32 div2_underflow; + u32 efifo_overflow; + u32 efifo_underflow; + u32 idle_rx; + u32 lrr_rx; + u32 lr_rx; + u32 ols_rx; + u32 nos_rx; + u32 lip_rx; + u32 arbf0_rx; + u32 mrk_rx; + u32 const_mrk_rx; + u32 prim_unknown; + u32 rsvd; +}; + + +struct bfa_fw_port_lksm_stats_s { + u32 hwsm_success; /* hwsm state machine success */ + u32 hwsm_fails; /* hwsm fails */ + u32 hwsm_wdtov; /* hwsm timed out */ + u32 swsm_success; /* swsm success */ + u32 swsm_fails; /* swsm fails */ + u32 swsm_wdtov; /* swsm timed out */ + u32 busybufs; /* link init failed due to busybuf */ + u32 buf_waits; /* bufwait state entries */ + u32 link_fails; /* link failures */ + u32 psp_errors; /* primitive sequence protocol errors */ + u32 lr_unexp; /* No. of times LR rx-ed unexpectedly */ + u32 lrr_unexp; /* No. of times LRR rx-ed unexpectedly */ + u32 lr_tx; /* No. of times LR tx started */ + u32 lrr_tx; /* No. of times LRR tx started */ + u32 ols_tx; /* No. of times OLS tx started */ + u32 nos_tx; /* No. of times NOS tx started */ +}; + + +struct bfa_fw_port_snsm_stats_s { + u32 hwsm_success; /* Successful hwsm terminations */ + u32 hwsm_fails; /* hwsm fail count */ + u32 hwsm_wdtov; /* hwsm timed out */ + u32 swsm_success; /* swsm success */ + u32 swsm_wdtov; /* swsm timed out */ + u32 error_resets; /* error resets initiated by upsm */ + u32 sync_lost; /* Sync loss count */ + u32 sig_lost; /* Signal loss count */ +}; + + +struct bfa_fw_port_physm_stats_s { + u32 module_inserts; /* Module insert count */ + u32 module_xtracts; /* Module extracts count */ + u32 module_invalids; /* Invalid module inserted count */ + u32 module_read_ign; /* Module validation status ignored */ + u32 laser_faults; /* Laser fault count */ + u32 rsvd; +}; + + +struct bfa_fw_fip_stats_s { + u32 disc_req; /* Discovery solicit requests */ + u32 disc_rsp; /* Discovery solicit response */ + u32 disc_err; /* Discovery advt. parse errors */ + u32 disc_unsol; /* Discovery unsolicited */ + u32 disc_timeouts; /* Discovery timeouts */ + u32 linksvc_unsupp; /* Unsupported link service req */ + u32 linksvc_err; /* Parse error in link service req */ + u32 logo_req; /* Number of FIP logos received */ + u32 clrvlink_req; /* Clear virtual link req */ + u32 op_unsupp; /* Unsupported FIP operation */ + u32 untagged; /* Untagged frames (ignored) */ + u32 rsvd; +}; + + +struct bfa_fw_lps_stats_s { + u32 mac_invalids; /* Invalid mac assigned */ + u32 rsvd; +}; + + +struct bfa_fw_fcoe_stats_s { + u32 cee_linkups; /* CEE link up count */ + u32 cee_linkdns; /* CEE link down count */ + u32 fip_linkups; /* FIP link up count */ + u32 fip_linkdns; /* FIP link up count */ + u32 fip_fails; /* FIP fail count */ + u32 mac_invalids; /* Invalid mac assigned */ +}; + +/** + * IOC firmware FCoE port stats + */ +struct bfa_fw_fcoe_port_stats_s { + struct bfa_fw_fcoe_stats_s fcoe_stats; + struct bfa_fw_fip_stats_s fip_stats; +}; + +/** + * IOC firmware FC port stats + */ +struct bfa_fw_fc_port_stats_s { + struct bfa_fw_port_fpg_stats_s fpg_stats; + struct bfa_fw_port_physm_stats_s physm_stats; + struct bfa_fw_port_snsm_stats_s snsm_stats; + struct bfa_fw_port_lksm_stats_s lksm_stats; +}; + +/** + * IOC firmware FC port stats + */ +union bfa_fw_port_stats_s { + struct bfa_fw_fc_port_stats_s fc_stats; + struct bfa_fw_fcoe_port_stats_s fcoe_stats; +}; + +/** + * IOC firmware stats + */ +struct bfa_fw_stats_s { + struct bfa_fw_ioc_stats_s ioc_stats; + struct bfa_fw_io_stats_s io_stats; + union bfa_fw_port_stats_s port_stats; +}; + +/** + * IOC statistics + */ +struct bfa_iocfc_stats_s { + struct bfa_fw_stats_s fw_stats; /* firmware IOC stats */ +}; + +/** + * IOC attributes returned in queries + */ +struct bfa_iocfc_attr_s { + struct bfa_iocfc_cfg_s config; /* IOCFC config */ + struct bfa_iocfc_intr_attr_s intr_attr; /* interrupt attr */ +}; + +#define BFA_IOCFC_PATHTOV_MAX 60 +#define BFA_IOCFC_QDEPTH_MAX 2000 + +#endif /* __BFA_DEFS_IOC_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_ipfc.h b/drivers/scsi/bfa/include/defs/bfa_defs_ipfc.h new file mode 100644 index 000000000000..7cb63ea98f38 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_ipfc.h @@ -0,0 +1,70 @@ +/* + * 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. + */ +#ifndef __BFA_DEFS_IPFC_H__ +#define __BFA_DEFS_IPFC_H__ + +#include +#include +#include + +/** + * FCS ip remote port states + */ +enum bfa_iprp_state { + BFA_IPRP_UNINIT = 0, /* PORT is not yet initialized */ + BFA_IPRP_ONLINE = 1, /* process login is complete */ + BFA_IPRP_OFFLINE = 2, /* iprp is offline */ +}; + +/** + * FCS remote port statistics + */ +struct bfa_iprp_stats_s { + u32 offlines; + u32 onlines; + u32 rscns; + u32 plogis; + u32 logos; + u32 plogi_timeouts; + u32 plogi_rejects; +}; + +/** + * FCS iprp attribute returned in queries + */ +struct bfa_iprp_attr_s { + enum bfa_iprp_state state; +}; + +struct bfa_ipfc_stats_s { + u32 arp_sent; + u32 arp_recv; + u32 arp_reply_sent; + u32 arp_reply_recv; + u32 farp_sent; + u32 farp_recv; + u32 farp_reply_sent; + u32 farp_reply_recv; + u32 farp_reject_sent; + u32 farp_reject_recv; +}; + +struct bfa_ipfc_attr_s { + bfa_boolean_t enabled; +}; + +#endif /* __BFA_DEFS_IPFC_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_itnim.h b/drivers/scsi/bfa/include/defs/bfa_defs_itnim.h new file mode 100644 index 000000000000..2ec769903d24 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_itnim.h @@ -0,0 +1,126 @@ +/* + * 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. + */ +#ifndef __BFA_DEFS_ITNIM_H__ +#define __BFA_DEFS_ITNIM_H__ + +#include +#include + +/** + * FCS itnim states + */ +enum bfa_itnim_state { + BFA_ITNIM_OFFLINE = 0, /* offline */ + BFA_ITNIM_PRLI_SEND = 1, /* prli send */ + BFA_ITNIM_PRLI_SENT = 2, /* prli sent */ + BFA_ITNIM_PRLI_RETRY = 3, /* prli retry */ + BFA_ITNIM_HCB_ONLINE = 4, /* online callback */ + BFA_ITNIM_ONLINE = 5, /* online */ + BFA_ITNIM_HCB_OFFLINE = 6, /* offline callback */ + BFA_ITNIM_INITIATIOR = 7, /* initiator */ +}; + +struct bfa_itnim_hal_stats_s { + u32 onlines; /* ITN nexus onlines (PRLI done) */ + u32 offlines; /* ITN Nexus offlines */ + u32 creates; /* ITN create requests */ + u32 deletes; /* ITN delete requests */ + u32 create_comps; /* ITN create completions */ + u32 delete_comps; /* ITN delete completions */ + u32 sler_events; /* SLER (sequence level error + * recovery) events */ + u32 ioc_disabled; /* Num IOC disables */ + u32 cleanup_comps; /* ITN cleanup completions */ + u32 tm_cmnds; /* task management(TM) cmnds sent */ + u32 tm_fw_rsps; /* TM cmds firmware responses */ + u32 tm_success; /* TM successes */ + u32 tm_failures; /* TM failures */ + u32 tm_io_comps; /* TM IO completions */ + u32 tm_qresumes; /* TM queue resumes (after waiting + * for resources) + */ + u32 tm_iocdowns; /* TM cmnds affected by IOC down */ + u32 tm_cleanups; /* TM cleanups */ + u32 tm_cleanup_comps; + /* TM cleanup completions */ + u32 ios; /* IO requests */ + u32 io_comps; /* IO completions */ + u64 input_reqs; /* INPUT requests */ + u64 output_reqs; /* OUTPUT requests */ +}; + +/** + * FCS remote port statistics + */ +struct bfa_itnim_stats_s { + u32 onlines; /* num rport online */ + u32 offlines; /* num rport offline */ + u32 prli_sent; /* num prli sent out */ + u32 fcxp_alloc_wait;/* num fcxp alloc waits */ + u32 prli_rsp_err; /* num prli rsp errors */ + u32 prli_rsp_acc; /* num prli rsp accepts */ + u32 initiator; /* rport is an initiator */ + u32 prli_rsp_parse_err; /* prli rsp parsing errors */ + u32 prli_rsp_rjt; /* num prli rsp rejects */ + u32 timeout; /* num timeouts detected */ + u32 sler; /* num sler notification from BFA */ + u32 rsvd; + struct bfa_itnim_hal_stats_s hal_stats; +}; + +/** + * FCS itnim attributes returned in queries + */ +struct bfa_itnim_attr_s { + enum bfa_itnim_state state; /* FCS itnim state */ + u8 retry; /* data retransmision support */ + u8 task_retry_id; /* task retry ident support */ + u8 rec_support; /* REC supported */ + u8 conf_comp; /* confirmed completion supp */ +}; + +/** + * BFA ITNIM events. + * Arguments below are in BFAL context from Mgmt + * BFA_ITNIM_AEN_NEW: [in]: None [out]: vf_id, lpwwn + * BFA_ITNIM_AEN_DELETE: [in]: vf_id, lpwwn, rpwwn (0 = all fcp4 targets), + * [out]: vf_id, ppwwn, lpwwn, rpwwn + * BFA_ITNIM_AEN_ONLINE: [in]: vf_id, lpwwn, rpwwn (0 = all fcp4 targets), + * [out]: vf_id, ppwwn, lpwwn, rpwwn + * BFA_ITNIM_AEN_OFFLINE: [in]: vf_id, lpwwn, rpwwn (0 = all fcp4 targets), + * [out]: vf_id, ppwwn, lpwwn, rpwwn + * BFA_ITNIM_AEN_DISCONNECT:[in]: vf_id, lpwwn, rpwwn (0 = all fcp4 targets), + * [out]: vf_id, ppwwn, lpwwn, rpwwn + */ +enum bfa_itnim_aen_event { + BFA_ITNIM_AEN_ONLINE = 1, /* Target online */ + BFA_ITNIM_AEN_OFFLINE = 2, /* Target offline */ + BFA_ITNIM_AEN_DISCONNECT = 3, /* Target disconnected */ +}; + +/** + * BFA ITNIM event data structure. + */ +struct bfa_itnim_aen_data_s { + u16 vf_id; /* vf_id of the IT nexus */ + u16 rsvd[3]; + wwn_t ppwwn; /* WWN of its physical port */ + wwn_t lpwwn; /* WWN of logical port */ + wwn_t rpwwn; /* WWN of remote(target) port */ +}; + +#endif /* __BFA_DEFS_ITNIM_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_led.h b/drivers/scsi/bfa/include/defs/bfa_defs_led.h new file mode 100644 index 000000000000..62039273264e --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_led.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_LED_H__ +#define __BFA_DEFS_LED_H__ + +#define BFA_LED_MAX_NUM 3 + +enum bfa_led_op { + BFA_LED_OFF = 0, + BFA_LED_ON = 1, + BFA_LED_FLICK = 2, + BFA_LED_BLINK = 3, +}; + +enum bfa_led_color { + BFA_LED_GREEN = 0, + BFA_LED_AMBER = 1, +}; + +#endif /* __BFA_DEFS_LED_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_lport.h b/drivers/scsi/bfa/include/defs/bfa_defs_lport.h new file mode 100644 index 000000000000..7359f82aacfc --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_lport.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_LPORT_H__ +#define __BFA_DEFS_LPORT_H__ + +#include +#include + +/** + * BFA AEN logical port events. + * Arguments below are in BFAL context from Mgmt + * BFA_LPORT_AEN_NEW: [in]: None [out]: vf_id, ppwwn, lpwwn, roles + * BFA_LPORT_AEN_DELETE: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_ONLINE: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_OFFLINE: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_DISCONNECT:[in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_NEW_PROP: [in]: None [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_DELETE_PROP: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_NEW_STANDARD: [in]: None [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_DELETE_STANDARD: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_NPIV_DUP_WWN: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_NPIV_FABRIC_MAX: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + * BFA_LPORT_AEN_NPIV_UNKNOWN: [in]: lpwwn [out]: vf_id, ppwwn. lpwwn, roles + */ +enum bfa_lport_aen_event { + BFA_LPORT_AEN_NEW = 1, /* LPort created event */ + BFA_LPORT_AEN_DELETE = 2, /* LPort deleted event */ + BFA_LPORT_AEN_ONLINE = 3, /* LPort online event */ + BFA_LPORT_AEN_OFFLINE = 4, /* LPort offline event */ + BFA_LPORT_AEN_DISCONNECT = 5, /* LPort disconnect event */ + BFA_LPORT_AEN_NEW_PROP = 6, /* VPort created event */ + BFA_LPORT_AEN_DELETE_PROP = 7, /* VPort deleted event */ + BFA_LPORT_AEN_NEW_STANDARD = 8, /* VPort created event */ + BFA_LPORT_AEN_DELETE_STANDARD = 9, /* VPort deleted event */ + BFA_LPORT_AEN_NPIV_DUP_WWN = 10, /* VPort configured with + * duplicate WWN event + */ + BFA_LPORT_AEN_NPIV_FABRIC_MAX = 11, /* Max NPIV in fabric/fport */ + BFA_LPORT_AEN_NPIV_UNKNOWN = 12, /* Unknown NPIV Error code event */ +}; + +/** + * BFA AEN event data structure + */ +struct bfa_lport_aen_data_s { + u16 vf_id; /* vf_id of this logical port */ + u16 rsvd; + enum bfa_port_role roles; /* Logical port mode,IM/TM/IP etc */ + wwn_t ppwwn; /* WWN of its physical port */ + wwn_t lpwwn; /* WWN of this logical port */ +}; + +#endif /* __BFA_DEFS_LPORT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_mfg.h b/drivers/scsi/bfa/include/defs/bfa_defs_mfg.h new file mode 100644 index 000000000000..13fd4ab6aae2 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_mfg.h @@ -0,0 +1,58 @@ +/* + * 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. + */ +#ifndef __BFA_DEFS_MFG_H__ +#define __BFA_DEFS_MFG_H__ + +#include + +/** + * Manufacturing block version + */ +#define BFA_MFG_VERSION 1 + +/** + * Manufacturing block format + */ +#define BFA_MFG_SERIALNUM_SIZE 11 +#define BFA_MFG_PARTNUM_SIZE 14 +#define BFA_MFG_SUPPLIER_ID_SIZE 10 +#define BFA_MFG_SUPPLIER_PARTNUM_SIZE 20 +#define BFA_MFG_SUPPLIER_SERIALNUM_SIZE 20 +#define BFA_MFG_SUPPLIER_REVISION_SIZE 4 +#define STRSZ(_n) (((_n) + 4) & ~3) + +/** + * VPD data length + */ +#define BFA_MFG_VPD_LEN 256 + +/** + * All numerical fields are in big-endian format. + */ +struct bfa_mfg_vpd_s { + u8 version; /* vpd data version */ + u8 vpd_sig[3]; /* characters 'V', 'P', 'D' */ + u8 chksum; /* u8 checksum */ + u8 vendor; /* vendor */ + u8 len; /* vpd data length excluding header */ + u8 rsv; + u8 data[BFA_MFG_VPD_LEN]; /* vpd data */ +}; + +#pragma pack(1) + +#endif /* __BFA_DEFS_MFG_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_pci.h b/drivers/scsi/bfa/include/defs/bfa_defs_pci.h new file mode 100644 index 000000000000..c9b83321694b --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_pci.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_PCI_H__ +#define __BFA_DEFS_PCI_H__ + +/** + * PCI device and vendor ID information + */ +enum { + BFA_PCI_VENDOR_ID_BROCADE = 0x1657, + BFA_PCI_DEVICE_ID_FC_8G2P = 0x13, + BFA_PCI_DEVICE_ID_FC_8G1P = 0x17, + BFA_PCI_DEVICE_ID_CT = 0x14, +}; + +/** + * PCI sub-system device and vendor ID information + */ +enum { + BFA_PCI_FCOE_SSDEVICE_ID = 0x14, +}; + +#define BFA_PCI_ACCESS_RANGES 1 /* Maximum number of device address ranges + * mapped through different BAR(s). */ + +#endif /* __BFA_DEFS_PCI_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_pm.h b/drivers/scsi/bfa/include/defs/bfa_defs_pm.h new file mode 100644 index 000000000000..e8d6d959006e --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_pm.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_PM_H__ +#define __BFA_DEFS_PM_H__ + +#include + +/** + * BFA power management device states + */ +enum bfa_pm_ds { + BFA_PM_DS_D0 = 0, /* full power mode */ + BFA_PM_DS_D1 = 1, /* power save state 1 */ + BFA_PM_DS_D2 = 2, /* power save state 2 */ + BFA_PM_DS_D3 = 3, /* power off state */ +}; + +#endif /* __BFA_DEFS_PM_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_pom.h b/drivers/scsi/bfa/include/defs/bfa_defs_pom.h new file mode 100644 index 000000000000..d9fa278472b7 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_pom.h @@ -0,0 +1,56 @@ +/* + * 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. + */ +#ifndef __BFA_DEFS_POM_H__ +#define __BFA_DEFS_POM_H__ + +#include +#include + +/** + * POM health status levels for each attributes. + */ +enum bfa_pom_entry_health { + BFA_POM_HEALTH_NOINFO = 1, /* no information */ + BFA_POM_HEALTH_NORMAL = 2, /* health is normal */ + BFA_POM_HEALTH_WARNING = 3, /* warning level */ + BFA_POM_HEALTH_ALARM = 4, /* alarming level */ +}; + +/** + * Reading of temperature/voltage/current/power + */ +struct bfa_pom_entry_s { + enum bfa_pom_entry_health health; /* POM entry health */ + u32 curr_value; /* current value */ + u32 thr_warn_high; /* threshold warning high */ + u32 thr_warn_low; /* threshold warning low */ + u32 thr_alarm_low; /* threshold alaram low */ + u32 thr_alarm_high; /* threshold alarm high */ +}; + +/** + * POM attributes + */ +struct bfa_pom_attr_s { + struct bfa_pom_entry_s temperature; /* centigrade */ + struct bfa_pom_entry_s voltage; /* volts */ + struct bfa_pom_entry_s curr; /* milli amps */ + struct bfa_pom_entry_s txpower; /* micro watts */ + struct bfa_pom_entry_s rxpower; /* micro watts */ +}; + +#endif /* __BFA_DEFS_POM_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_port.h b/drivers/scsi/bfa/include/defs/bfa_defs_port.h new file mode 100644 index 000000000000..de0696c81bc4 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_port.h @@ -0,0 +1,245 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_PORT_H__ +#define __BFA_DEFS_PORT_H__ + +#include +#include +#include +#include + +#define BFA_FCS_FABRIC_IPADDR_SZ 16 + +/** + * symbolic names for base port/virtual port + */ +#define BFA_SYMNAME_MAXLEN 128 /* vmware/windows uses 128 bytes */ +struct bfa_port_symname_s { + char symname[BFA_SYMNAME_MAXLEN]; +}; + +/** +* Roles of FCS port: + * - FCP IM and FCP TM roles cannot be enabled together for a FCS port + * - Create multiple ports if both IM and TM functions required. + * - Atleast one role must be specified. + */ +enum bfa_port_role { + BFA_PORT_ROLE_FCP_IM = 0x01, /* FCP initiator role */ + BFA_PORT_ROLE_FCP_TM = 0x02, /* FCP target role */ + BFA_PORT_ROLE_FCP_IPFC = 0x04, /* IP over FC role */ + BFA_PORT_ROLE_FCP_MAX = BFA_PORT_ROLE_FCP_IPFC | BFA_PORT_ROLE_FCP_IM +}; + +/** + * FCS port configuration. + */ +struct bfa_port_cfg_s { + wwn_t pwwn; /* port wwn */ + wwn_t nwwn; /* node wwn */ + struct bfa_port_symname_s sym_name; /* vm port symbolic name */ + enum bfa_port_role roles; /* FCS port roles */ + u32 rsvd; + u8 tag[16]; /* opaque tag from application */ +}; + +/** + * FCS port states + */ +enum bfa_port_state { + BFA_PORT_UNINIT = 0, /* PORT is not yet initialized */ + BFA_PORT_FDISC = 1, /* FDISC is in progress */ + BFA_PORT_ONLINE = 2, /* login to fabric is complete */ + BFA_PORT_OFFLINE = 3, /* No login to fabric */ +}; + +/** + * FCS port type. Required for VmWare. + */ +enum bfa_port_type { + BFA_PORT_TYPE_PHYSICAL = 0, + BFA_PORT_TYPE_VIRTUAL, +}; + +/** + * FCS port offline reason. Required for VmWare. + */ +enum bfa_port_offline_reason { + BFA_PORT_OFFLINE_UNKNOWN = 0, + BFA_PORT_OFFLINE_LINKDOWN, + BFA_PORT_OFFLINE_FAB_UNSUPPORTED, /* NPIV not supported by the + * fabric */ + BFA_PORT_OFFLINE_FAB_NORESOURCES, + BFA_PORT_OFFLINE_FAB_LOGOUT, +}; + +/** + * FCS lport info. Required for VmWare. + */ +struct bfa_port_info_s { + u8 port_type; /* bfa_port_type_t : physical or + * virtual */ + u8 port_state; /* one of bfa_port_state values */ + u8 offline_reason; /* one of bfa_port_offline_reason_t + * values */ + wwn_t port_wwn; + wwn_t node_wwn; + + /* + * following 4 feilds are valid for Physical Ports only + */ + u32 max_vports_supp; /* Max supported vports */ + u32 num_vports_inuse; /* Num of in use vports */ + u32 max_rports_supp; /* Max supported rports */ + u32 num_rports_inuse; /* Num of doscovered rports */ + +}; + +/** + * FCS port statistics + */ +struct bfa_port_stats_s { + u32 ns_plogi_sent; + u32 ns_plogi_rsp_err; + u32 ns_plogi_acc_err; + u32 ns_plogi_accepts; + u32 ns_rejects; /* NS command rejects */ + u32 ns_plogi_unknown_rsp; + u32 ns_plogi_alloc_wait; + + u32 ns_retries; /* NS command retries */ + u32 ns_timeouts; /* NS command timeouts */ + + u32 ns_rspnid_sent; + u32 ns_rspnid_accepts; + u32 ns_rspnid_rsp_err; + u32 ns_rspnid_rejects; + u32 ns_rspnid_alloc_wait; + + u32 ns_rftid_sent; + u32 ns_rftid_accepts; + u32 ns_rftid_rsp_err; + u32 ns_rftid_rejects; + u32 ns_rftid_alloc_wait; + + u32 ns_rffid_sent; + u32 ns_rffid_accepts; + u32 ns_rffid_rsp_err; + u32 ns_rffid_rejects; + u32 ns_rffid_alloc_wait; + + u32 ns_gidft_sent; + u32 ns_gidft_accepts; + u32 ns_gidft_rsp_err; + u32 ns_gidft_rejects; + u32 ns_gidft_unknown_rsp; + u32 ns_gidft_alloc_wait; + + /* + * Mgmt Server stats + */ + u32 ms_retries; /* MS command retries */ + u32 ms_timeouts; /* MS command timeouts */ + u32 ms_plogi_sent; + u32 ms_plogi_rsp_err; + u32 ms_plogi_acc_err; + u32 ms_plogi_accepts; + u32 ms_rejects; /* NS command rejects */ + u32 ms_plogi_unknown_rsp; + u32 ms_plogi_alloc_wait; + + u32 num_rscn; /* Num of RSCN received */ + u32 num_portid_rscn;/* Num portid format RSCN + * received */ + + u32 uf_recvs; /* unsolicited recv frames */ + u32 uf_recv_drops; /* dropped received frames */ + + u32 rsvd; /* padding for 64 bit alignment */ +}; + +/** + * BFA port attribute returned in queries + */ +struct bfa_port_attr_s { + enum bfa_port_state state; /* port state */ + u32 pid; /* port ID */ + struct bfa_port_cfg_s port_cfg; /* port configuration */ + enum bfa_pport_type port_type; /* current topology */ + u32 loopback; /* cable is externally looped back */ + wwn_t fabric_name; /* attached switch's nwwn */ + u8 fabric_ip_addr[BFA_FCS_FABRIC_IPADDR_SZ]; /* attached + * fabric's ip addr */ +}; + +/** + * BFA physical port Level events + * Arguments below are in BFAL context from Mgmt + * BFA_PORT_AEN_ONLINE: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_OFFLINE: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_RLIR: [in]: None [out]: pwwn, rlir_data, rlir_len + * BFA_PORT_AEN_SFP_INSERT: [in]: pwwn [out]: port_id, pwwn + * BFA_PORT_AEN_SFP_REMOVE: [in]: pwwn [out]: port_id, pwwn + * BFA_PORT_AEN_SFP_POM: [in]: pwwn [out]: level, port_id, pwwn + * BFA_PORT_AEN_ENABLE: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_DISABLE: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_AUTH_ON: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_AUTH_OFF: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_DISCONNECT: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_QOS_NEG: [in]: pwwn [out]: pwwn + * BFA_PORT_AEN_FABRIC_NAME_CHANGE: [in]: pwwn, [out]: pwwn, fwwn + * + */ +enum bfa_port_aen_event { + BFA_PORT_AEN_ONLINE = 1, /* Physical Port online event */ + BFA_PORT_AEN_OFFLINE = 2, /* Physical Port offline event */ + BFA_PORT_AEN_RLIR = 3, /* RLIR event, not supported */ + BFA_PORT_AEN_SFP_INSERT = 4, /* SFP inserted event */ + BFA_PORT_AEN_SFP_REMOVE = 5, /* SFP removed event */ + BFA_PORT_AEN_SFP_POM = 6, /* SFP POM event */ + BFA_PORT_AEN_ENABLE = 7, /* Physical Port enable event */ + BFA_PORT_AEN_DISABLE = 8, /* Physical Port disable event */ + BFA_PORT_AEN_AUTH_ON = 9, /* Physical Port auth success event */ + BFA_PORT_AEN_AUTH_OFF = 10, /* Physical Port auth fail event */ + BFA_PORT_AEN_DISCONNECT = 11, /* Physical Port disconnect event */ + BFA_PORT_AEN_QOS_NEG = 12, /* Base Port QOS negotiation event */ + BFA_PORT_AEN_FABRIC_NAME_CHANGE = 13, /* Fabric Name/WWN change + * event */ + BFA_PORT_AEN_SFP_ACCESS_ERROR = 14, /* SFP read error event */ + BFA_PORT_AEN_SFP_UNSUPPORT = 15, /* Unsupported SFP event */ +}; + +enum bfa_port_aen_sfp_pom { + BFA_PORT_AEN_SFP_POM_GREEN = 1, /* Normal */ + BFA_PORT_AEN_SFP_POM_AMBER = 2, /* Warning */ + BFA_PORT_AEN_SFP_POM_RED = 3, /* Critical */ + BFA_PORT_AEN_SFP_POM_MAX = BFA_PORT_AEN_SFP_POM_RED +}; + +struct bfa_port_aen_data_s { + enum bfa_ioc_type_e ioc_type; + wwn_t pwwn; /* WWN of the physical port */ + wwn_t fwwn; /* WWN of the fabric port */ + mac_t mac; /* MAC addres of the ethernet port, + * applicable to CNA port only */ + int phy_port_num; /*! For SFP related events */ + enum bfa_port_aen_sfp_pom level; /* Only transitions will + * be informed */ +}; + +#endif /* __BFA_DEFS_PORT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_pport.h b/drivers/scsi/bfa/include/defs/bfa_defs_pport.h new file mode 100644 index 000000000000..a000bc4e2d4a --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_pport.h @@ -0,0 +1,383 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_PPORT_H__ +#define __BFA_DEFS_PPORT_H__ + +#include +#include +#include +#include +#include + +/* Modify char* port_stt[] in bfal_port.c if a new state was added */ +enum bfa_pport_states { + BFA_PPORT_ST_UNINIT = 1, + BFA_PPORT_ST_ENABLING_QWAIT = 2, + BFA_PPORT_ST_ENABLING = 3, + BFA_PPORT_ST_LINKDOWN = 4, + BFA_PPORT_ST_LINKUP = 5, + BFA_PPORT_ST_DISABLING_QWAIT = 6, + BFA_PPORT_ST_DISABLING = 7, + BFA_PPORT_ST_DISABLED = 8, + BFA_PPORT_ST_STOPPED = 9, + BFA_PPORT_ST_IOCDOWN = 10, + BFA_PPORT_ST_IOCDIS = 11, + BFA_PPORT_ST_FWMISMATCH = 12, + BFA_PPORT_ST_MAX_STATE, +}; + +/** + * Port speed settings. Each specific speed is a bit field. Use multiple + * bits to specify speeds to be selected for auto-negotiation. + */ +enum bfa_pport_speed { + BFA_PPORT_SPEED_UNKNOWN = 0, + BFA_PPORT_SPEED_1GBPS = 1, + BFA_PPORT_SPEED_2GBPS = 2, + BFA_PPORT_SPEED_4GBPS = 4, + BFA_PPORT_SPEED_8GBPS = 8, + BFA_PPORT_SPEED_10GBPS = 10, + BFA_PPORT_SPEED_AUTO = + (BFA_PPORT_SPEED_1GBPS | BFA_PPORT_SPEED_2GBPS | + BFA_PPORT_SPEED_4GBPS | BFA_PPORT_SPEED_8GBPS), +}; + +/** + * Port operational type (in sync with SNIA port type). + */ +enum bfa_pport_type { + BFA_PPORT_TYPE_UNKNOWN = 1, /* port type is unkown */ + BFA_PPORT_TYPE_TRUNKED = 2, /* Trunked mode */ + BFA_PPORT_TYPE_NPORT = 5, /* P2P with switched fabric */ + BFA_PPORT_TYPE_NLPORT = 6, /* public loop */ + BFA_PPORT_TYPE_LPORT = 20, /* private loop */ + BFA_PPORT_TYPE_P2P = 21, /* P2P with no switched fabric */ + BFA_PPORT_TYPE_VPORT = 22, /* NPIV - virtual port */ +}; + +/** + * Port topology setting. A port's topology and fabric login status + * determine its operational type. + */ +enum bfa_pport_topology { + BFA_PPORT_TOPOLOGY_NONE = 0, /* No valid topology */ + BFA_PPORT_TOPOLOGY_P2P = 1, /* P2P only */ + BFA_PPORT_TOPOLOGY_LOOP = 2, /* LOOP topology */ + BFA_PPORT_TOPOLOGY_AUTO = 3, /* auto topology selection */ +}; + +/** + * Physical port loopback types. + */ +enum bfa_pport_opmode { + BFA_PPORT_OPMODE_NORMAL = 0x00, /* normal non-loopback mode */ + BFA_PPORT_OPMODE_LB_INT = 0x01, /* internal loop back */ + BFA_PPORT_OPMODE_LB_SLW = 0x02, /* serial link wrapback (serdes) */ + BFA_PPORT_OPMODE_LB_EXT = 0x04, /* external loop back (serdes) */ + BFA_PPORT_OPMODE_LB_CBL = 0x08, /* cabled loop back */ + BFA_PPORT_OPMODE_LB_NLINT = 0x20, /* NL_Port internal loopback */ +}; + +#define BFA_PPORT_OPMODE_LB_HARD(_mode) \ + ((_mode == BFA_PPORT_OPMODE_LB_INT) || \ + (_mode == BFA_PPORT_OPMODE_LB_SLW) || \ + (_mode == BFA_PPORT_OPMODE_LB_EXT)) + +/** + Port State (in sync with SNIA port state). + */ +enum bfa_pport_snia_state { + BFA_PPORT_STATE_UNKNOWN = 1, /* port is not initialized */ + BFA_PPORT_STATE_ONLINE = 2, /* port is ONLINE */ + BFA_PPORT_STATE_DISABLED = 3, /* port is disabled by user */ + BFA_PPORT_STATE_BYPASSED = 4, /* port is bypassed (in LOOP) */ + BFA_PPORT_STATE_DIAG = 5, /* port diagnostics is active */ + BFA_PPORT_STATE_LINKDOWN = 6, /* link is down */ + BFA_PPORT_STATE_LOOPBACK = 8, /* port is looped back */ +}; + +/** + * Port link state + */ +enum bfa_pport_linkstate { + BFA_PPORT_LINKUP = 1, /* Physical port/Trunk link up */ + BFA_PPORT_LINKDOWN = 2, /* Physical port/Trunk link down */ + BFA_PPORT_TRUNK_LINKDOWN = 3, /* Trunk link down (new tmaster) */ +}; + +/** + * Port link state event + */ +#define bfa_pport_event_t enum bfa_pport_linkstate + +/** + * Port link state reason code + */ +enum bfa_pport_linkstate_rsn { + BFA_PPORT_LINKSTATE_RSN_NONE = 0, + BFA_PPORT_LINKSTATE_RSN_DISABLED = 1, + BFA_PPORT_LINKSTATE_RSN_RX_NOS = 2, + BFA_PPORT_LINKSTATE_RSN_RX_OLS = 3, + BFA_PPORT_LINKSTATE_RSN_RX_LIP = 4, + BFA_PPORT_LINKSTATE_RSN_RX_LIPF7 = 5, + BFA_PPORT_LINKSTATE_RSN_SFP_REMOVED = 6, + BFA_PPORT_LINKSTATE_RSN_PORT_FAULT = 7, + BFA_PPORT_LINKSTATE_RSN_RX_LOS = 8, + BFA_PPORT_LINKSTATE_RSN_LOCAL_FAULT = 9, + BFA_PPORT_LINKSTATE_RSN_REMOTE_FAULT = 10, + BFA_PPORT_LINKSTATE_RSN_TIMEOUT = 11, + + + + /* CEE related reason codes/errors */ + CEE_LLDP_INFO_AGED_OUT = 20, + CEE_LLDP_SHUTDOWN_TLV_RCVD = 21, + CEE_PEER_NOT_ADVERTISE_DCBX = 22, + CEE_PEER_NOT_ADVERTISE_PG = 23, + CEE_PEER_NOT_ADVERTISE_PFC = 24, + CEE_PEER_NOT_ADVERTISE_FCOE = 25, + CEE_PG_NOT_COMPATIBLE = 26, + CEE_PFC_NOT_COMPATIBLE = 27, + CEE_FCOE_NOT_COMPATIBLE = 28, + CEE_BAD_PG_RCVD = 29, + CEE_BAD_BW_RCVD = 30, + CEE_BAD_PFC_RCVD = 31, + CEE_BAD_FCOE_PRI_RCVD = 32, + CEE_FCOE_PRI_PFC_OFF = 33, + CEE_DUP_CONTROL_TLV_RCVD = 34, + CEE_DUP_FEAT_TLV_RCVD = 35, + CEE_APPLY_NEW_CFG = 36, /* reason, not an error */ + CEE_PROTOCOL_INIT = 37, /* reason, not an error */ + CEE_PHY_LINK_DOWN = 38, + CEE_LLS_FCOE_ABSENT = 39, + CEE_LLS_FCOE_DOWN = 40 +}; + +/** + * Default Target Rate Limiting Speed. + */ +#define BFA_PPORT_DEF_TRL_SPEED BFA_PPORT_SPEED_1GBPS + +/** + * Physical port configuration + */ +struct bfa_pport_cfg_s { + u8 topology; /* bfa_pport_topology */ + u8 speed; /* enum bfa_pport_speed */ + u8 trunked; /* trunked or not */ + u8 qos_enabled; /* qos enabled or not */ + u8 trunk_ports; /* bitmap of trunked ports */ + u8 cfg_hardalpa; /* is hard alpa configured */ + u16 maxfrsize; /* maximum frame size */ + u8 hardalpa; /* configured hard alpa */ + u8 rx_bbcredit; /* receive buffer credits */ + u8 tx_bbcredit; /* transmit buffer credits */ + u8 ratelimit; /* ratelimit enabled or not */ + u8 trl_def_speed; /* ratelimit default speed */ + u8 rsvd[3]; + u16 path_tov; /* device path timeout */ + u16 q_depth; /* SCSI Queue depth */ +}; + +/** + * Port attribute values. + */ +struct bfa_pport_attr_s { + /* + * Static fields + */ + wwn_t nwwn; /* node wwn */ + wwn_t pwwn; /* port wwn */ + enum fc_cos cos_supported; /* supported class of services */ + u32 rsvd; + struct fc_symname_s port_symname; /* port symbolic name */ + enum bfa_pport_speed speed_supported; /* supported speeds */ + bfa_boolean_t pbind_enabled; /* Will be set if Persistent binding + * enabled. Relevant only in Windows + */ + + /* + * Configured values + */ + struct bfa_pport_cfg_s pport_cfg; /* pport cfg */ + + /* + * Dynamic field - info from BFA + */ + enum bfa_pport_states port_state; /* current port state */ + enum bfa_pport_speed speed; /* current speed */ + enum bfa_pport_topology topology; /* current topology */ + bfa_boolean_t beacon; /* current beacon status */ + bfa_boolean_t link_e2e_beacon;/* set if link beacon on */ + bfa_boolean_t plog_enabled; /* set if portlog is enabled*/ + + /* + * Dynamic field - info from FCS + */ + u32 pid; /* port ID */ + enum bfa_pport_type port_type; /* current topology */ + u32 loopback; /* external loopback */ + u32 rsvd1; + u32 rsvd2; /* padding for 64 bit */ +}; + +/** + * FC Port statistics. + */ +struct bfa_pport_fc_stats_s { + u64 secs_reset; /* seconds since stats is reset */ + u64 tx_frames; /* transmitted frames */ + u64 tx_words; /* transmitted words */ + u64 rx_frames; /* received frames */ + u64 rx_words; /* received words */ + u64 lip_count; /* LIPs seen */ + u64 nos_count; /* NOS count */ + u64 error_frames; /* errored frames (sent?) */ + u64 dropped_frames; /* dropped frames */ + u64 link_failures; /* link failure count */ + u64 loss_of_syncs; /* loss of sync count */ + u64 loss_of_signals;/* loss of signal count */ + u64 primseq_errs; /* primitive sequence protocol */ + u64 bad_os_count; /* invalid ordered set */ + u64 err_enc_out; /* Encoding error outside frame */ + u64 invalid_crcs; /* frames received with invalid CRC*/ + u64 undersized_frm; /* undersized frames */ + u64 oversized_frm; /* oversized frames */ + u64 bad_eof_frm; /* frames with bad EOF */ + struct bfa_qos_stats_s qos_stats; /* QoS statistics */ +}; + +/** + * Eth Port statistics. + */ +struct bfa_pport_eth_stats_s { + u64 secs_reset; /* seconds since stats is reset */ + u64 frame_64; /* both rx and tx counter */ + u64 frame_65_127; /* both rx and tx counter */ + u64 frame_128_255; /* both rx and tx counter */ + u64 frame_256_511; /* both rx and tx counter */ + u64 frame_512_1023; /* both rx and tx counter */ + u64 frame_1024_1518; /* both rx and tx counter */ + u64 frame_1519_1522; /* both rx and tx counter */ + + u64 tx_bytes; + u64 tx_packets; + u64 tx_mcast_packets; + u64 tx_bcast_packets; + u64 tx_control_frame; + u64 tx_drop; + u64 tx_jabber; + u64 tx_fcs_error; + u64 tx_fragments; + + u64 rx_bytes; + u64 rx_packets; + u64 rx_mcast_packets; + u64 rx_bcast_packets; + u64 rx_control_frames; + u64 rx_unknown_opcode; + u64 rx_drop; + u64 rx_jabber; + u64 rx_fcs_error; + u64 rx_alignment_error; + u64 rx_frame_length_error; + u64 rx_code_error; + u64 rx_fragments; + + u64 rx_pause; /* BPC */ + u64 rx_zero_pause; /* BPC Pause cancellation */ + u64 tx_pause; /* BPC */ + u64 tx_zero_pause; /* BPC Pause cancellation */ + u64 rx_fcoe_pause; /* BPC */ + u64 rx_fcoe_zero_pause; /* BPC Pause cancellation */ + u64 tx_fcoe_pause; /* BPC */ + u64 tx_fcoe_zero_pause; /* BPC Pause cancellation */ +}; + +/** + * Port statistics. + */ +union bfa_pport_stats_u { + struct bfa_pport_fc_stats_s fc; + struct bfa_pport_eth_stats_s eth; +}; + +/** + * Port FCP mappings. + */ +struct bfa_pport_fcpmap_s { + char osdevname[256]; + u32 bus; + u32 target; + u32 oslun; + u32 fcid; + wwn_t nwwn; + wwn_t pwwn; + u64 fcplun; + char luid[256]; +}; + +/** + * Port RNID info. + */ +struct bfa_pport_rnid_s { + wwn_t wwn; + u32 unittype; + u32 portid; + u32 attached_nodes_num; + u16 ip_version; + u16 udp_port; + u8 ipaddr[16]; + u16 rsvd; + u16 topologydiscoveryflags; +}; + +/** + * Link state information + */ +struct bfa_pport_link_s { + u8 linkstate; /* Link state bfa_pport_linkstate */ + u8 linkstate_rsn; /* bfa_pport_linkstate_rsn_t */ + u8 topology; /* P2P/LOOP bfa_pport_topology */ + u8 speed; /* Link speed (1/2/4/8 G) */ + u32 linkstate_opt; /* Linkstate optional data (debug) */ + u8 trunked; /* Trunked or not (1 or 0) */ + u8 resvd[3]; + struct bfa_qos_attr_s qos_attr; /* QoS Attributes */ + struct bfa_qos_vc_attr_s qos_vc_attr; /* VC info from ELP */ + union { + struct { + u8 tmaster;/* Trunk Master or + * not (1 or 0) */ + u8 tlinks; /* Trunk links bitmap + * (linkup) */ + u8 resv1; /* Reserved */ + } trunk_info; + + struct { + u8 myalpa; /* alpa claimed */ + u8 login_req; /* Login required or + * not (1 or 0) */ + u8 alpabm_val;/* alpa bitmap valid + * or not (1 or 0) */ + struct fc_alpabm_s alpabm; /* alpa bitmap */ + } loop_info; + } tl; +}; + +#endif /* __BFA_DEFS_PPORT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_qos.h b/drivers/scsi/bfa/include/defs/bfa_defs_qos.h new file mode 100644 index 000000000000..aadbacd1d2d7 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_qos.h @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_QOS_H__ +#define __BFA_DEFS_QOS_H__ + +/** + * QoS states + */ +enum bfa_qos_state { + BFA_QOS_ONLINE = 1, /* QoS is online */ + BFA_QOS_OFFLINE = 2, /* QoS is offline */ +}; + + +/** + * QoS Priority levels. + */ +enum bfa_qos_priority { + BFA_QOS_UNKNOWN = 0, + BFA_QOS_HIGH = 1, /* QoS Priority Level High */ + BFA_QOS_MED = 2, /* QoS Priority Level Medium */ + BFA_QOS_LOW = 3, /* QoS Priority Level Low */ +}; + + +/** + * QoS bandwidth allocation for each priority level + */ +enum bfa_qos_bw_alloc { + BFA_QOS_BW_HIGH = 60, /* bandwidth allocation for High */ + BFA_QOS_BW_MED = 30, /* bandwidth allocation for Medium */ + BFA_QOS_BW_LOW = 10, /* bandwidth allocation for Low */ +}; + +/** + * QoS attribute returned in QoS Query + */ +struct bfa_qos_attr_s { + enum bfa_qos_state state; /* QoS current state */ + u32 total_bb_cr; /* Total BB Credits */ +}; + +/** + * These fields should be displayed only from the CLI. + * There will be a separate BFAL API (get_qos_vc_attr ?) + * to retrieve this. + * + */ +#define BFA_QOS_MAX_VC 16 + +struct bfa_qos_vc_info_s { + u8 vc_credit; + u8 borrow_credit; + u8 priority; + u8 resvd; +}; + +struct bfa_qos_vc_attr_s { + u16 total_vc_count; /* Total VC Count */ + u16 shared_credit; + u32 elp_opmode_flags; + struct bfa_qos_vc_info_s vc_info[BFA_QOS_MAX_VC]; /* as many as + * total_vc_count */ +}; + +/** + * QoS statistics + */ +struct bfa_qos_stats_s { + u32 flogi_sent; /* QoS Flogi sent */ + u32 flogi_acc_recvd; /* QoS Flogi Acc received */ + u32 flogi_rjt_recvd; /* QoS Flogi rejects received */ + u32 flogi_retries; /* QoS Flogi retries */ + + u32 elp_recvd; /* QoS ELP received */ + u32 elp_accepted; /* QoS ELP Accepted */ + u32 elp_rejected; /* QoS ELP rejected */ + u32 elp_dropped; /* QoS ELP dropped */ + + u32 qos_rscn_recvd; /* QoS RSCN received */ + u32 rsvd; /* padding for 64 bit alignment */ +}; + +#endif /* __BFA_DEFS_QOS_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_rport.h b/drivers/scsi/bfa/include/defs/bfa_defs_rport.h new file mode 100644 index 000000000000..e0af59d6d2f6 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_rport.h @@ -0,0 +1,199 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_RPORT_H__ +#define __BFA_DEFS_RPORT_H__ + +#include +#include +#include +#include +#include + +/** + * FCS remote port states + */ +enum bfa_rport_state { + BFA_RPORT_UNINIT = 0, /* PORT is not yet initialized */ + BFA_RPORT_OFFLINE = 1, /* rport is offline */ + BFA_RPORT_PLOGI = 2, /* PLOGI to rport is in progress */ + BFA_RPORT_ONLINE = 3, /* login to rport is complete */ + BFA_RPORT_PLOGI_RETRY = 4, /* retrying login to rport */ + BFA_RPORT_NSQUERY = 5, /* nameserver query */ + BFA_RPORT_ADISC = 6, /* ADISC authentication */ + BFA_RPORT_LOGO = 7, /* logging out with rport */ + BFA_RPORT_LOGORCV = 8, /* handling LOGO from rport */ + BFA_RPORT_NSDISC = 9, /* re-discover rport */ +}; + +/** + * Rport Scsi Function : Initiator/Target. + */ +enum bfa_rport_function { + BFA_RPORT_INITIATOR = 0x01, /* SCSI Initiator */ + BFA_RPORT_TARGET = 0x02, /* SCSI Target */ +}; + +/** + * port/node symbolic names for rport + */ +#define BFA_RPORT_SYMNAME_MAXLEN 255 +struct bfa_rport_symname_s { + char symname[BFA_RPORT_SYMNAME_MAXLEN]; +}; + +struct bfa_rport_hal_stats_s { + u32 sm_un_cr; /* uninit: create events */ + u32 sm_un_unexp; /* uninit: exception events */ + u32 sm_cr_on; /* created: online events */ + u32 sm_cr_del; /* created: delete events */ + u32 sm_cr_hwf; /* created: IOC down */ + u32 sm_cr_unexp; /* created: exception events */ + u32 sm_fwc_rsp; /* fw create: f/w responses */ + u32 sm_fwc_del; /* fw create: delete events */ + u32 sm_fwc_off; /* fw create: offline events */ + u32 sm_fwc_hwf; /* fw create: IOC down */ + u32 sm_fwc_unexp; /* fw create: exception events*/ + u32 sm_on_off; /* online: offline events */ + u32 sm_on_del; /* online: delete events */ + u32 sm_on_hwf; /* online: IOC down events */ + u32 sm_on_unexp; /* online: exception events */ + u32 sm_fwd_rsp; /* fw delete: fw responses */ + u32 sm_fwd_del; /* fw delete: delete events */ + u32 sm_fwd_hwf; /* fw delete: IOC down events */ + u32 sm_fwd_unexp; /* fw delete: exception events*/ + u32 sm_off_del; /* offline: delete events */ + u32 sm_off_on; /* offline: online events */ + u32 sm_off_hwf; /* offline: IOC down events */ + u32 sm_off_unexp; /* offline: exception events */ + u32 sm_del_fwrsp; /* delete: fw responses */ + u32 sm_del_hwf; /* delete: IOC down events */ + u32 sm_del_unexp; /* delete: exception events */ + u32 sm_delp_fwrsp; /* delete pend: fw responses */ + u32 sm_delp_hwf; /* delete pend: IOC downs */ + u32 sm_delp_unexp; /* delete pend: exceptions */ + u32 sm_offp_fwrsp; /* off-pending: fw responses */ + u32 sm_offp_del; /* off-pending: deletes */ + u32 sm_offp_hwf; /* off-pending: IOC downs */ + u32 sm_offp_unexp; /* off-pending: exceptions */ + u32 sm_iocd_off; /* IOC down: offline events */ + u32 sm_iocd_del; /* IOC down: delete events */ + u32 sm_iocd_on; /* IOC down: online events */ + u32 sm_iocd_unexp; /* IOC down: exceptions */ + u32 rsvd; +}; + +/** + * FCS remote port statistics + */ +struct bfa_rport_stats_s { + u32 offlines; /* remote port offline count */ + u32 onlines; /* remote port online count */ + u32 rscns; /* RSCN affecting rport */ + u32 plogis; /* plogis sent */ + u32 plogi_accs; /* plogi accepts */ + u32 plogi_timeouts; /* plogi timeouts */ + u32 plogi_rejects; /* rcvd plogi rejects */ + u32 plogi_failed; /* local failure */ + u32 plogi_rcvd; /* plogis rcvd */ + u32 prli_rcvd; /* inbound PRLIs */ + u32 adisc_rcvd; /* ADISCs received */ + u32 adisc_rejects; /* recvd ADISC rejects */ + u32 adisc_sent; /* ADISC requests sent */ + u32 adisc_accs; /* ADISC accepted by rport */ + u32 adisc_failed; /* ADISC failed (no response) */ + u32 adisc_rejected; /* ADISC rejected by us */ + u32 logos; /* logos sent */ + u32 logo_accs; /* LOGO accepts from rport */ + u32 logo_failed; /* LOGO failures */ + u32 logo_rejected; /* LOGO rejects from rport */ + u32 logo_rcvd; /* LOGO from remote port */ + + u32 rpsc_rcvd; /* RPSC received */ + u32 rpsc_rejects; /* recvd RPSC rejects */ + u32 rpsc_sent; /* RPSC requests sent */ + u32 rpsc_accs; /* RPSC accepted by rport */ + u32 rpsc_failed; /* RPSC failed (no response) */ + u32 rpsc_rejected; /* RPSC rejected by us */ + + u32 rsvd; + struct bfa_rport_hal_stats_s hal_stats; /* BFA rport stats */ +}; + +/** + * Rport's QoS attributes + */ +struct bfa_rport_qos_attr_s { + enum bfa_qos_priority qos_priority; /* rport's QoS priority */ + u32 qos_flow_id; /* QoS flow Id */ +}; + +/** + * FCS remote port attributes returned in queries + */ +struct bfa_rport_attr_s { + wwn_t nwwn; /* node wwn */ + wwn_t pwwn; /* port wwn */ + enum fc_cos cos_supported; /* supported class of services */ + u32 pid; /* port ID */ + u32 df_sz; /* Max payload size */ + enum bfa_rport_state state; /* Rport State machine state */ + enum fc_cos fc_cos; /* FC classes of services */ + bfa_boolean_t cisc; /* CISC capable device */ + struct bfa_rport_symname_s symname; /* Symbolic Name */ + enum bfa_rport_function scsi_function; /* Initiator/Target */ + struct bfa_rport_qos_attr_s qos_attr; /* qos attributes */ + enum bfa_pport_speed curr_speed; /* operating speed got from + * RPSC ELS. UNKNOWN, if RPSC + * is not supported */ + bfa_boolean_t trl_enforced; /* TRL enforced ? TRUE/FALSE */ + enum bfa_pport_speed assigned_speed; /* Speed assigned by the user. + * will be used if RPSC is not + * supported by the rport */ +}; + +#define bfa_rport_aen_qos_data_t struct bfa_rport_qos_attr_s + +/** + * BFA remote port events + * Arguments below are in BFAL context from Mgmt + * BFA_RPORT_AEN_ONLINE: [in]: lpwwn [out]: vf_id, lpwwn, rpwwn + * BFA_RPORT_AEN_OFFLINE: [in]: lpwwn [out]: vf_id, lpwwn, rpwwn + * BFA_RPORT_AEN_DISCONNECT:[in]: lpwwn [out]: vf_id, lpwwn, rpwwn + * BFA_RPORT_AEN_QOS_PRIO: [in]: lpwwn [out]: vf_id, lpwwn, rpwwn, prio + * BFA_RPORT_AEN_QOS_FLOWID:[in]: lpwwn [out]: vf_id, lpwwn, rpwwn, flow_id + */ +enum bfa_rport_aen_event { + BFA_RPORT_AEN_ONLINE = 1, /* RPort online event */ + BFA_RPORT_AEN_OFFLINE = 2, /* RPort offline event */ + BFA_RPORT_AEN_DISCONNECT = 3, /* RPort disconnect event */ + BFA_RPORT_AEN_QOS_PRIO = 4, /* QOS priority change event */ + BFA_RPORT_AEN_QOS_FLOWID = 5, /* QOS flow Id change event */ +}; + +struct bfa_rport_aen_data_s { + u16 vf_id; /* vf_id of this logical port */ + u16 rsvd[3]; + wwn_t ppwwn; /* WWN of its physical port */ + wwn_t lpwwn; /* WWN of this logical port */ + wwn_t rpwwn; /* WWN of this remote port */ + union { + bfa_rport_aen_qos_data_t qos; + } priv; +}; + +#endif /* __BFA_DEFS_RPORT_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_status.h b/drivers/scsi/bfa/include/defs/bfa_defs_status.h new file mode 100644 index 000000000000..cdceaeb9f4b8 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_status.h @@ -0,0 +1,255 @@ +/* + * 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. + */ +#ifndef __BFA_DEFS_STATUS_H__ +#define __BFA_DEFS_STATUS_H__ + +/** + * API status return values + * + * NOTE: The error msgs are auto generated from the comments. Only singe line + * comments are supported + */ +enum bfa_status { + BFA_STATUS_OK = 0, /* Success */ + BFA_STATUS_FAILED = 1, /* Operation failed */ + BFA_STATUS_EINVAL = 2, /* Invalid params Check input + * parameters */ + BFA_STATUS_ENOMEM = 3, /* Out of resources */ + BFA_STATUS_ENOSYS = 4, /* Function not implemented */ + BFA_STATUS_ETIMER = 5, /* Timer expired - Retry, if + * persists, contact support */ + BFA_STATUS_EPROTOCOL = 6, /* Protocol error */ + BFA_STATUS_ENOFCPORTS = 7, /* No FC ports resources */ + BFA_STATUS_NOFLASH = 8, /* Flash not present */ + BFA_STATUS_BADFLASH = 9, /* Flash is corrupted or bad */ + BFA_STATUS_SFP_UNSUPP = 10, /* Unsupported SFP - Replace SFP */ + BFA_STATUS_UNKNOWN_VFID = 11, /* VF_ID not found */ + BFA_STATUS_DATACORRUPTED = 12, /* Diag returned data corrupted + * contact support */ + BFA_STATUS_DEVBUSY = 13, /* Device busy - Retry operation */ + BFA_STATUS_ABORTED = 14, /* Operation aborted */ + BFA_STATUS_NODEV = 15, /* Dev is not present */ + BFA_STATUS_HDMA_FAILED = 16, /* Host dma failed contact support */ + BFA_STATUS_FLASH_BAD_LEN = 17, /* Flash bad length */ + BFA_STATUS_UNKNOWN_LWWN = 18, /* LPORT PWWN not found */ + BFA_STATUS_UNKNOWN_RWWN = 19, /* RPORT PWWN not found */ + BFA_STATUS_FCPT_LS_RJT = 20, /* Got LS_RJT for FC Pass + * through Req */ + BFA_STATUS_VPORT_EXISTS = 21, /* VPORT already exists */ + BFA_STATUS_VPORT_MAX = 22, /* Reached max VPORT supported + * limit */ + BFA_STATUS_UNSUPP_SPEED = 23, /* Invalid Speed Check speed + * setting */ + BFA_STATUS_INVLD_DFSZ = 24, /* Invalid Max data field size */ + BFA_STATUS_CNFG_FAILED = 25, /* Setting can not be persisted */ + BFA_STATUS_CMD_NOTSUPP = 26, /* Command/API not supported */ + BFA_STATUS_NO_ADAPTER = 27, /* No Brocade Adapter Found */ + BFA_STATUS_LINKDOWN = 28, /* Link is down - Check or replace + * SFP/cable */ + BFA_STATUS_FABRIC_RJT = 29, /* Reject from attached fabric */ + BFA_STATUS_UNKNOWN_VWWN = 30, /* VPORT PWWN not found */ + BFA_STATUS_NSLOGIN_FAILED = 31, /* Nameserver login failed */ + BFA_STATUS_NO_RPORTS = 32, /* No remote ports found */ + BFA_STATUS_NSQUERY_FAILED = 33, /* Nameserver query failed */ + BFA_STATUS_PORT_OFFLINE = 34, /* Port is not online */ + BFA_STATUS_RPORT_OFFLINE = 35, /* RPORT is not online */ + BFA_STATUS_TGTOPEN_FAILED = 36, /* Remote SCSI target open failed */ + BFA_STATUS_BAD_LUNS = 37, /* No valid LUNs found */ + BFA_STATUS_IO_FAILURE = 38, /* SCSI target IO failure */ + BFA_STATUS_NO_FABRIC = 39, /* No switched fabric present */ + BFA_STATUS_EBADF = 40, /* Bad file descriptor */ + BFA_STATUS_EINTR = 41, /* A signal was caught during ioctl */ + BFA_STATUS_EIO = 42, /* I/O error */ + BFA_STATUS_ENOTTY = 43, /* Inappropriate I/O control + * operation */ + BFA_STATUS_ENXIO = 44, /* No such device or address */ + BFA_STATUS_EFOPEN = 45, /* Failed to open file */ + BFA_STATUS_VPORT_WWN_BP = 46, /* WWN is same as base port's WWN */ + BFA_STATUS_PORT_NOT_DISABLED = 47, /* Port not disabled disable port + * first */ + BFA_STATUS_BADFRMHDR = 48, /* Bad frame header */ + BFA_STATUS_BADFRMSZ = 49, /* Bad frame size check and replace + * SFP/cable */ + BFA_STATUS_MISSINGFRM = 50, /* Missing frame check and replace + * SFP/cable */ + BFA_STATUS_LINKTIMEOUT = 51, /* Link timeout check and replace + * SFP/cable */ + BFA_STATUS_NO_FCPIM_NEXUS = 52, /* No FCP Nexus exists with the + * rport */ + BFA_STATUS_CHECKSUM_FAIL = 53, /* checksum failure */ + BFA_STATUS_GZME_FAILED = 54, /* Get zone member query failed */ + BFA_STATUS_SCSISTART_REQD = 55, /* SCSI disk require START command */ + BFA_STATUS_IOC_FAILURE = 56, /* IOC failure - Retry, if persists + * contact support */ + BFA_STATUS_INVALID_WWN = 57, /* Invalid WWN */ + BFA_STATUS_MISMATCH = 58, /* Version mismatch */ + BFA_STATUS_IOC_ENABLED = 59, /* IOC is already enabled */ + BFA_STATUS_ADAPTER_ENABLED = 60, /* Adapter is not disabled disable + * adapter first */ + BFA_STATUS_IOC_NON_OP = 61, /* IOC is not operational. Enable IOC + * and if it still fails, + * contact support */ + BFA_STATUS_ADDR_MAP_FAILURE = 62, /* PCI base address not mapped + * in OS */ + BFA_STATUS_SAME_NAME = 63, /* Name exists! use a different + * name */ + BFA_STATUS_PENDING = 64, /* API completes asynchronously */ + BFA_STATUS_8G_SPD = 65, /* Speed setting not valid for + * 8G HBA */ + BFA_STATUS_4G_SPD = 66, /* Speed setting not valid for + * 4G HBA */ + BFA_STATUS_AD_IS_ENABLE = 67, /* Adapter is already enabled */ + BFA_STATUS_EINVAL_TOV = 68, /* Invalid path failover TOV */ + BFA_STATUS_EINVAL_QDEPTH = 69, /* Invalid queue depth value */ + BFA_STATUS_VERSION_FAIL = 70, /* Application/Driver version + * mismatch */ + BFA_STATUS_DIAG_BUSY = 71, /* diag busy */ + BFA_STATUS_BEACON_ON = 72, /* Port Beacon already on */ + BFA_STATUS_BEACON_OFF = 73, /* Port Beacon already off */ + BFA_STATUS_LBEACON_ON = 74, /* Link End-to-End Beacon already + * on */ + BFA_STATUS_LBEACON_OFF = 75, /* Link End-to-End Beacon already + * off */ + BFA_STATUS_PORT_NOT_INITED = 76, /* Port not initialized */ + BFA_STATUS_RPSC_ENABLED = 77, /* Target has a valid speed */ + BFA_STATUS_ENOFSAVE = 78, /* No saved firmware trace */ + BFA_STATUS_BAD_FILE = 79, /* Not a valid Brocade Boot Code + * file */ + BFA_STATUS_RLIM_EN = 80, /* Target rate limiting is already + * enabled */ + BFA_STATUS_RLIM_DIS = 81, /* Target rate limiting is already + * disabled */ + BFA_STATUS_IOC_DISABLED = 82, /* IOC is already disabled */ + BFA_STATUS_ADAPTER_DISABLED = 83, /* Adapter is already disabled */ + BFA_STATUS_BIOS_DISABLED = 84, /* Bios is already disabled */ + BFA_STATUS_AUTH_ENABLED = 85, /* Authentication is already + * enabled */ + BFA_STATUS_AUTH_DISABLED = 86, /* Authentication is already + * disabled */ + BFA_STATUS_ERROR_TRL_ENABLED = 87, /* Target rate limiting is + * enabled */ + BFA_STATUS_ERROR_QOS_ENABLED = 88, /* QoS is enabled */ + BFA_STATUS_NO_SFP_DEV = 89, /* No SFP device check or replace SFP */ + BFA_STATUS_MEMTEST_FAILED = 90, /* Memory test failed contact + * support */ + BFA_STATUS_INVALID_DEVID = 91, /* Invalid device id provided */ + BFA_STATUS_QOS_ENABLED = 92, /* QOS is already enabled */ + BFA_STATUS_QOS_DISABLED = 93, /* QOS is already disabled */ + BFA_STATUS_INCORRECT_DRV_CONFIG = 94, /* Check configuration + * key/value pair */ + BFA_STATUS_REG_FAIL = 95, /* Can't read windows registry */ + BFA_STATUS_IM_INV_CODE = 96, /* Invalid IOCTL code */ + BFA_STATUS_IM_INV_VLAN = 97, /* Invalid VLAN ID */ + BFA_STATUS_IM_INV_ADAPT_NAME = 98, /* Invalid adapter name */ + BFA_STATUS_IM_LOW_RESOURCES = 99, /* Memory allocation failure in + * driver */ + BFA_STATUS_IM_VLANID_IS_PVID = 100, /* Given VLAN id same as PVID */ + BFA_STATUS_IM_VLANID_EXISTS = 101, /* Given VLAN id already exists */ + BFA_STATUS_IM_FW_UPDATE_FAIL = 102, /* Updating firmware with new + * VLAN ID failed */ + BFA_STATUS_PORTLOG_ENABLED = 103, /* Port Log is already enabled */ + BFA_STATUS_PORTLOG_DISABLED = 104, /* Port Log is already disabled */ + BFA_STATUS_FILE_NOT_FOUND = 105, /* Specified file could not be + * found */ + BFA_STATUS_QOS_FC_ONLY = 106, /* QOS can be enabled for FC mode + * only */ + BFA_STATUS_RLIM_FC_ONLY = 107, /* RATELIM can be enabled for FC mode + * only */ + BFA_STATUS_CT_SPD = 108, /* Invalid speed selection for Catapult. */ + BFA_STATUS_LEDTEST_OP = 109, /* LED test is operating */ + BFA_STATUS_CEE_NOT_DN = 110, /* eth port is not at down state, please + * bring down first */ + BFA_STATUS_10G_SPD = 111, /* Speed setting not valid for 10G HBA */ + BFA_STATUS_IM_INV_TEAM_NAME = 112, /* Invalid team name */ + BFA_STATUS_IM_DUP_TEAM_NAME = 113, /* Given team name already + * exists */ + BFA_STATUS_IM_ADAPT_ALREADY_IN_TEAM = 114, /* Given adapter is part + * of another team */ + BFA_STATUS_IM_ADAPT_HAS_VLANS = 115, /* Adapter has VLANs configured. + * Delete all VLANs before + * creating team */ + BFA_STATUS_IM_PVID_MISMATCH = 116, /* Mismatching PVIDs configured + * for adapters */ + BFA_STATUS_IM_LINK_SPEED_MISMATCH = 117, /* Mismatching link speeds + * configured for adapters */ + BFA_STATUS_IM_MTU_MISMATCH = 118, /* Mismatching MTUs configured for + * adapters */ + BFA_STATUS_IM_RSS_MISMATCH = 119, /* Mismatching RSS parameters + * configured for adapters */ + BFA_STATUS_IM_HDS_MISMATCH = 120, /* Mismatching HDS parameters + * configured for adapters */ + BFA_STATUS_IM_OFFLOAD_MISMATCH = 121, /* Mismatching offload + * parameters configured for + * adapters */ + BFA_STATUS_IM_PORT_PARAMS = 122, /* Error setting port parameters */ + BFA_STATUS_IM_PORT_NOT_IN_TEAM = 123, /* Port is not part of team */ + BFA_STATUS_IM_CANNOT_REM_PRI = 124, /* Primary adapter cannot be + * removed. Change primary before + * removing */ + BFA_STATUS_IM_MAX_PORTS_REACHED = 125, /* Exceeding maximum ports + * per team */ + BFA_STATUS_IM_LAST_PORT_DELETE = 126, /* Last port in team being + * deleted */ + BFA_STATUS_IM_NO_DRIVER = 127, /* IM driver is not installed */ + BFA_STATUS_IM_MAX_VLANS_REACHED = 128, /* Exceeding maximum VLANs + * per port */ + BFA_STATUS_TOMCAT_SPD_NOT_ALLOWED = 129, /* Bios speed config not + * allowed for CNA */ + BFA_STATUS_NO_MINPORT_DRIVER = 130, /* Miniport driver is not + * loaded */ + BFA_STATUS_CARD_TYPE_MISMATCH = 131, /* Card type mismatch */ + BFA_STATUS_BAD_ASICBLK = 132, /* Bad ASIC block */ + BFA_STATUS_NO_DRIVER = 133, /* Storage/Ethernet driver not loaded */ + BFA_STATUS_INVALID_MAC = 134, /* Invalid mac address */ + BFA_STATUS_IM_NO_VLAN = 135, /* No VLANs configured on the adapter */ + BFA_STATUS_IM_ETH_LB_FAILED = 136, /* Ethernet loopback test failed */ + BFA_STATUS_IM_PVID_REMOVE = 137, /* Cannot remove port vlan (PVID) */ + BFA_STATUS_IM_PVID_EDIT = 138, /* Cannot edit port vlan (PVID) */ + BFA_STATUS_CNA_NO_BOOT = 139, /* Boot upload not allowed for CNA */ + BFA_STATUS_IM_PVID_NON_ZERO = 140, /* Port VLAN ID (PVID) is Set to + * Non-Zero Value */ + BFA_STATUS_IM_INETCFG_LOCK_FAILED = 141, /* Acquiring Network + * Subsytem Lock Failed.Please + * try after some time */ + BFA_STATUS_IM_GET_INETCFG_FAILED = 142, /* Acquiring Network Subsytem + * handle Failed. Please try + * after some time */ + BFA_STATUS_IM_NOT_BOUND = 143, /* Brocade 10G Ethernet Service is not + * Enabled on this port */ + BFA_STATUS_INSUFFICIENT_PERMS = 144, /* User doesn't have sufficient + * permissions to execute the BCU + * application */ + BFA_STATUS_IM_INV_VLAN_NAME = 145, /* Invalid/Reserved Vlan name + * string. The name is not allowed + * for the normal Vlans */ + BFA_STATUS_CMD_NOTSUPP_CNA = 146, /* Command not supported for CNA */ + BFA_STATUS_IM_PASSTHRU_EDIT = 147, /* Can not edit passthru vlan id */ + BFA_STATUS_IM_BIND_FAILED = 148, /*! < IM Driver bind operation + * failed */ + BFA_STATUS_IM_UNBIND_FAILED = 149, /* ! < IM Driver unbind operation + * failed */ + BFA_STATUS_MAX_VAL /* Unknown error code */ +}; +#define bfa_status_t enum bfa_status + +enum bfa_eproto_status { + BFA_EPROTO_BAD_ACCEPT = 0, + BFA_EPROTO_UNKNOWN_RSP = 1 +}; +#define bfa_eproto_status_t enum bfa_eproto_status + +#endif /* __BFA_DEFS_STATUS_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_tin.h b/drivers/scsi/bfa/include/defs/bfa_defs_tin.h new file mode 100644 index 000000000000..e05a2db7abed --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_tin.h @@ -0,0 +1,118 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_TIN_H__ +#define __BFA_DEFS_TIN_H__ + +#include +#include + +/** + * FCS tin states + */ +enum bfa_tin_state_e { + BFA_TIN_SM_OFFLINE = 0, /* tin is offline */ + BFA_TIN_SM_WOS_LOGIN = 1, /* Waiting PRLI ACC/RJT from ULP */ + BFA_TIN_SM_WFW_ONLINE = 2, /* Waiting ACK to PRLI ACC from FW */ + BFA_TIN_SM_ONLINE = 3, /* tin login is complete */ + BFA_TIN_SM_WIO_RELOGIN = 4, /* tin relogin is in progress */ + BFA_TIN_SM_WIO_LOGOUT = 5, /* Processing of PRLO req from + * Initiator is in progress + */ + BFA_TIN_SM_WOS_LOGOUT = 6, /* Processing of PRLO req from + * Initiator is in progress + */ + BFA_TIN_SM_WIO_CLEAN = 7, /* Waiting for IO cleanup before tin + * is offline. This can be triggered + * by RPORT LOGO (rcvd/sent) or by + * PRLO (rcvd/sent) + */ +}; + +struct bfa_prli_req_s { + struct fchs_s fchs; + struct fc_prli_s prli_payload; +}; + +struct bfa_prlo_req_s { + struct fchs_s fchs; + struct fc_prlo_s prlo_payload; +}; + +void bfa_tin_send_login_rsp(void *bfa_tin, u32 login_rsp, + struct fc_ls_rjt_s rjt_payload); +void bfa_tin_send_logout_rsp(void *bfa_tin, u32 logout_rsp, + struct fc_ls_rjt_s rjt_payload); +/** + * FCS target port statistics + */ +struct bfa_tin_stats_s { + u32 onlines; /* ITN nexus onlines (PRLI done) */ + u32 offlines; /* ITN Nexus offlines */ + u32 prli_req_parse_err; /* prli req parsing errors */ + u32 prli_rsp_rjt; /* num prli rsp rejects sent */ + u32 prli_rsp_acc; /* num prli rsp accepts sent */ + u32 cleanup_comps; /* ITN cleanup completions */ +}; + +/** + * FCS tin attributes returned in queries + */ +struct bfa_tin_attr_s { + enum bfa_tin_state_e state; + u8 seq_retry; /* Sequence retry supported */ + u8 rsvd[3]; +}; + +/** + * BFA TIN async event data structure for BFAL + */ +enum bfa_tin_aen_event { + BFA_TIN_AEN_ONLINE = 1, /* Target online */ + BFA_TIN_AEN_OFFLINE = 2, /* Target offline */ + BFA_TIN_AEN_DISCONNECT = 3, /* Target disconnected */ +}; + +/** + * BFA TIN event data structure. + */ +struct bfa_tin_aen_data_s { + u16 vf_id; /* vf_id of the IT nexus */ + u16 rsvd[3]; + wwn_t lpwwn; /* WWN of logical port */ + wwn_t rpwwn; /* WWN of remote(target) port */ +}; + +/** + * Below APIs are needed from BFA driver + * Move these to BFA driver public header file? + */ +/* TIN rcvd new PRLI & gets bfad_tin_t ptr from driver this callback */ +void *bfad_tin_rcvd_login_req(void *bfad_tm_port, void *bfa_tin, + wwn_t rp_wwn, u32 rp_fcid, + struct bfa_prli_req_s prli_req); +/* TIN rcvd new PRLO */ +void bfad_tin_rcvd_logout_req(void *bfad_tin, wwn_t rp_wwn, u32 rp_fcid, + struct bfa_prlo_req_s prlo_req); +/* TIN is online and ready for IO */ +void bfad_tin_online(void *bfad_tin); +/* TIN is offline and BFA driver can shutdown its upper stack */ +void bfad_tin_offline(void *bfad_tin); +/* TIN does not need this BFA driver tin tag anymore, so can be freed */ +void bfad_tin_res_free(void *bfad_tin); + +#endif /* __BFA_DEFS_TIN_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h b/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h new file mode 100644 index 000000000000..31881d218515 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_TSENSOR_H__ +#define __BFA_DEFS_TSENSOR_H__ + +#include +#include + +/** + * Temperature sensor status values + */ +enum bfa_tsensor_status { + BFA_TSENSOR_STATUS_UNKNOWN = 1, /* unkown status */ + BFA_TSENSOR_STATUS_FAULTY = 2, /* sensor is faulty */ + BFA_TSENSOR_STATUS_BELOW_MIN = 3, /* temperature below mininum */ + BFA_TSENSOR_STATUS_NOMINAL = 4, /* normal temperature */ + BFA_TSENSOR_STATUS_ABOVE_MAX = 5, /* temperature above maximum */ +}; + +/** + * Temperature sensor attribute + */ +struct bfa_tsensor_attr_s { + enum bfa_tsensor_status status; /* temperature sensor status */ + u32 value; /* current temperature in celsius */ +}; + +#endif /* __BFA_DEFS_TSENSOR_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_types.h b/drivers/scsi/bfa/include/defs/bfa_defs_types.h new file mode 100644 index 000000000000..4348332b107a --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_types.h @@ -0,0 +1,30 @@ +/* + * 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. + */ +#ifndef __BFA_DEFS_TYPES_H__ +#define __BFA_DEFS_TYPES_H__ + +#include + +enum bfa_boolean { + BFA_FALSE = 0, + BFA_TRUE = 1 +}; +#define bfa_boolean_t enum bfa_boolean + +#define BFA_STRING_32 32 + +#endif /* __BFA_DEFS_TYPES_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_version.h b/drivers/scsi/bfa/include/defs/bfa_defs_version.h new file mode 100644 index 000000000000..f8902a2c9aad --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_version.h @@ -0,0 +1,22 @@ +/* + * 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. + */ +#ifndef __BFA_DEFS_VERSION_H__ +#define __BFA_DEFS_VERSION_H__ + +#define BFA_VERSION_LEN 64 + +#endif diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_vf.h b/drivers/scsi/bfa/include/defs/bfa_defs_vf.h new file mode 100644 index 000000000000..3235be5e9423 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_vf.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_VF_H__ +#define __BFA_DEFS_VF_H__ + +#include +#include +#include + +/** + * VF states + */ +enum bfa_vf_state { + BFA_VF_UNINIT = 0, /* fabric is not yet initialized */ + BFA_VF_LINK_DOWN = 1, /* link is down */ + BFA_VF_FLOGI = 2, /* flogi is in progress */ + BFA_VF_AUTH = 3, /* authentication in progress */ + BFA_VF_NOFABRIC = 4, /* fabric is not present */ + BFA_VF_ONLINE = 5, /* login to fabric is complete */ + BFA_VF_EVFP = 6, /* EVFP is in progress */ + BFA_VF_ISOLATED = 7, /* port isolated due to vf_id mismatch */ +}; + +/** + * VF statistics + */ +struct bfa_vf_stats_s { + u32 flogi_sent; /* Num FLOGIs sent */ + u32 flogi_rsp_err; /* FLOGI response errors */ + u32 flogi_acc_err; /* FLOGI accept errors */ + u32 flogi_accepts; /* FLOGI accepts received */ + u32 flogi_rejects; /* FLOGI rejects received */ + u32 flogi_unknown_rsp; /* Unknown responses for FLOGI */ + u32 flogi_alloc_wait; /* Allocation waits prior to + * sending FLOGI + */ + u32 flogi_rcvd; /* FLOGIs received */ + u32 flogi_rejected; /* Incoming FLOGIs rejected */ + u32 fabric_onlines; /* Internal fabric online + * notification sent to other + * modules + */ + u32 fabric_offlines; /* Internal fabric offline + * notification sent to other + * modules + */ + u32 resvd; +}; + +/** + * VF attributes returned in queries + */ +struct bfa_vf_attr_s { + enum bfa_vf_state state; /* VF state */ + u32 rsvd; + wwn_t fabric_name; /* fabric name */ +}; + +#endif /* __BFA_DEFS_VF_H__ */ diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_vport.h b/drivers/scsi/bfa/include/defs/bfa_defs_vport.h new file mode 100644 index 000000000000..9f021f43b3b4 --- /dev/null +++ b/drivers/scsi/bfa/include/defs/bfa_defs_vport.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#ifndef __BFA_DEFS_VPORT_H__ +#define __BFA_DEFS_VPORT_H__ + +#include +#include +#include + +/** + * VPORT states + */ +enum bfa_vport_state { + BFA_FCS_VPORT_UNINIT = 0, + BFA_FCS_VPORT_CREATED = 1, + BFA_FCS_VPORT_OFFLINE = 1, + BFA_FCS_VPORT_FDISC_SEND = 2, + BFA_FCS_VPORT_FDISC = 3, + BFA_FCS_VPORT_FDISC_RETRY = 4, + BFA_FCS_VPORT_ONLINE = 5, + BFA_FCS_VPORT_DELETING = 6, + BFA_FCS_VPORT_CLEANUP = 6, + BFA_FCS_VPORT_LOGO_SEND = 7, + BFA_FCS_VPORT_LOGO = 8, + BFA_FCS_VPORT_ERROR = 9, + BFA_FCS_VPORT_MAX_STATE, +}; + +/** + * vport statistics + */ +struct bfa_vport_stats_s { + struct bfa_port_stats_s port_stats; /* base class (port) stats */ + /* + * TODO - remove + */ + + u32 fdisc_sent; /* num fdisc sent */ + u32 fdisc_accepts; /* fdisc accepts */ + u32 fdisc_retries; /* fdisc retries */ + u32 fdisc_timeouts; /* fdisc timeouts */ + u32 fdisc_rsp_err; /* fdisc response error */ + u32 fdisc_acc_bad; /* bad fdisc accepts */ + u32 fdisc_rejects; /* fdisc rejects */ + u32 fdisc_unknown_rsp; + /* + *!< fdisc rsp unknown error + */ + u32 fdisc_alloc_wait;/* fdisc req (fcxp)alloc wait */ + + u32 logo_alloc_wait;/* logo req (fcxp) alloc wait */ + u32 logo_sent; /* logo sent */ + u32 logo_accepts; /* logo accepts */ + u32 logo_rejects; /* logo rejects */ + u32 logo_rsp_err; /* logo rsp errors */ + u32 logo_unknown_rsp; + /* logo rsp unknown errors */ + + u32 fab_no_npiv; /* fabric does not support npiv */ + + u32 fab_offline; /* offline events from fab SM */ + u32 fab_online; /* online events from fab SM */ + u32 fab_cleanup; /* cleanup request from fab SM */ + u32 rsvd; +}; + +/** + * BFA vport attribute returned in queries + */ +struct bfa_vport_attr_s { + struct bfa_port_attr_s port_attr; /* base class (port) attributes */ + enum bfa_vport_state vport_state; /* vport state */ + u32 rsvd; +}; + +#endif /* __BFA_DEFS_VPORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb.h b/drivers/scsi/bfa/include/fcb/bfa_fcb.h new file mode 100644 index 000000000000..2963b0bc30e7 --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/** + * bfa_fcb.h BFA FCS callback interfaces + */ + +#ifndef __BFA_FCB_H__ +#define __BFA_FCB_H__ + +/** + * fcb Main fcs callbacks + */ + +void bfa_fcb_exit(struct bfad_s *bfad); + + + +#endif /* __BFA_FCB_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb_fcpim.h b/drivers/scsi/bfa/include/fcb/bfa_fcb_fcpim.h new file mode 100644 index 000000000000..a6c70aee0aa3 --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb_fcpim.h @@ -0,0 +1,76 @@ +/* + * 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_fcpim.h - BFA FCS initiator mode remote port callbacks + */ + +#ifndef __BFAD_FCB_FCPIM_H__ +#define __BFAD_FCB_FCPIM_H__ + +struct bfad_itnim_s; + +/* + * RPIM callbacks + */ + +/** + * Memory allocation for remote port instance. Called before PRLI is + * initiated to the remote target port. + * + * @param[in] bfad - driver instance + * @param[out] itnim - FCS remote port (IM) instance + * @param[out] itnim_drv - driver remote port (IM) instance + * + * @return None + */ +void bfa_fcb_itnim_alloc(struct bfad_s *bfad, struct bfa_fcs_itnim_s **itnim, + struct bfad_itnim_s **itnim_drv); + +/** + * Free remote port (IM) instance. + * + * @param[in] bfad - driver instance + * @param[in] itnim_drv - driver remote port instance + * + * @return None + */ +void bfa_fcb_itnim_free(struct bfad_s *bfad, + struct bfad_itnim_s *itnim_drv); + +/** + * Notification of when login with a remote target device is complete. + * + * @param[in] itnim_drv - driver remote port instance + * + * @return None + */ +void bfa_fcb_itnim_online(struct bfad_itnim_s *itnim_drv); + +/** + * Notification when login with the remote device is severed. + * + * @param[in] itnim_drv - driver remote port instance + * + * @return None + */ +void bfa_fcb_itnim_offline(struct bfad_itnim_s *itnim_drv); + +void bfa_fcb_itnim_tov_begin(struct bfad_itnim_s *itnim_drv); +void bfa_fcb_itnim_tov(struct bfad_itnim_s *itnim_drv); + +#endif /* __BFAD_FCB_FCPIM_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb_port.h b/drivers/scsi/bfa/include/fcb/bfa_fcb_port.h new file mode 100644 index 000000000000..5fd7f986fa32 --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb_port.h @@ -0,0 +1,113 @@ +/* + * 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. + */ + +/** + * bfa_fcb_port.h BFA FCS virtual port driver interfaces + */ + +#ifndef __BFA_FCB_PORT_H__ +#define __BFA_FCB_PORT_H__ + +#include +/** + * fcs_port_fcb FCS port driver interfaces + */ + +/* + * Forward declarations + */ +struct bfad_port_s; + +/* + * Callback functions from BFA FCS to driver + */ + +/** + * Call from FCS to driver module when a port is instantiated. The port + * can be a base port or a virtual port with in the base fabric or + * a virtual fabric. + * + * On this callback, driver is supposed to create scsi_host, scsi_tgt or + * network interfaces bases on ports personality/roles. + * + * base port of base fabric: vf_drv == NULL && vp_drv == NULL + * vport of base fabric: vf_drv == NULL && vp_drv != NULL + * base port of VF: vf_drv != NULL && vp_drv == NULL + * vport of VF: vf_drv != NULL && vp_drv != NULL + * + * @param[in] bfad - driver instance + * @param[in] port - FCS port instance + * @param[in] roles - port roles: IM, TM, IP + * @param[in] vf_drv - VF driver instance, NULL if base fabric (no VF) + * @param[in] vp_drv - vport driver instance, NULL if base port + * + * @return None + */ +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); + +/** + * Call from FCS to driver module when a port is deleted. The port + * can be a base port or a virtual port with in the base fabric or + * a virtual fabric. + * + * @param[in] bfad - driver instance + * @param[in] roles - port roles: IM, TM, IP + * @param[in] vf_drv - VF driver instance, NULL if base fabric (no VF) + * @param[in] vp_drv - vport driver instance, NULL if base port + * + * @return None + */ +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); + +/** + * Notification when port transitions to ONLINE state. + * + * Online notification is a logical link up for the local port. This + * notification is sent after a successfull FLOGI, or a successful + * link initialization in proviate-loop or N2N topologies. + * + * @param[in] bfad - driver instance + * @param[in] roles - port roles: IM, TM, IP + * @param[in] vf_drv - VF driver instance, NULL if base fabric (no VF) + * @param[in] vp_drv - vport driver instance, NULL if base port + * + * @return None + */ +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); + +/** + * Notification when port transitions to OFFLINE state. + * + * Offline notification is a logical link down for the local port. + * + * @param[in] bfad - driver instance + * @param[in] roles - port roles: IM, TM, IP + * @param[in] vf_drv - VF driver instance, NULL if base fabric (no VF) + * @param[in] vp_drv - vport driver instance, NULL if base port + * + * @return None + */ +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); + + +#endif /* __BFA_FCB_PORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb_rport.h b/drivers/scsi/bfa/include/fcb/bfa_fcb_rport.h new file mode 100644 index 000000000000..e0261bb6d1c1 --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb_rport.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +/** + * bfa_fcb_rport.h BFA FCS rport driver interfaces + */ + +#ifndef __BFA_FCB_RPORT_H__ +#define __BFA_FCB_RPORT_H__ + +/** + * fcs_rport_fcb Remote port driver interfaces + */ + + +struct bfad_rport_s; + +/* + * Callback functions from BFA FCS to driver + */ + +/** + * Completion callback for bfa_fcs_rport_add(). + * + * @param[in] rport_drv - driver instance of rport + * + * @return None + */ +void bfa_fcb_rport_add(struct bfad_rport_s *rport_drv); + +/** + * Completion callback for bfa_fcs_rport_remove(). + * + * @param[in] rport_drv - driver instance of rport + * + * @return None + */ +void bfa_fcb_rport_remove(struct bfad_rport_s *rport_drv); + +/** + * Call to allocate a rport instance. + * + * @param[in] bfad - driver instance + * @param[out] rport - BFA FCS instance of rport + * @param[out] rport_drv - driver instance of rport + * + * @retval BFA_STATUS_OK - successfully allocated + * @retval BFA_STATUS_ENOMEM - cannot allocate + */ +bfa_status_t bfa_fcb_rport_alloc(struct bfad_s *bfad, + struct bfa_fcs_rport_s **rport, + struct bfad_rport_s **rport_drv); + +/** + * Call to free rport memory resources. + * + * @param[in] bfad - driver instance + * @param[in] rport_drv - driver instance of rport + * + * @return None + */ +void bfa_fcb_rport_free(struct bfad_s *bfad, struct bfad_rport_s **rport_drv); + + + +#endif /* __BFA_FCB_RPORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb_vf.h b/drivers/scsi/bfa/include/fcb/bfa_fcb_vf.h new file mode 100644 index 000000000000..cfd3fac0a4e2 --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb_vf.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +/** + * bfa_fcb_vf.h BFA FCS virtual fabric driver interfaces + */ + +#ifndef __BFA_FCB_VF_H__ +#define __BFA_FCB_VF_H__ + +/** + * fcs_vf_fcb Virtual fabric driver intrefaces + */ + + +struct bfad_vf_s; + +/* + * Callback functions from BFA FCS to driver + */ + +/** + * Completion callback for bfa_fcs_vf_stop(). + * + * @param[in] vf_drv - driver instance of vf + * + * @return None + */ +void bfa_fcb_vf_stop(struct bfad_vf_s *vf_drv); + + + +#endif /* __BFA_FCB_VF_H__ */ diff --git a/drivers/scsi/bfa/include/fcb/bfa_fcb_vport.h b/drivers/scsi/bfa/include/fcb/bfa_fcb_vport.h new file mode 100644 index 000000000000..a39f474c2fcf --- /dev/null +++ b/drivers/scsi/bfa/include/fcb/bfa_fcb_vport.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +/** + * bfa_fcb_vport.h BFA FCS virtual port driver interfaces + */ + +#ifndef __BFA_FCB_VPORT_H__ +#define __BFA_FCB_VPORT_H__ + +/** + * fcs_vport_fcb Virtual port driver interfaces + */ + + +struct bfad_vport_s; + +/* + * Callback functions from BFA FCS to driver + */ + +/** + * Completion callback for bfa_fcs_vport_delete(). + * + * @param[in] vport_drv - driver instance of vport + * + * @return None + */ +void bfa_fcb_vport_delete(struct bfad_vport_s *vport_drv); + + + +#endif /* __BFA_FCB_VPORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs.h b/drivers/scsi/bfa/include/fcs/bfa_fcs.h new file mode 100644 index 000000000000..627669c65546 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef __BFA_FCS_H__ +#define __BFA_FCS_H__ + +#include +#include +#include +#include +#include + +#define BFA_FCS_OS_STR_LEN 64 + +struct bfa_fcs_stats_s { + struct { + u32 untagged; /* untagged receive frames */ + u32 tagged; /* tagged receive frames */ + u32 vfid_unknown; /* VF id is unknown */ + } uf; +}; + +struct bfa_fcs_driver_info_s { + u8 version[BFA_VERSION_LEN]; /* Driver Version */ + u8 host_machine_name[BFA_FCS_OS_STR_LEN]; + u8 host_os_name[BFA_FCS_OS_STR_LEN]; /* OS name and version */ + u8 host_os_patch[BFA_FCS_OS_STR_LEN];/* patch or service pack */ + u8 os_device_name[BFA_FCS_OS_STR_LEN]; /* Driver Device Name */ +}; + +struct bfa_fcs_s { + struct bfa_s *bfa; /* corresponding BFA bfa instance */ + struct bfad_s *bfad; /* corresponding BDA driver instance */ + struct bfa_log_mod_s *logm; /* driver logging module instance */ + struct bfa_trc_mod_s *trcmod; /* tracing module */ + struct bfa_aen_s *aen; /* aen component */ + bfa_boolean_t vf_enabled; /* VF mode is enabled */ + bfa_boolean_t min_cfg; /* min cfg enabled/disabled */ + u16 port_vfid; /* port default VF ID */ + struct bfa_fcs_driver_info_s driver_info; + struct bfa_fcs_fabric_s fabric; /* base fabric state machine */ + struct bfa_fcs_stats_s stats; /* FCS statistics */ + struct bfa_wc_s wc; /* waiting counter */ +}; + +/* + * bfa fcs API functions + */ +void bfa_fcs_init(struct bfa_fcs_s *fcs, struct bfa_s *bfa, struct bfad_s *bfad, + bfa_boolean_t min_cfg); +void bfa_fcs_driver_info_init(struct bfa_fcs_s *fcs, + struct bfa_fcs_driver_info_s *driver_info); +void bfa_fcs_exit(struct bfa_fcs_s *fcs); +void bfa_fcs_trc_init(struct bfa_fcs_s *fcs, struct bfa_trc_mod_s *trcmod); +void bfa_fcs_log_init(struct bfa_fcs_s *fcs, struct bfa_log_mod_s *logmod); +void bfa_fcs_aen_init(struct bfa_fcs_s *fcs, struct bfa_aen_s *aen); +void bfa_fcs_start(struct bfa_fcs_s *fcs); + +#endif /* __BFA_FCS_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_auth.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_auth.h new file mode 100644 index 000000000000..28c4c9ff08b3 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_auth.h @@ -0,0 +1,82 @@ +/* + * 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. + */ + +#ifndef __BFA_FCS_AUTH_H__ +#define __BFA_FCS_AUTH_H__ + +struct bfa_fcs_s; + +#include +#include +#include +#include +#include +#include +#include +#include + +struct bfa_fcs_fabric_s; + + + +struct bfa_fcs_auth_s { + bfa_sm_t sm; /* state machine */ + bfa_boolean_t policy; /* authentication enabled/disabled */ + enum bfa_auth_status status; /* authentication status */ + enum auth_rjt_codes rjt_code; /* auth reject status */ + enum auth_rjt_code_exps rjt_code_exp; /* auth reject reason */ + enum bfa_auth_algo algo; /* Authentication algorithm */ + struct bfa_auth_stats_s stats; /* Statistics */ + enum auth_dh_gid group; /* DH(diffie-hellman) Group */ + enum bfa_auth_secretsource source; /* Secret source */ + char secret[BFA_AUTH_SECRET_STRING_LEN]; + /* secret string */ + u8 secret_len; + /* secret string length */ + u8 nretries; + /* number of retries */ + struct bfa_fcs_fabric_s *fabric;/* pointer to fabric */ + u8 sentcode; /* pointer to response data */ + u8 *response; /* pointer to response data */ + struct bfa_timer_s delay_timer; /* delay timer */ + struct bfa_fcxp_s *fcxp; /* pointer to fcxp */ + struct bfa_fcxp_wqe_s fcxp_wqe; +}; + +/** + * bfa fcs authentication public functions + */ +bfa_status_t bfa_fcs_auth_get_attr(struct bfa_fcs_s *port, + struct bfa_auth_attr_s *attr); +bfa_status_t bfa_fcs_auth_set_policy(struct bfa_fcs_s *port, + bfa_boolean_t policy); +enum bfa_auth_status bfa_fcs_auth_get_status(struct bfa_fcs_s *port); +bfa_status_t bfa_fcs_auth_set_algo(struct bfa_fcs_s *port, + enum bfa_auth_algo algo); +bfa_status_t bfa_fcs_auth_get_stats(struct bfa_fcs_s *port, + struct bfa_auth_stats_s *stats); +bfa_status_t bfa_fcs_auth_set_dh_group(struct bfa_fcs_s *port, int group); +bfa_status_t bfa_fcs_auth_set_secretstring(struct bfa_fcs_s *port, + char *secret); +bfa_status_t bfa_fcs_auth_set_secretstring_encrypt(struct bfa_fcs_s *port, + u32 secret[], u32 len); +bfa_status_t bfa_fcs_auth_set_secretsource(struct bfa_fcs_s *port, + enum bfa_auth_secretsource src); +bfa_status_t bfa_fcs_auth_reset_stats(struct bfa_fcs_s *port); +bfa_status_t bfa_fcs_auth_reinit(struct bfa_fcs_s *port); + +#endif /* __BFA_FCS_AUTH_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h new file mode 100644 index 000000000000..4ffd2242d3de --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h @@ -0,0 +1,112 @@ +/* + * 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. + */ + +#ifndef __BFA_FCS_FABRIC_H__ +#define __BFA_FCS_FABRIC_H__ + +struct bfa_fcs_s; + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * forward declaration + */ +struct bfad_vf_s; + +enum bfa_fcs_fabric_type { + BFA_FCS_FABRIC_UNKNOWN = 0, + BFA_FCS_FABRIC_SWITCHED = 1, + BFA_FCS_FABRIC_PLOOP = 2, + BFA_FCS_FABRIC_N2N = 3, +}; + + +struct bfa_fcs_fabric_s { + struct list_head qe; /* queue element */ + bfa_sm_t sm; /* state machine */ + struct bfa_fcs_s *fcs; /* FCS instance */ + struct bfa_fcs_port_s bport; /* base logical port */ + enum bfa_fcs_fabric_type fab_type; /* fabric type */ + enum bfa_pport_type oper_type; /* current link topology */ + u8 is_vf; /* is virtual fabric? */ + u8 is_npiv; /* is NPIV supported ? */ + u8 is_auth; /* is Security/Auth supported ? */ + u16 bb_credit; /* BB credit from fabric */ + u16 vf_id; /* virtual fabric ID */ + u16 num_vports; /* num vports */ + u16 rsvd; + struct list_head vport_q; /* queue of virtual ports */ + struct list_head vf_q; /* queue of virtual fabrics */ + struct bfad_vf_s *vf_drv; /* driver vf structure */ + struct bfa_timer_s link_timer; /* Link Failure timer. Vport */ + wwn_t fabric_name; /* attached fabric name */ + bfa_boolean_t auth_reqd; /* authentication required */ + struct bfa_timer_s delay_timer; /* delay timer */ + union { + u16 swp_vfid;/* switch port VF id */ + } event_arg; + struct bfa_fcs_auth_s auth; /* authentication config */ + struct bfa_wc_s wc; /* wait counter for delete */ + struct bfa_vf_stats_s stats; /* fabric/vf stats */ + struct bfa_lps_s *lps; /* lport login services */ + u8 fabric_ip_addr[BFA_FCS_FABRIC_IPADDR_SZ]; /* attached + * fabric's ip addr + */ +}; + +#define bfa_fcs_fabric_npiv_capable(__f) (__f)->is_npiv +#define bfa_fcs_fabric_is_switched(__f) \ + ((__f)->fab_type == BFA_FCS_FABRIC_SWITCHED) + +/** + * The design calls for a single implementation of base fabric and vf. + */ +#define bfa_fcs_vf_t struct bfa_fcs_fabric_s + +struct bfa_vf_event_s { + u32 undefined; +}; + +/** + * bfa fcs vf public functions + */ +bfa_status_t bfa_fcs_vf_mode_enable(struct bfa_fcs_s *fcs, u16 vf_id); +bfa_status_t bfa_fcs_vf_mode_disable(struct bfa_fcs_s *fcs); +bfa_status_t bfa_fcs_vf_create(bfa_fcs_vf_t *vf, struct bfa_fcs_s *fcs, + u16 vf_id, struct bfa_port_cfg_s *port_cfg, + struct bfad_vf_s *vf_drv); +bfa_status_t bfa_fcs_vf_delete(bfa_fcs_vf_t *vf); +void bfa_fcs_vf_start(bfa_fcs_vf_t *vf); +bfa_status_t bfa_fcs_vf_stop(bfa_fcs_vf_t *vf); +void bfa_fcs_vf_list(struct bfa_fcs_s *fcs, u16 *vf_ids, int *nvfs); +void bfa_fcs_vf_list_all(struct bfa_fcs_s *fcs, u16 *vf_ids, int *nvfs); +void bfa_fcs_vf_get_attr(bfa_fcs_vf_t *vf, struct bfa_vf_attr_s *vf_attr); +void bfa_fcs_vf_get_stats(bfa_fcs_vf_t *vf, + struct bfa_vf_stats_s *vf_stats); +void bfa_fcs_vf_clear_stats(bfa_fcs_vf_t *vf); +void bfa_fcs_vf_get_ports(bfa_fcs_vf_t *vf, wwn_t vpwwn[], int *nports); +bfa_fcs_vf_t *bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id); +struct bfad_vf_s *bfa_fcs_vf_get_drv_vf(bfa_fcs_vf_t *vf); + +#endif /* __BFA_FCS_FABRIC_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_fcpim.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_fcpim.h new file mode 100644 index 000000000000..e719f2c3eb35 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_fcpim.h @@ -0,0 +1,131 @@ +/* + * 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. + */ + +/** + * bfa_fcs_fcpim.h BFA FCS FCP Initiator Mode interfaces/defines. + */ + +#ifndef __BFA_FCS_FCPIM_H__ +#define __BFA_FCS_FCPIM_H__ + +#include +#include +#include +#include +#include +#include + +/* + * forward declarations + */ +struct bfad_itnim_s; + +struct bfa_fcs_itnim_s { + bfa_sm_t sm; /* state machine */ + struct bfa_fcs_rport_s *rport; /* parent remote rport */ + struct bfad_itnim_s *itnim_drv; /* driver peer instance */ + struct bfa_fcs_s *fcs; /* fcs instance */ + struct bfa_timer_s timer; /* timer functions */ + struct bfa_itnim_s *bfa_itnim; /* BFA itnim struct */ + bfa_boolean_t seq_rec; /* seq recovery support */ + bfa_boolean_t rec_support; /* REC supported */ + bfa_boolean_t conf_comp; /* FCP_CONF support */ + bfa_boolean_t task_retry_id; /* task retry id supp */ + struct bfa_fcxp_wqe_s fcxp_wqe; /* wait qelem for fcxp */ + struct bfa_fcxp_s *fcxp; /* FCXP in use */ + struct bfa_itnim_stats_s stats; /* itn statistics */ +}; + + +static inline struct bfad_port_s * +bfa_fcs_itnim_get_drvport(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->port->bfad_port; +} + + +static inline struct bfa_fcs_port_s * +bfa_fcs_itnim_get_port(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->port; +} + + +static inline wwn_t +bfa_fcs_itnim_get_nwwn(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->nwwn; +} + + +static inline wwn_t +bfa_fcs_itnim_get_pwwn(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->pwwn; +} + + +static inline u32 +bfa_fcs_itnim_get_fcid(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->pid; +} + + +static inline u32 +bfa_fcs_itnim_get_maxfrsize(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->maxfrsize; +} + + +static inline enum fc_cos +bfa_fcs_itnim_get_cos(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->rport->fc_cos; +} + + +static inline struct bfad_itnim_s * +bfa_fcs_itnim_get_drvitn(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->itnim_drv; +} + + +static inline struct bfa_itnim_s * +bfa_fcs_itnim_get_halitn(struct bfa_fcs_itnim_s *itnim) +{ + return itnim->bfa_itnim; +} + +/** + * bfa fcs FCP Initiator mode API functions + */ +void bfa_fcs_itnim_get_attr(struct bfa_fcs_itnim_s *itnim, + struct bfa_itnim_attr_s *attr); +void bfa_fcs_itnim_get_stats(struct bfa_fcs_itnim_s *itnim, + struct bfa_itnim_stats_s *stats); +struct bfa_fcs_itnim_s *bfa_fcs_itnim_lookup(struct bfa_fcs_port_s *port, + wwn_t rpwwn); +bfa_status_t bfa_fcs_itnim_attr_get(struct bfa_fcs_port_s *port, wwn_t rpwwn, + struct bfa_itnim_attr_s *attr); +bfa_status_t bfa_fcs_itnim_stats_get(struct bfa_fcs_port_s *port, wwn_t rpwwn, + struct bfa_itnim_stats_s *stats); +bfa_status_t bfa_fcs_itnim_stats_clear(struct bfa_fcs_port_s *port, + wwn_t rpwwn); +#endif /* __BFA_FCS_FCPIM_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_fdmi.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_fdmi.h new file mode 100644 index 000000000000..4441fffc9c82 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_fdmi.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +/** + * bfa_fcs_fdmi.h BFA fcs fdmi module public interface + */ + +#ifndef __BFA_FCS_FDMI_H__ +#define __BFA_FCS_FDMI_H__ +#include +#include + +#define BFA_FCS_FDMI_SUPORTED_SPEEDS (FDMI_TRANS_SPEED_1G | \ + FDMI_TRANS_SPEED_2G | \ + FDMI_TRANS_SPEED_4G | \ + FDMI_TRANS_SPEED_8G) + +/* +* HBA Attribute Block : BFA internal representation. Note : Some variable +* sizes have been trimmed to suit BFA For Ex : Model will be "Brocade". Based + * on this the size has been reduced to 16 bytes from the standard's 64 bytes. + */ +struct bfa_fcs_fdmi_hba_attr_s { + wwn_t node_name; + u8 manufacturer[64]; + u8 serial_num[64]; + u8 model[16]; + u8 model_desc[256]; + u8 hw_version[8]; + u8 driver_version[8]; + u8 option_rom_ver[BFA_VERSION_LEN]; + u8 fw_version[8]; + u8 os_name[256]; + u32 max_ct_pyld; +}; + +/* + * Port Attribute Block + */ +struct bfa_fcs_fdmi_port_attr_s { + u8 supp_fc4_types[32]; /* supported FC4 types */ + u32 supp_speed; /* supported speed */ + u32 curr_speed; /* current Speed */ + u32 max_frm_size; /* max frame size */ + u8 os_device_name[256]; /* OS device Name */ + u8 host_name[256]; /* host name */ +}; + +#endif /* __BFA_FCS_FDMI_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h new file mode 100644 index 000000000000..b85cba884b96 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h @@ -0,0 +1,226 @@ +/* + * 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. + */ + +/** + * bfa_fcs_port.h BFA fcs port module public interface + */ + +#ifndef __BFA_FCS_PORT_H__ +#define __BFA_FCS_PORT_H__ + +#include +#include +#include +#include +#include +#include +#include + +struct bfa_fcs_s; +struct bfa_fcs_fabric_s; + +/* +* @todo : need to move to a global config file. + * Maximum Vports supported per physical port or vf. + */ +#define BFA_FCS_MAX_VPORTS_SUPP_CB 255 +#define BFA_FCS_MAX_VPORTS_SUPP_CT 191 + +/* +* @todo : need to move to a global config file. + * Maximum Rports supported per port (physical/logical). + */ +#define BFA_FCS_MAX_RPORTS_SUPP 256 /* @todo : tentative value */ + + +struct bfa_fcs_port_ns_s { + bfa_sm_t sm; /* state machine */ + struct bfa_timer_s timer; + struct bfa_fcs_port_s *port; /* parent port */ + struct bfa_fcxp_s *fcxp; + struct bfa_fcxp_wqe_s fcxp_wqe; +}; + + +struct bfa_fcs_port_scn_s { + bfa_sm_t sm; /* state machine */ + struct bfa_timer_s timer; + struct bfa_fcs_port_s *port; /* parent port */ + struct bfa_fcxp_s *fcxp; + struct bfa_fcxp_wqe_s fcxp_wqe; +}; + + +struct bfa_fcs_port_fdmi_s { + bfa_sm_t sm; /* state machine */ + struct bfa_timer_s timer; + struct bfa_fcs_port_ms_s *ms; /* parent ms */ + struct bfa_fcxp_s *fcxp; + struct bfa_fcxp_wqe_s fcxp_wqe; + u8 retry_cnt; /* retry count */ + u8 rsvd[3]; +}; + + +struct bfa_fcs_port_ms_s { + bfa_sm_t sm; /* state machine */ + struct bfa_timer_s timer; + struct bfa_fcs_port_s *port; /* parent port */ + struct bfa_fcxp_s *fcxp; + struct bfa_fcxp_wqe_s fcxp_wqe; + struct bfa_fcs_port_fdmi_s fdmi; /* FDMI component of MS */ + u8 retry_cnt; /* retry count */ + u8 rsvd[3]; +}; + + +struct bfa_fcs_port_fab_s { + struct bfa_fcs_port_ns_s ns; /* NS component of port */ + struct bfa_fcs_port_scn_s scn; /* scn component of port */ + struct bfa_fcs_port_ms_s ms; /* MS component of port */ +}; + + + +#define MAX_ALPA_COUNT 127 + +struct bfa_fcs_port_loop_s { + u8 num_alpa; /* Num of ALPA entries in the map */ + u8 alpa_pos_map[MAX_ALPA_COUNT]; /* ALPA Positional + *Map */ + struct bfa_fcs_port_s *port; /* parent port */ +}; + + + +struct bfa_fcs_port_n2n_s { + u32 rsvd; + u16 reply_oxid; /* ox_id from the req flogi to be + *used in flogi acc */ + wwn_t rem_port_wwn; /* Attached port's wwn */ +}; + + +union bfa_fcs_port_topo_u { + struct bfa_fcs_port_fab_s pfab; + struct bfa_fcs_port_loop_s ploop; + struct bfa_fcs_port_n2n_s pn2n; +}; + + +struct bfa_fcs_port_s { + struct list_head qe; /* used by port/vport */ + bfa_sm_t sm; /* state machine */ + struct bfa_fcs_fabric_s *fabric; /* parent fabric */ + struct bfa_port_cfg_s port_cfg; /* port configuration */ + struct bfa_timer_s link_timer; /* timer for link offline */ + u32 pid : 24; /* FC address */ + u8 lp_tag; /* lport tag */ + u16 num_rports; /* Num of r-ports */ + struct list_head rport_q; /* queue of discovered r-ports */ + struct bfa_fcs_s *fcs; /* FCS instance */ + union bfa_fcs_port_topo_u port_topo; /* fabric/loop/n2n details */ + struct bfad_port_s *bfad_port; /* driver peer instance */ + struct bfa_fcs_vport_s *vport; /* NULL for base ports */ + struct bfa_fcxp_s *fcxp; + struct bfa_fcxp_wqe_s fcxp_wqe; + struct bfa_port_stats_s stats; + struct bfa_wc_s wc; /* waiting counter for events */ +}; + +#define bfa_fcs_lport_t struct bfa_fcs_port_s + +/** + * Symbolic Name related defines + * Total bytes 255. + * Physical Port's symbolic name 128 bytes. + * For Vports, Vport's symbolic name is appended to the Physical port's + * Symbolic Name. + * + * Physical Port's symbolic name Format : (Total 128 bytes) + * Adapter Model number/name : 12 bytes + * Driver Version : 10 bytes + * Host Machine Name : 30 bytes + * Host OS Info : 48 bytes + * Host OS PATCH Info : 16 bytes + * ( remaining 12 bytes reserved to be used for separator) + */ +#define BFA_FCS_PORT_SYMBNAME_SEPARATOR " | " + +#define BFA_FCS_PORT_SYMBNAME_MODEL_SZ 12 +#define BFA_FCS_PORT_SYMBNAME_VERSION_SZ 10 +#define BFA_FCS_PORT_SYMBNAME_MACHINENAME_SZ 30 +#define BFA_FCS_PORT_SYMBNAME_OSINFO_SZ 48 +#define BFA_FCS_PORT_SYMBNAME_OSPATCH_SZ 16 + +/** + * Get FC port ID for a logical port. + */ +#define bfa_fcs_port_get_fcid(_lport) ((_lport)->pid) +#define bfa_fcs_port_get_pwwn(_lport) ((_lport)->port_cfg.pwwn) +#define bfa_fcs_port_get_nwwn(_lport) ((_lport)->port_cfg.nwwn) +#define bfa_fcs_port_get_psym_name(_lport) ((_lport)->port_cfg.sym_name) +#define bfa_fcs_port_is_initiator(_lport) \ + ((_lport)->port_cfg.roles & BFA_PORT_ROLE_FCP_IM) +#define bfa_fcs_port_is_target(_lport) \ + ((_lport)->port_cfg.roles & BFA_PORT_ROLE_FCP_TM) +#define bfa_fcs_port_get_nrports(_lport) \ + ((_lport) ? (_lport)->num_rports : 0) + +static inline struct bfad_port_s * +bfa_fcs_port_get_drvport(struct bfa_fcs_port_s *port) +{ + return port->bfad_port; +} + + +#define bfa_fcs_port_get_opertype(_lport) (_lport)->fabric->oper_type + + +#define bfa_fcs_port_get_fabric_name(_lport) (_lport)->fabric->fabric_name + + +#define bfa_fcs_port_get_fabric_ipaddr(_lport) (_lport)->fabric->fabric_ip_addr + +/** + * bfa fcs port public functions + */ +void bfa_fcs_cfg_base_port(struct bfa_fcs_s *fcs, + struct bfa_port_cfg_s *port_cfg); +struct bfa_fcs_port_s *bfa_fcs_get_base_port(struct bfa_fcs_s *fcs); +void bfa_fcs_port_get_rports(struct bfa_fcs_port_s *port, + wwn_t rport_wwns[], int *nrports); + +wwn_t bfa_fcs_port_get_rport(struct bfa_fcs_port_s *port, wwn_t wwn, + int index, int nrports, bfa_boolean_t bwwn); + +struct bfa_fcs_port_s *bfa_fcs_lookup_port(struct bfa_fcs_s *fcs, + u16 vf_id, wwn_t lpwwn); + +void bfa_fcs_port_get_info(struct bfa_fcs_port_s *port, + struct bfa_port_info_s *port_info); +void bfa_fcs_port_get_attr(struct bfa_fcs_port_s *port, + struct bfa_port_attr_s *port_attr); +void bfa_fcs_port_get_stats(struct bfa_fcs_port_s *fcs_port, + struct bfa_port_stats_s *port_stats); +void bfa_fcs_port_clear_stats(struct bfa_fcs_port_s *fcs_port); +enum bfa_pport_speed bfa_fcs_port_get_rport_max_speed( + struct bfa_fcs_port_s *port); +void bfa_fcs_port_enable_ipfc_roles(struct bfa_fcs_port_s *fcs_port); +void bfa_fcs_port_disable_ipfc_roles(struct bfa_fcs_port_s *fcs_port); + +#endif /* __BFA_FCS_PORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_rport.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_rport.h new file mode 100644 index 000000000000..702b95b76c2d --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_rport.h @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#ifndef __BFA_FCS_RPORT_H__ +#define __BFA_FCS_RPORT_H__ + +#include +#include +#include +#include + +#define BFA_FCS_RPORT_DEF_DEL_TIMEOUT 90 /* in secs */ +/* + * forward declarations + */ +struct bfad_rport_s; + +struct bfa_fcs_itnim_s; +struct bfa_fcs_tin_s; +struct bfa_fcs_iprp_s; + +/* Rport Features (RPF) */ +struct bfa_fcs_rpf_s { + bfa_sm_t sm; /* state machine */ + struct bfa_fcs_rport_s *rport; /* parent rport */ + struct bfa_timer_s timer; /* general purpose timer */ + struct bfa_fcxp_s *fcxp; /* FCXP needed for discarding */ + struct bfa_fcxp_wqe_s fcxp_wqe; /* fcxp wait queue element */ + int rpsc_retries; /* max RPSC retry attempts */ + enum bfa_pport_speed rpsc_speed; /* Current Speed from RPSC. + * O if RPSC fails */ + enum bfa_pport_speed assigned_speed; /* Speed assigned by the user. + * will be used if RPSC is not + * supported by the rport */ +}; + +struct bfa_fcs_rport_s { + struct list_head qe; /* used by port/vport */ + struct bfa_fcs_port_s *port; /* parent FCS port */ + struct bfa_fcs_s *fcs; /* fcs instance */ + struct bfad_rport_s *rp_drv; /* driver peer instance */ + u32 pid; /* port ID of rport */ + u16 maxfrsize; /* maximum frame size */ + u16 reply_oxid; /* OX_ID of inbound requests */ + enum fc_cos fc_cos; /* FC classes of service supp */ + bfa_boolean_t cisc; /* CISC capable device */ + wwn_t pwwn; /* port wwn of rport */ + wwn_t nwwn; /* node wwn of rport */ + struct bfa_rport_symname_s psym_name; /* port symbolic name */ + bfa_sm_t sm; /* state machine */ + struct bfa_timer_s timer; /* general purpose timer */ + struct bfa_fcs_itnim_s *itnim; /* ITN initiator mode role */ + struct bfa_fcs_tin_s *tin; /* ITN initiator mode role */ + struct bfa_fcs_iprp_s *iprp; /* IP/FC role */ + struct bfa_rport_s *bfa_rport; /* BFA Rport */ + struct bfa_fcxp_s *fcxp; /* FCXP needed for discarding */ + int plogi_retries; /* max plogi retry attempts */ + int ns_retries; /* max NS query retry attempts */ + struct bfa_fcxp_wqe_s fcxp_wqe; /* fcxp wait queue element */ + struct bfa_rport_stats_s stats; /* rport stats */ + enum bfa_rport_function scsi_function; /* Initiator/Target */ + struct bfa_fcs_rpf_s rpf; /* Rport features module */ +}; + +static inline struct bfa_rport_s * +bfa_fcs_rport_get_halrport(struct bfa_fcs_rport_s *rport) +{ + return rport->bfa_rport; +} + +/** + * bfa fcs rport API functions + */ +bfa_status_t bfa_fcs_rport_add(struct bfa_fcs_port_s *port, wwn_t *pwwn, + struct bfa_fcs_rport_s *rport, + struct bfad_rport_s *rport_drv); +bfa_status_t bfa_fcs_rport_remove(struct bfa_fcs_rport_s *rport); +void bfa_fcs_rport_get_attr(struct bfa_fcs_rport_s *rport, + struct bfa_rport_attr_s *attr); +void bfa_fcs_rport_get_stats(struct bfa_fcs_rport_s *rport, + struct bfa_rport_stats_s *stats); +void bfa_fcs_rport_clear_stats(struct bfa_fcs_rport_s *rport); +struct bfa_fcs_rport_s *bfa_fcs_rport_lookup(struct bfa_fcs_port_s *port, + wwn_t rpwwn); +struct bfa_fcs_rport_s *bfa_fcs_rport_lookup_by_nwwn( + struct bfa_fcs_port_s *port, wwn_t rnwwn); +void bfa_fcs_rport_set_del_timeout(u8 rport_tmo); +void bfa_fcs_rport_set_speed(struct bfa_fcs_rport_s *rport, + enum bfa_pport_speed speed); +#endif /* __BFA_FCS_RPORT_H__ */ diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_vport.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_vport.h new file mode 100644 index 000000000000..cd33f2cd5c34 --- /dev/null +++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_vport.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +/** + * bfa_fcs_vport.h BFA fcs vport module public interface + */ + +#ifndef __BFA_FCS_VPORT_H__ +#define __BFA_FCS_VPORT_H__ + +#include +#include +#include +#include +#include + +struct bfa_fcs_vport_s { + struct list_head qe; /* queue elem */ + bfa_sm_t sm; /* state machine */ + bfa_fcs_lport_t lport; /* logical port */ + struct bfa_timer_s timer; /* general purpose timer */ + struct bfad_vport_s *vport_drv; /* Driver private */ + struct bfa_vport_stats_s vport_stats; /* vport statistics */ + struct bfa_lps_s *lps; /* Lport login service */ + int fdisc_retries; +}; + +#define bfa_fcs_vport_get_port(vport) \ + ((struct bfa_fcs_port_s *)(&vport->port)) + +/** + * bfa fcs vport public functions + */ +bfa_status_t bfa_fcs_vport_create(struct bfa_fcs_vport_s *vport, + struct bfa_fcs_s *fcs, u16 vf_id, + struct bfa_port_cfg_s *port_cfg, + struct bfad_vport_s *vport_drv); +bfa_status_t bfa_fcs_vport_delete(struct bfa_fcs_vport_s *vport); +bfa_status_t bfa_fcs_vport_start(struct bfa_fcs_vport_s *vport); +bfa_status_t bfa_fcs_vport_stop(struct bfa_fcs_vport_s *vport); +void bfa_fcs_vport_get_attr(struct bfa_fcs_vport_s *vport, + struct bfa_vport_attr_s *vport_attr); +void bfa_fcs_vport_get_stats(struct bfa_fcs_vport_s *vport, + struct bfa_vport_stats_s *vport_stats); +void bfa_fcs_vport_clr_stats(struct bfa_fcs_vport_s *vport); +struct bfa_fcs_vport_s *bfa_fcs_vport_lookup(struct bfa_fcs_s *fcs, + u16 vf_id, wwn_t vpwwn); + +#endif /* __BFA_FCS_VPORT_H__ */ diff --git a/drivers/scsi/bfa/include/log/bfa_log_fcs.h b/drivers/scsi/bfa/include/log/bfa_log_fcs.h new file mode 100644 index 000000000000..b6f5df8827f8 --- /dev/null +++ b/drivers/scsi/bfa/include/log/bfa_log_fcs.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +/* + * messages define for FCS Module + */ +#ifndef __BFA_LOG_FCS_H__ +#define __BFA_LOG_FCS_H__ +#include +#define BFA_LOG_FCS_FABRIC_NOSWITCH \ + (((u32) BFA_LOG_FCS_ID << BFA_LOG_MODID_OFFSET) | 1) +#define BFA_LOG_FCS_FABRIC_ISOLATED \ + (((u32) BFA_LOG_FCS_ID << BFA_LOG_MODID_OFFSET) | 2) +#endif diff --git a/drivers/scsi/bfa/include/log/bfa_log_hal.h b/drivers/scsi/bfa/include/log/bfa_log_hal.h new file mode 100644 index 000000000000..0412aea2ec30 --- /dev/null +++ b/drivers/scsi/bfa/include/log/bfa_log_hal.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/* messages define for HAL Module */ +#ifndef __BFA_LOG_HAL_H__ +#define __BFA_LOG_HAL_H__ +#include +#define BFA_LOG_HAL_ASSERT \ + (((u32) BFA_LOG_HAL_ID << BFA_LOG_MODID_OFFSET) | 1) +#define BFA_LOG_HAL_HEARTBEAT_FAILURE \ + (((u32) BFA_LOG_HAL_ID << BFA_LOG_MODID_OFFSET) | 2) +#define BFA_LOG_HAL_FCPIM_PARM_INVALID \ + (((u32) BFA_LOG_HAL_ID << BFA_LOG_MODID_OFFSET) | 3) +#define BFA_LOG_HAL_SM_ASSERT \ + (((u32) BFA_LOG_HAL_ID << BFA_LOG_MODID_OFFSET) | 4) +#endif diff --git a/drivers/scsi/bfa/include/log/bfa_log_linux.h b/drivers/scsi/bfa/include/log/bfa_log_linux.h new file mode 100644 index 000000000000..317c0547ee16 --- /dev/null +++ b/drivers/scsi/bfa/include/log/bfa_log_linux.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +/* messages define for LINUX Module */ +#ifndef __BFA_LOG_LINUX_H__ +#define __BFA_LOG_LINUX_H__ +#include +#define BFA_LOG_LINUX_DEVICE_CLAIMED \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 1) +#define BFA_LOG_LINUX_HASH_INIT_FAILED \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 2) +#define BFA_LOG_LINUX_SYSFS_FAILED \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 3) +#define BFA_LOG_LINUX_MEM_ALLOC_FAILED \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 4) +#define BFA_LOG_LINUX_DRIVER_REGISTRATION_FAILED \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 5) +#define BFA_LOG_LINUX_ITNIM_FREE \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 6) +#define BFA_LOG_LINUX_ITNIM_ONLINE \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 7) +#define BFA_LOG_LINUX_ITNIM_OFFLINE \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 8) +#define BFA_LOG_LINUX_SCSI_HOST_FREE \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 9) +#define BFA_LOG_LINUX_SCSI_ABORT \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 10) +#define BFA_LOG_LINUX_SCSI_ABORT_COMP \ + (((u32) BFA_LOG_LINUX_ID << BFA_LOG_MODID_OFFSET) | 11) +#endif diff --git a/drivers/scsi/bfa/include/log/bfa_log_wdrv.h b/drivers/scsi/bfa/include/log/bfa_log_wdrv.h new file mode 100644 index 000000000000..809a95f7afe2 --- /dev/null +++ b/drivers/scsi/bfa/include/log/bfa_log_wdrv.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +/* + * messages define for WDRV Module + */ +#ifndef __BFA_LOG_WDRV_H__ +#define __BFA_LOG_WDRV_H__ +#include +#define BFA_LOG_WDRV_IOC_INIT_ERROR \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 1) +#define BFA_LOG_WDRV_IOC_INTERNAL_ERROR \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 2) +#define BFA_LOG_WDRV_IOC_START_ERROR \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 3) +#define BFA_LOG_WDRV_IOC_STOP_ERROR \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 4) +#define BFA_LOG_WDRV_INSUFFICIENT_RESOURCES \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 5) +#define BFA_LOG_WDRV_BASE_ADDRESS_MAP_ERROR \ + (((u32) BFA_LOG_WDRV_ID << BFA_LOG_MODID_OFFSET) | 6) +#endif diff --git a/drivers/scsi/bfa/include/protocol/ct.h b/drivers/scsi/bfa/include/protocol/ct.h new file mode 100644 index 000000000000..c59d6630b070 --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/ct.h @@ -0,0 +1,492 @@ +/* + * 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. + */ + +#ifndef __CT_H__ +#define __CT_H__ + +#include + +#pragma pack(1) + +struct ct_hdr_s{ + u32 rev_id:8; /* Revision of the CT */ + u32 in_id:24; /* Initiator Id */ + u32 gs_type:8; /* Generic service Type */ + u32 gs_sub_type:8; /* Generic service sub type */ + u32 options:8; /* options */ + u32 rsvrd:8; /* reserved */ + u32 cmd_rsp_code:16;/* ct command/response code */ + u32 max_res_size:16;/* maximum/residual size */ + u32 frag_id:8; /* fragment ID */ + u32 reason_code:8; /* reason code */ + u32 exp_code:8; /* explanation code */ + u32 vendor_unq:8; /* vendor unique */ +}; + +/* + * defines for the Revision + */ +enum { + CT_GS3_REVISION = 0x01, +}; + +/* + * defines for gs_type + */ +enum { + CT_GSTYPE_KEYSERVICE = 0xF7, + CT_GSTYPE_ALIASSERVICE = 0xF8, + CT_GSTYPE_MGMTSERVICE = 0xFA, + CT_GSTYPE_TIMESERVICE = 0xFB, + CT_GSTYPE_DIRSERVICE = 0xFC, +}; + +/* + * defines for gs_sub_type for gs type directory service + */ +enum { + CT_GSSUBTYPE_NAMESERVER = 0x02, +}; + +/* + * defines for gs_sub_type for gs type management service + */ +enum { + CT_GSSUBTYPE_CFGSERVER = 0x01, + CT_GSSUBTYPE_UNZONED_NS = 0x02, + CT_GSSUBTYPE_ZONESERVER = 0x03, + CT_GSSUBTYPE_LOCKSERVER = 0x04, + CT_GSSUBTYPE_HBA_MGMTSERVER = 0x10, /* for FDMI */ +}; + +/* + * defines for CT response code field + */ +enum { + CT_RSP_REJECT = 0x8001, + CT_RSP_ACCEPT = 0x8002, +}; + +/* + * defintions for CT reason code + */ +enum { + CT_RSN_INV_CMD = 0x01, + CT_RSN_INV_VER = 0x02, + CT_RSN_LOGIC_ERR = 0x03, + CT_RSN_INV_SIZE = 0x04, + CT_RSN_LOGICAL_BUSY = 0x05, + CT_RSN_PROTO_ERR = 0x07, + CT_RSN_UNABLE_TO_PERF = 0x09, + CT_RSN_NOT_SUPP = 0x0B, + CT_RSN_SERVER_NOT_AVBL = 0x0D, + CT_RSN_SESSION_COULD_NOT_BE_ESTBD = 0x0E, + CT_RSN_VENDOR_SPECIFIC = 0xFF, + +}; + +/* + * definitions for explanations code for Name server + */ +enum { + CT_NS_EXP_NOADDITIONAL = 0x00, + CT_NS_EXP_ID_NOT_REG = 0x01, + CT_NS_EXP_PN_NOT_REG = 0x02, + CT_NS_EXP_NN_NOT_REG = 0x03, + CT_NS_EXP_CS_NOT_REG = 0x04, + CT_NS_EXP_IPN_NOT_REG = 0x05, + CT_NS_EXP_IPA_NOT_REG = 0x06, + CT_NS_EXP_FT_NOT_REG = 0x07, + CT_NS_EXP_SPN_NOT_REG = 0x08, + CT_NS_EXP_SNN_NOT_REG = 0x09, + CT_NS_EXP_PT_NOT_REG = 0x0A, + CT_NS_EXP_IPP_NOT_REG = 0x0B, + CT_NS_EXP_FPN_NOT_REG = 0x0C, + CT_NS_EXP_HA_NOT_REG = 0x0D, + CT_NS_EXP_FD_NOT_REG = 0x0E, + CT_NS_EXP_FF_NOT_REG = 0x0F, + CT_NS_EXP_ACCESSDENIED = 0x10, + CT_NS_EXP_UNACCEPTABLE_ID = 0x11, + CT_NS_EXP_DATABASEEMPTY = 0x12, + CT_NS_EXP_NOT_REG_IN_SCOPE = 0x13, + CT_NS_EXP_DOM_ID_NOT_PRESENT = 0x14, + CT_NS_EXP_PORT_NUM_NOT_PRESENT = 0x15, + CT_NS_EXP_NO_DEVICE_ATTACHED = 0x16 +}; + +/* + * defintions for the explanation code for all servers + */ +enum { + CT_EXP_AUTH_EXCEPTION = 0xF1, + CT_EXP_DB_FULL = 0xF2, + CT_EXP_DB_EMPTY = 0xF3, + CT_EXP_PROCESSING_REQ = 0xF4, + CT_EXP_UNABLE_TO_VERIFY_CONN = 0xF5, + CT_EXP_DEVICES_NOT_IN_CMN_ZONE = 0xF6 +}; + +/* + * Command codes for Name server + */ +enum { + GS_GID_PN = 0x0121, /* Get Id on port name */ + GS_GPN_ID = 0x0112, /* Get port name on ID */ + GS_GNN_ID = 0x0113, /* Get node name on ID */ + GS_GID_FT = 0x0171, /* Get Id on FC4 type */ + GS_GSPN_ID = 0x0118, /* Get symbolic PN on ID */ + GS_RFT_ID = 0x0217, /* Register fc4type on ID */ + GS_RSPN_ID = 0x0218, /* Register symbolic PN on ID */ + GS_RPN_ID = 0x0212, /* Register port name */ + GS_RNN_ID = 0x0213, /* Register node name */ + GS_RCS_ID = 0x0214, /* Register class of service */ + GS_RPT_ID = 0x021A, /* Register port type */ + GS_GA_NXT = 0x0100, /* Get all next */ + GS_RFF_ID = 0x021F, /* Register FC4 Feature */ +}; + +struct fcgs_id_req_s{ + u32 rsvd:8; + u32 dap:24; /* port identifier */ +}; +#define fcgs_gpnid_req_t struct fcgs_id_req_s +#define fcgs_gnnid_req_t struct fcgs_id_req_s +#define fcgs_gspnid_req_t struct fcgs_id_req_s + +struct fcgs_gidpn_req_s{ + wwn_t port_name; /* port wwn */ +}; + +struct fcgs_gidpn_resp_s{ + u32 rsvd:8; + u32 dap:24; /* port identifier */ +}; + +/** + * RFT_ID + */ +struct fcgs_rftid_req_s { + u32 rsvd:8; + u32 dap:24; /* port identifier */ + u32 fc4_type[8]; /* fc4 types */ +}; + +/** + * RFF_ID : Register FC4 features. + */ + +#define FC_GS_FCP_FC4_FEATURE_INITIATOR 0x02 +#define FC_GS_FCP_FC4_FEATURE_TARGET 0x01 + +struct fcgs_rffid_req_s{ + u32 rsvd :8; + u32 dap :24; /* port identifier */ + u32 rsvd1 :16; + u32 fc4ftr_bits :8; /* fc4 feature bits */ + u32 fc4_type :8; /* corresponding FC4 Type */ +}; + +/** + * GID_FT Request + */ +struct fcgs_gidft_req_s{ + u8 reserved; + u8 domain_id; /* domain, 0 - all fabric */ + u8 area_id; /* area, 0 - whole domain */ + u8 fc4_type; /* FC_TYPE_FCP for SCSI devices */ +}; /* GID_FT Request */ + +/** + * GID_FT Response + */ +struct fcgs_gidft_resp_s { + u8 last:1; /* last port identifier flag */ + u8 reserved:7; + u32 pid:24; /* port identifier */ +}; /* GID_FT Response */ + +/** + * RSPN_ID + */ +struct fcgs_rspnid_req_s{ + u32 rsvd:8; + u32 dap:24; /* port identifier */ + u8 spn_len; /* symbolic port name length */ + u8 spn[256]; /* symbolic port name */ +}; + +/** + * RPN_ID + */ +struct fcgs_rpnid_req_s{ + u32 rsvd:8; + u32 port_id:24; + wwn_t port_name; +}; + +/** + * RNN_ID + */ +struct fcgs_rnnid_req_s{ + u32 rsvd:8; + u32 port_id:24; + wwn_t node_name; +}; + +/** + * RCS_ID + */ +struct fcgs_rcsid_req_s{ + u32 rsvd:8; + u32 port_id:24; + u32 cos; +}; + +/** + * RPT_ID + */ +struct fcgs_rptid_req_s{ + u32 rsvd:8; + u32 port_id:24; + u32 port_type:8; + u32 rsvd1:24; +}; + +/** + * GA_NXT Request + */ +struct fcgs_ganxt_req_s{ + u32 rsvd:8; + u32 port_id:24; +}; + +/** + * GA_NXT Response + */ +struct fcgs_ganxt_rsp_s{ + u32 port_type:8; /* Port Type */ + u32 port_id:24; /* Port Identifier */ + wwn_t port_name; /* Port Name */ + u8 spn_len; /* Length of Symbolic Port Name */ + char spn[255]; /* Symbolic Port Name */ + wwn_t node_name; /* Node Name */ + u8 snn_len; /* Length of Symbolic Node Name */ + char snn[255]; /* Symbolic Node Name */ + u8 ipa[8]; /* Initial Process Associator */ + u8 ip[16]; /* IP Address */ + u32 cos; /* Class of Service */ + u32 fc4types[8]; /* FC-4 TYPEs */ + wwn_t fabric_port_name; + /* Fabric Port Name */ + u32 rsvd:8; /* Reserved */ + u32 hard_addr:24; /* Hard Address */ +}; + +/* + * Fabric Config Server + */ + +/* + * Command codes for Fabric Configuration Server + */ +enum { + GS_FC_GFN_CMD = 0x0114, /* GS FC Get Fabric Name */ + GS_FC_GMAL_CMD = 0x0116, /* GS FC GMAL */ + GS_FC_TRACE_CMD = 0x0400, /* GS FC Trace Route */ + GS_FC_PING_CMD = 0x0401, /* GS FC Ping */ +}; + +/* + * Source or Destination Port Tags. + */ +enum { + GS_FTRACE_TAG_NPORT_ID = 1, + GS_FTRACE_TAG_NPORT_NAME = 2, +}; + +/* +* Port Value : Could be a Port id or wwn + */ +union fcgs_port_val_u{ + u32 nport_id; + wwn_t nport_wwn; +}; + +#define GS_FTRACE_MAX_HOP_COUNT 20 +#define GS_FTRACE_REVISION 1 + +/* + * Ftrace Related Structures. + */ + +/* + * STR (Switch Trace) Reject Reason Codes. From FC-SW. + */ +enum { + GS_FTRACE_STR_CMD_COMPLETED_SUCC = 0, + GS_FTRACE_STR_CMD_NOT_SUPP_IN_NEXT_SWITCH, + GS_FTRACE_STR_NO_RESP_FROM_NEXT_SWITCH, + GS_FTRACE_STR_MAX_HOP_CNT_REACHED, + GS_FTRACE_STR_SRC_PORT_NOT_FOUND, + GS_FTRACE_STR_DST_PORT_NOT_FOUND, + GS_FTRACE_STR_DEVICES_NOT_IN_COMMON_ZONE, + GS_FTRACE_STR_NO_ROUTE_BW_PORTS, + GS_FTRACE_STR_NO_ADDL_EXPLN, + GS_FTRACE_STR_FABRIC_BUSY, + GS_FTRACE_STR_FABRIC_BUILD_IN_PROGRESS, + GS_FTRACE_STR_VENDOR_SPECIFIC_ERR_START = 0xf0, + GS_FTRACE_STR_VENDOR_SPECIFIC_ERR_END = 0xff, +}; + +/* + * Ftrace Request + */ +struct fcgs_ftrace_req_s{ + u32 revision; + u16 src_port_tag; /* Source Port tag */ + u16 src_port_len; /* Source Port len */ + union fcgs_port_val_u src_port_val; /* Source Port value */ + u16 dst_port_tag; /* Destination Port tag */ + u16 dst_port_len; /* Destination Port len */ + union fcgs_port_val_u dst_port_val; /* Destination Port value */ + u32 token; + u8 vendor_id[8]; /* T10 Vendor Identifier */ + u8 vendor_info[8]; /* Vendor specific Info */ + u32 max_hop_cnt; /* Max Hop Count */ +}; + +/* + * Path info structure + */ +struct fcgs_ftrace_path_info_s{ + wwn_t switch_name; /* Switch WWN */ + u32 domain_id; + wwn_t ingress_port_name; /* Ingress ports wwn */ + u32 ingress_phys_port_num; /* Ingress ports physical port + * number + */ + wwn_t egress_port_name; /* Ingress ports wwn */ + u32 egress_phys_port_num; /* Ingress ports physical port + * number + */ +}; + +/* + * Ftrace Acc Response + */ +struct fcgs_ftrace_resp_s{ + u32 revision; + u32 token; + u8 vendor_id[8]; /* T10 Vendor Identifier */ + u8 vendor_info[8]; /* Vendor specific Info */ + u32 str_rej_reason_code; /* STR Reject Reason Code */ + u32 num_path_info_entries; /* No. of path info entries */ + /* + * path info entry/entries. + */ + struct fcgs_ftrace_path_info_s path_info[1]; + +}; + +/* +* Fabric Config Server : FCPing + */ + +/* + * FC Ping Request + */ +struct fcgs_fcping_req_s{ + u32 revision; + u16 port_tag; + u16 port_len; /* Port len */ + union fcgs_port_val_u port_val; /* Port value */ + u32 token; +}; + +/* + * FC Ping Response + */ +struct fcgs_fcping_resp_s{ + u32 token; +}; + +/* + * Command codes for zone server query. + */ +enum { + ZS_GZME = 0x0124, /* Get zone member extended */ +}; + +/* + * ZS GZME request + */ +#define ZS_GZME_ZNAMELEN 32 +struct zs_gzme_req_s{ + u8 znamelen; + u8 rsvd[3]; + u8 zname[ZS_GZME_ZNAMELEN]; +}; + +enum zs_mbr_type{ + ZS_MBR_TYPE_PWWN = 1, + ZS_MBR_TYPE_DOMPORT = 2, + ZS_MBR_TYPE_PORTID = 3, + ZS_MBR_TYPE_NWWN = 4, +}; + +struct zs_mbr_wwn_s{ + u8 mbr_type; + u8 rsvd[3]; + wwn_t wwn; +}; + +struct zs_query_resp_s{ + u32 nmbrs; /* number of zone members */ + struct zs_mbr_wwn_s mbr[1]; +}; + +/* + * GMAL Command ( Get ( interconnect Element) Management Address List) + * To retrieve the IP Address of a Switch. + */ + +#define CT_GMAL_RESP_PREFIX_TELNET "telnet://" +#define CT_GMAL_RESP_PREFIX_HTTP "http://" + +/* GMAL/GFN request */ +struct fcgs_req_s { + wwn_t wwn; /* PWWN/NWWN */ +}; + +#define fcgs_gmal_req_t struct fcgs_req_s +#define fcgs_gfn_req_t struct fcgs_req_s + +/* Accept Response to GMAL */ +struct fcgs_gmal_resp_s { + u32 ms_len; /* Num of entries */ + u8 ms_ma[256]; +}; + +struct fc_gmal_entry_s { + u8 len; + u8 prefix[7]; /* like "http://" */ + u8 ip_addr[248]; +}; + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/include/protocol/fc.h b/drivers/scsi/bfa/include/protocol/fc.h new file mode 100644 index 000000000000..3e39ba58cfb5 --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/fc.h @@ -0,0 +1,1105 @@ +/* + * 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. + */ + +#ifndef __FC_H__ +#define __FC_H__ + +#include + +#pragma pack(1) + +/* + * Fibre Channel Header Structure (FCHS) definition + */ +struct fchs_s { +#ifdef __BIGENDIAN + u32 routing:4; /* routing bits */ + u32 cat_info:4; /* category info */ +#else + u32 cat_info:4; /* category info */ + u32 routing:4; /* routing bits */ +#endif + u32 d_id:24; /* destination identifier */ + + u32 cs_ctl:8; /* class specific control */ + u32 s_id:24; /* source identifier */ + + u32 type:8; /* data structure type */ + u32 f_ctl:24; /* initial frame control */ + + u8 seq_id; /* sequence identifier */ + u8 df_ctl; /* data field control */ + u16 seq_cnt; /* sequence count */ + + u16 ox_id; /* originator exchange ID */ + u16 rx_id; /* responder exchange ID */ + + u32 ro; /* relative offset */ +}; +/* + * Fibre Channel BB_E Header Structure + */ +struct fcbbehs_s { + u16 ver_rsvd; + u32 rsvd[2]; + u32 rsvd__sof; +}; + +#define FC_SEQ_ID_MAX 256 + +/* + * routing bit definitions + */ +enum { + FC_RTG_FC4_DEV_DATA = 0x0, /* FC-4 Device Data */ + FC_RTG_EXT_LINK = 0x2, /* Extended Link Data */ + FC_RTG_FC4_LINK_DATA = 0x3, /* FC-4 Link Data */ + FC_RTG_VIDEO_DATA = 0x4, /* Video Data */ + FC_RTG_EXT_HDR = 0x5, /* VFT, IFR or Encapsuled */ + FC_RTG_BASIC_LINK = 0x8, /* Basic Link data */ + FC_RTG_LINK_CTRL = 0xC, /* Link Control */ +}; + +/* + * information category for extended link data and FC-4 Link Data + */ +enum { + FC_CAT_LD_REQUEST = 0x2, /* Request */ + FC_CAT_LD_REPLY = 0x3, /* Reply */ + FC_CAT_LD_DIAG = 0xF, /* for DIAG use only */ +}; + +/* + * information category for extended headers (VFT, IFR or encapsulation) + */ +enum { + FC_CAT_VFT_HDR = 0x0, /* Virtual fabric tagging header */ + FC_CAT_IFR_HDR = 0x1, /* Inter-Fabric routing header */ + FC_CAT_ENC_HDR = 0x2, /* Encapsulation header */ +}; + +/* + * information category for FC-4 device data + */ +enum { + FC_CAT_UNCATEG_INFO = 0x0, /* Uncategorized information */ + FC_CAT_SOLICIT_DATA = 0x1, /* Solicited Data */ + FC_CAT_UNSOLICIT_CTRL = 0x2, /* Unsolicited Control */ + FC_CAT_SOLICIT_CTRL = 0x3, /* Solicited Control */ + FC_CAT_UNSOLICIT_DATA = 0x4, /* Unsolicited Data */ + FC_CAT_DATA_DESC = 0x5, /* Data Descriptor */ + FC_CAT_UNSOLICIT_CMD = 0x6, /* Unsolicited Command */ + FC_CAT_CMD_STATUS = 0x7, /* Command Status */ +}; + +/* + * information category for Link Control + */ +enum { + FC_CAT_ACK_1 = 0x00, + FC_CAT_ACK_0_N = 0x01, + FC_CAT_P_RJT = 0x02, + FC_CAT_F_RJT = 0x03, + FC_CAT_P_BSY = 0x04, + FC_CAT_F_BSY_DATA = 0x05, + FC_CAT_F_BSY_LINK_CTL = 0x06, + FC_CAT_F_LCR = 0x07, + FC_CAT_NTY = 0x08, + FC_CAT_END = 0x09, +}; + +/* + * Type Field Definitions. FC-PH Section 18.5 pg. 165 + */ +enum { + FC_TYPE_BLS = 0x0, /* Basic Link Service */ + FC_TYPE_ELS = 0x1, /* Extended Link Service */ + FC_TYPE_IP = 0x5, /* IP */ + FC_TYPE_FCP = 0x8, /* SCSI-FCP */ + FC_TYPE_GPP = 0x9, /* SCSI_GPP */ + FC_TYPE_SERVICES = 0x20, /* Fibre Channel Services */ + FC_TYPE_FC_FSS = 0x22, /* Fabric Switch Services */ + FC_TYPE_FC_AL = 0x23, /* FC-AL */ + FC_TYPE_FC_SNMP = 0x24, /* FC-SNMP */ + FC_TYPE_MAX = 256, /* 256 FC-4 types */ +}; + +struct fc_fc4types_s{ + u8 bits[FC_TYPE_MAX / 8]; +}; + +/* + * Frame Control Definitions. FC-PH Table-45. pg. 168 + */ +enum { + FCTL_EC_ORIG = 0x000000, /* exchange originator */ + FCTL_EC_RESP = 0x800000, /* exchange responder */ + FCTL_SEQ_INI = 0x000000, /* sequence initiator */ + FCTL_SEQ_REC = 0x400000, /* sequence recipient */ + FCTL_FS_EXCH = 0x200000, /* first sequence of xchg */ + FCTL_LS_EXCH = 0x100000, /* last sequence of xchg */ + FCTL_END_SEQ = 0x080000, /* last frame of sequence */ + FCTL_SI_XFER = 0x010000, /* seq initiative transfer */ + FCTL_RO_PRESENT = 0x000008, /* relative offset present */ + FCTL_FILLBYTE_MASK = 0x000003 /* , fill byte mask */ +}; + +/* + * Fabric Well Known Addresses + */ +enum { + FC_MIN_WELL_KNOWN_ADDR = 0xFFFFF0, + FC_DOMAIN_CONTROLLER_MASK = 0xFFFC00, + FC_ALIAS_SERVER = 0xFFFFF8, + FC_MGMT_SERVER = 0xFFFFFA, + FC_TIME_SERVER = 0xFFFFFB, + FC_NAME_SERVER = 0xFFFFFC, + FC_FABRIC_CONTROLLER = 0xFFFFFD, + FC_FABRIC_PORT = 0xFFFFFE, + FC_BROADCAST_SERVER = 0xFFFFFF +}; + +/* + * domain/area/port defines + */ +#define FC_DOMAIN_MASK 0xFF0000 +#define FC_DOMAIN_SHIFT 16 +#define FC_AREA_MASK 0x00FF00 +#define FC_AREA_SHIFT 8 +#define FC_PORT_MASK 0x0000FF +#define FC_PORT_SHIFT 0 + +#define FC_GET_DOMAIN(p) (((p) & FC_DOMAIN_MASK) >> FC_DOMAIN_SHIFT) +#define FC_GET_AREA(p) (((p) & FC_AREA_MASK) >> FC_AREA_SHIFT) +#define FC_GET_PORT(p) (((p) & FC_PORT_MASK) >> FC_PORT_SHIFT) + +#define FC_DOMAIN_CTRLR(p) (FC_DOMAIN_CONTROLLER_MASK | (FC_GET_DOMAIN(p))) + +enum { + FC_RXID_ANY = 0xFFFFU, +}; + +/* + * generic ELS command + */ +struct fc_els_cmd_s{ + u32 els_code:8; /* ELS Command Code */ + u32 reserved:24; +}; + +/* + * ELS Command Codes. FC-PH Table-75. pg. 223 + */ +enum { + FC_ELS_LS_RJT = 0x1, /* Link Service Reject. */ + FC_ELS_ACC = 0x02, /* Accept */ + FC_ELS_PLOGI = 0x03, /* N_Port Login. */ + FC_ELS_FLOGI = 0x04, /* F_Port Login. */ + FC_ELS_LOGO = 0x05, /* Logout. */ + FC_ELS_ABTX = 0x06, /* Abort Exchange */ + FC_ELS_RES = 0x08, /* Read Exchange status */ + FC_ELS_RSS = 0x09, /* Read sequence status block */ + FC_ELS_RSI = 0x0A, /* Request Sequence Initiative */ + FC_ELS_ESTC = 0x0C, /* Estimate Credit. */ + FC_ELS_RTV = 0x0E, /* Read Timeout Value. */ + FC_ELS_RLS = 0x0F, /* Read Link Status. */ + FC_ELS_ECHO = 0x10, /* Echo */ + FC_ELS_TEST = 0x11, /* Test */ + FC_ELS_RRQ = 0x12, /* Reinstate Recovery Qualifier. */ + FC_ELS_REC = 0x13, /* Add this for TAPE support in FCR */ + FC_ELS_PRLI = 0x20, /* Process Login */ + FC_ELS_PRLO = 0x21, /* Process Logout. */ + FC_ELS_SCN = 0x22, /* State Change Notification. */ + FC_ELS_TPRLO = 0x24, /* Third Party Process Logout. */ + FC_ELS_PDISC = 0x50, /* Discover N_Port Parameters. */ + FC_ELS_FDISC = 0x51, /* Discover F_Port Parameters. */ + FC_ELS_ADISC = 0x52, /* Discover Address. */ + FC_ELS_FAN = 0x60, /* Fabric Address Notification */ + FC_ELS_RSCN = 0x61, /* Reg State Change Notification */ + FC_ELS_SCR = 0x62, /* State Change Registration. */ + FC_ELS_RTIN = 0x77, /* Mangement server request */ + FC_ELS_RNID = 0x78, /* Mangement server request */ + FC_ELS_RLIR = 0x79, /* Registered Link Incident Record */ + + FC_ELS_RPSC = 0x7D, /* Report Port Speed Capabilities */ + FC_ELS_QSA = 0x7E, /* Query Security Attributes. Ref FC-SP */ + FC_ELS_E2E_LBEACON = 0x81, + /* End-to-End Link Beacon */ + FC_ELS_AUTH = 0x90, /* Authentication. Ref FC-SP */ + FC_ELS_RFCN = 0x97, /* Request Fabric Change Notification. Ref + *FC-SP */ + +}; + +/* + * Version numbers for FC-PH standards, + * used in login to indicate what port + * supports. See FC-PH-X table 158. + */ +enum { + FC_PH_VER_4_3 = 0x09, + FC_PH_VER_PH_3 = 0x20, +}; + +/* + * PDU size defines + */ +enum { + FC_MIN_PDUSZ = 512, + FC_MAX_PDUSZ = 2112, +}; + +/* + * N_Port PLOGI Common Service Parameters. + * FC-PH-x. Figure-76. pg. 308. + */ +struct fc_plogi_csp_s{ + u8 verhi; /* FC-PH high version */ + u8 verlo; /* FC-PH low version */ + u16 bbcred; /* BB_Credit */ + +#ifdef __BIGENDIAN + u8 ciro:1, /* continuously increasing RO */ + rro:1, /* random relative offset */ + npiv_supp:1, /* NPIV supported */ + port_type:1, /* N_Port/F_port */ + altbbcred:1, /* alternate BB_Credit */ + resolution:1, /* ms/ns ED_TOV resolution */ + vvl_info:1, /* VVL Info included */ + reserved1:1; + + u8 hg_supp:1, + query_dbc:1, + security:1, + sync_cap:1, + r_t_tov:1, + dh_dup_supp:1, + cisc:1, /* continuously increasing seq count */ + payload:1; +#else + u8 reserved2:2, + resolution:1, /* ms/ns ED_TOV resolution */ + altbbcred:1, /* alternate BB_Credit */ + port_type:1, /* N_Port/F_port */ + npiv_supp:1, /* NPIV supported */ + rro:1, /* random relative offset */ + ciro:1; /* continuously increasing RO */ + + u8 payload:1, + cisc:1, /* continuously increasing seq count */ + dh_dup_supp:1, + r_t_tov:1, + sync_cap:1, + security:1, + query_dbc:1, + hg_supp:1; +#endif + + u16 rxsz; /* recieve data_field size */ + + u16 conseq; + u16 ro_bitmap; + + u32 e_d_tov; +}; + +/* + * N_Port PLOGI Class Specific Parameters. + * FC-PH-x. Figure 78. pg. 318. + */ +struct fc_plogi_clp_s{ +#ifdef __BIGENDIAN + u32 class_valid:1; + u32 intermix:1; /* class intermix supported if set =1. + * valid only for class1. Reserved for + * class2 & class3 + */ + u32 reserved1:2; + u32 sequential:1; + u32 reserved2:3; +#else + u32 reserved2:3; + u32 sequential:1; + u32 reserved1:2; + u32 intermix:1; /* class intermix supported if set =1. + * valid only for class1. Reserved for + * class2 & class3 + */ + u32 class_valid:1; +#endif + + u32 reserved3:24; + + u32 reserved4:16; + u32 rxsz:16; /* Receive data_field size */ + + u32 reserved5:8; + u32 conseq:8; + u32 e2e_credit:16; /* end to end credit */ + + u32 reserved7:8; + u32 ospx:8; + u32 reserved8:16; +}; + +#define FLOGI_VVL_BRCD 0x42524344 /* ASCII value for each character in + * string "BRCD" */ + +/* + * PLOGI els command and reply payload + */ +struct fc_logi_s{ + struct fc_els_cmd_s els_cmd; /* ELS command code */ + struct fc_plogi_csp_s csp; /* common service params */ + wwn_t port_name; + wwn_t node_name; + struct fc_plogi_clp_s class1; /* class 1 service parameters */ + struct fc_plogi_clp_s class2; /* class 2 service parameters */ + struct fc_plogi_clp_s class3; /* class 3 service parameters */ + struct fc_plogi_clp_s class4; /* class 4 service parameters */ + u8 vvl[16]; /* vendor version level */ +}; + +/* + * LOGO els command payload + */ +struct fc_logo_s{ + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 nport_id:24; /* N_Port identifier of source */ + wwn_t orig_port_name; /* Port name of the LOGO originator */ +}; + +/* + * ADISC els command payload + */ +struct fc_adisc_s { + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 orig_HA:24; /* originator hard address */ + wwn_t orig_port_name; /* originator port name */ + wwn_t orig_node_name; /* originator node name */ + u32 res2:8; + u32 nport_id:24; /* originator NPortID */ +}; + +/* + * Exchange status block + */ +struct fc_exch_status_blk_s{ + u32 oxid:16; + u32 rxid:16; + u32 res1:8; + u32 orig_np:24; /* originator NPortID */ + u32 res2:8; + u32 resp_np:24; /* responder NPortID */ + u32 es_bits; + u32 res3; + /* + * un modified section of the fields + */ +}; + +/* + * RES els command payload + */ +struct fc_res_s { + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 nport_id:24; /* N_Port identifier of source */ + u32 oxid:16; + u32 rxid:16; + u8 assoc_hdr[32]; +}; + +/* + * RES els accept payload + */ +struct fc_res_acc_s{ + struct fc_els_cmd_s els_cmd; /* ELS command code */ + struct fc_exch_status_blk_s fc_exch_blk; /* Exchange status block */ +}; + +/* + * REC els command payload + */ +struct fc_rec_s { + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 nport_id:24; /* N_Port identifier of source */ + u32 oxid:16; + u32 rxid:16; +}; + +#define FC_REC_ESB_OWN_RSP 0x80000000 /* responder owns */ +#define FC_REC_ESB_SI 0x40000000 /* SI is owned */ +#define FC_REC_ESB_COMP 0x20000000 /* exchange is complete */ +#define FC_REC_ESB_ENDCOND_ABN 0x10000000 /* abnormal ending */ +#define FC_REC_ESB_RQACT 0x04000000 /* recovery qual active */ +#define FC_REC_ESB_ERRP_MSK 0x03000000 +#define FC_REC_ESB_OXID_INV 0x00800000 /* invalid OXID */ +#define FC_REC_ESB_RXID_INV 0x00400000 /* invalid RXID */ +#define FC_REC_ESB_PRIO_INUSE 0x00200000 + +/* + * REC els accept payload + */ +struct fc_rec_acc_s { + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 oxid:16; + u32 rxid:16; + u32 res1:8; + u32 orig_id:24; /* N_Port id of exchange originator */ + u32 res2:8; + u32 resp_id:24; /* N_Port id of exchange responder */ + u32 count; /* data transfer count */ + u32 e_stat; /* exchange status */ +}; + +/* + * RSI els payload + */ +struct fc_rsi_s { + struct fc_els_cmd_s els_cmd; + u32 res1:8; + u32 orig_sid:24; + u32 oxid:16; + u32 rxid:16; +}; + +/* + * structure for PRLI paramater pages, both request & response + * see FC-PH-X table 113 & 115 for explanation also FCP table 8 + */ +struct fc_prli_params_s{ + u32 reserved: 16; +#ifdef __BIGENDIAN + u32 reserved1: 5; + u32 rec_support : 1; + u32 task_retry_id : 1; + u32 retry : 1; + + u32 confirm : 1; + u32 doverlay:1; + u32 initiator:1; + u32 target:1; + u32 cdmix:1; + u32 drmix:1; + u32 rxrdisab:1; + u32 wxrdisab:1; +#else + u32 retry : 1; + u32 task_retry_id : 1; + u32 rec_support : 1; + u32 reserved1: 5; + + u32 wxrdisab:1; + u32 rxrdisab:1; + u32 drmix:1; + u32 cdmix:1; + u32 target:1; + u32 initiator:1; + u32 doverlay:1; + u32 confirm : 1; +#endif +}; + +/* + * valid values for rspcode in PRLI ACC payload + */ +enum { + FC_PRLI_ACC_XQTD = 0x1, /* request executed */ + FC_PRLI_ACC_PREDEF_IMG = 0x5, /* predefined image - no prli needed */ +}; + +struct fc_prli_params_page_s{ + u32 type:8; + u32 codext:8; +#ifdef __BIGENDIAN + u32 origprocasv:1; + u32 rsppav:1; + u32 imagepair:1; + u32 reserved1:1; + u32 rspcode:4; +#else + u32 rspcode:4; + u32 reserved1:1; + u32 imagepair:1; + u32 rsppav:1; + u32 origprocasv:1; +#endif + u32 reserved2:8; + + u32 origprocas; + u32 rspprocas; + struct fc_prli_params_s servparams; +}; + +/* + * PRLI request and accept payload, FC-PH-X tables 112 & 114 + */ +struct fc_prli_s{ + u32 command:8; + u32 pglen:8; + u32 pagebytes:16; + struct fc_prli_params_page_s parampage; +}; + +/* + * PRLO logout params page + */ +struct fc_prlo_params_page_s{ + u32 type:8; + u32 type_ext:8; +#ifdef __BIGENDIAN + u32 opa_valid:1; /* originator process associator + * valid + */ + u32 rpa_valid:1; /* responder process associator valid */ + u32 res1:14; +#else + u32 res1:14; + u32 rpa_valid:1; /* responder process associator valid */ + u32 opa_valid:1; /* originator process associator + * valid + */ +#endif + u32 orig_process_assc; + u32 resp_process_assc; + + u32 res2; +}; + +/* + * PRLO els command payload + */ +struct fc_prlo_s{ + u32 command:8; + u32 page_len:8; + u32 payload_len:16; + struct fc_prlo_params_page_s prlo_params[1]; +}; + +/* + * PRLO Logout response parameter page + */ +struct fc_prlo_acc_params_page_s{ + u32 type:8; + u32 type_ext:8; + +#ifdef __BIGENDIAN + u32 opa_valid:1; /* originator process associator + * valid + */ + u32 rpa_valid:1; /* responder process associator valid */ + u32 res1:14; +#else + u32 res1:14; + u32 rpa_valid:1; /* responder process associator valid */ + u32 opa_valid:1; /* originator process associator + * valid + */ +#endif + u32 orig_process_assc; + u32 resp_process_assc; + + u32 fc4type_csp; +}; + +/* + * PRLO els command ACC payload + */ +struct fc_prlo_acc_s{ + u32 command:8; + u32 page_len:8; + u32 payload_len:16; + struct fc_prlo_acc_params_page_s prlo_acc_params[1]; +}; + +/* + * SCR els command payload + */ +enum { + FC_SCR_REG_FUNC_FABRIC_DETECTED = 0x01, + FC_SCR_REG_FUNC_N_PORT_DETECTED = 0x02, + FC_SCR_REG_FUNC_FULL = 0x03, + FC_SCR_REG_FUNC_CLEAR_REG = 0xFF, +}; + +/* SCR VU registrations */ +enum { + FC_VU_SCR_REG_FUNC_FABRIC_NAME_CHANGE = 0x01 +}; + +struct fc_scr_s{ + u32 command:8; + u32 res:24; + u32 vu_reg_func:8; /* Vendor Unique Registrations */ + u32 res1:16; + u32 reg_func:8; +}; + +/* + * Information category for Basic link data + */ +enum { + FC_CAT_NOP = 0x0, + FC_CAT_ABTS = 0x1, + FC_CAT_RMC = 0x2, + FC_CAT_BA_ACC = 0x4, + FC_CAT_BA_RJT = 0x5, + FC_CAT_PRMT = 0x6, +}; + +/* + * LS_RJT els reply payload + */ +struct fc_ls_rjt_s { + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 reason_code:8; /* Reason code for reject */ + u32 reason_code_expl:8; /* Reason code explanation */ + u32 vendor_unique:8; /* Vendor specific */ +}; + +/* + * LS_RJT reason codes + */ +enum { + FC_LS_RJT_RSN_INV_CMD_CODE = 0x01, + FC_LS_RJT_RSN_LOGICAL_ERROR = 0x03, + FC_LS_RJT_RSN_LOGICAL_BUSY = 0x05, + FC_LS_RJT_RSN_PROTOCOL_ERROR = 0x07, + FC_LS_RJT_RSN_UNABLE_TO_PERF_CMD = 0x09, + FC_LS_RJT_RSN_CMD_NOT_SUPP = 0x0B, +}; + +/* + * LS_RJT reason code explanation + */ +enum { + FC_LS_RJT_EXP_NO_ADDL_INFO = 0x00, + FC_LS_RJT_EXP_SPARMS_ERR_OPTIONS = 0x01, + FC_LS_RJT_EXP_SPARMS_ERR_INI_CTL = 0x03, + FC_LS_RJT_EXP_SPARMS_ERR_REC_CTL = 0x05, + FC_LS_RJT_EXP_SPARMS_ERR_RXSZ = 0x07, + FC_LS_RJT_EXP_SPARMS_ERR_CONSEQ = 0x09, + FC_LS_RJT_EXP_SPARMS_ERR_CREDIT = 0x0B, + FC_LS_RJT_EXP_INV_PORT_NAME = 0x0D, + FC_LS_RJT_EXP_INV_NODE_FABRIC_NAME = 0x0E, + FC_LS_RJT_EXP_INV_CSP = 0x0F, + FC_LS_RJT_EXP_INV_ASSOC_HDR = 0x11, + FC_LS_RJT_EXP_ASSOC_HDR_REQD = 0x13, + FC_LS_RJT_EXP_INV_ORIG_S_ID = 0x15, + FC_LS_RJT_EXP_INV_OXID_RXID_COMB = 0x17, + FC_LS_RJT_EXP_CMD_ALREADY_IN_PROG = 0x19, + FC_LS_RJT_EXP_LOGIN_REQUIRED = 0x1E, + FC_LS_RJT_EXP_INVALID_NPORT_ID = 0x1F, + FC_LS_RJT_EXP_INSUFF_RES = 0x29, + FC_LS_RJT_EXP_CMD_NOT_SUPP = 0x2C, + FC_LS_RJT_EXP_INV_PAYLOAD_LEN = 0x2D, +}; + +/* + * RRQ els command payload + */ +struct fc_rrq_s{ + struct fc_els_cmd_s els_cmd; /* ELS command code */ + u32 res1:8; + u32 s_id:24; /* exchange originator S_ID */ + + u32 ox_id:16; /* originator exchange ID */ + u32 rx_id:16; /* responder exchange ID */ + + u32 res2[8]; /* optional association header */ +}; + +/* + * ABTS BA_ACC reply payload + */ +struct fc_ba_acc_s{ + u32 seq_id_valid:8; /* set to 0x00 for Abort Exchange */ + u32 seq_id:8; /* invalid for Abort Exchange */ + u32 res2:16; + u32 ox_id:16; /* OX_ID from ABTS frame */ + u32 rx_id:16; /* RX_ID from ABTS frame */ + u32 low_seq_cnt:16; /* set to 0x0000 for Abort Exchange */ + u32 high_seq_cnt:16;/* set to 0xFFFF for Abort Exchange */ +}; + +/* + * ABTS BA_RJT reject payload + */ +struct fc_ba_rjt_s{ + u32 res1:8; /* Reserved */ + u32 reason_code:8; /* reason code for reject */ + u32 reason_expl:8; /* reason code explanation */ + u32 vendor_unique:8;/* vendor unique reason code,set to 0 */ +}; + +/* + * TPRLO logout parameter page + */ +struct fc_tprlo_params_page_s{ + u32 type:8; + u32 type_ext:8; + +#ifdef __BIGENDIAN + u32 opa_valid:1; + u32 rpa_valid:1; + u32 tpo_nport_valid:1; + u32 global_process_logout:1; + u32 res1:12; +#else + u32 res1:12; + u32 global_process_logout:1; + u32 tpo_nport_valid:1; + u32 rpa_valid:1; + u32 opa_valid:1; +#endif + + u32 orig_process_assc; + u32 resp_process_assc; + + u32 res2:8; + u32 tpo_nport_id; +}; + +/* + * TPRLO ELS command payload + */ +struct fc_tprlo_s{ + u32 command:8; + u32 page_len:8; + u32 payload_len:16; + + struct fc_tprlo_params_page_s tprlo_params[1]; +}; + +enum fc_tprlo_type{ + FC_GLOBAL_LOGO = 1, + FC_TPR_LOGO +}; + +/* + * TPRLO els command ACC payload + */ +struct fc_tprlo_acc_s{ + u32 command:8; + u32 page_len:8; + u32 payload_len:16; + struct fc_prlo_acc_params_page_s tprlo_acc_params[1]; +}; + +/* + * RSCN els command req payload + */ +#define FC_RSCN_PGLEN 0x4 + +enum fc_rscn_format{ + FC_RSCN_FORMAT_PORTID = 0x0, + FC_RSCN_FORMAT_AREA = 0x1, + FC_RSCN_FORMAT_DOMAIN = 0x2, + FC_RSCN_FORMAT_FABRIC = 0x3, +}; + +struct fc_rscn_event_s{ + u32 format:2; + u32 qualifier:4; + u32 resvd:2; + u32 portid:24; +}; + +struct fc_rscn_pl_s{ + u8 command; + u8 pagelen; + u16 payldlen; + struct fc_rscn_event_s event[1]; +}; + +/* + * ECHO els command req payload + */ +struct fc_echo_s { + struct fc_els_cmd_s els_cmd; +}; + +/* + * RNID els command + */ + +#define RNID_NODEID_DATA_FORMAT_COMMON 0x00 +#define RNID_NODEID_DATA_FORMAT_FCP3 0x08 +#define RNID_NODEID_DATA_FORMAT_DISCOVERY 0xDF + +#define RNID_ASSOCIATED_TYPE_UNKNOWN 0x00000001 +#define RNID_ASSOCIATED_TYPE_OTHER 0x00000002 +#define RNID_ASSOCIATED_TYPE_HUB 0x00000003 +#define RNID_ASSOCIATED_TYPE_SWITCH 0x00000004 +#define RNID_ASSOCIATED_TYPE_GATEWAY 0x00000005 +#define RNID_ASSOCIATED_TYPE_STORAGE_DEVICE 0x00000009 +#define RNID_ASSOCIATED_TYPE_HOST 0x0000000A +#define RNID_ASSOCIATED_TYPE_STORAGE_SUBSYSTEM 0x0000000B +#define RNID_ASSOCIATED_TYPE_STORAGE_ACCESS_DEVICE 0x0000000E +#define RNID_ASSOCIATED_TYPE_NAS_SERVER 0x00000011 +#define RNID_ASSOCIATED_TYPE_BRIDGE 0x00000002 +#define RNID_ASSOCIATED_TYPE_VIRTUALIZATION_DEVICE 0x00000003 +#define RNID_ASSOCIATED_TYPE_MULTI_FUNCTION_DEVICE 0x000000FF + +/* + * RNID els command payload + */ +struct fc_rnid_cmd_s{ + struct fc_els_cmd_s els_cmd; + u32 node_id_data_format:8; + u32 reserved:24; +}; + +/* + * RNID els response payload + */ + +struct fc_rnid_common_id_data_s{ + wwn_t port_name; + wwn_t node_name; +}; + +struct fc_rnid_general_topology_data_s{ + u32 vendor_unique[4]; + u32 asso_type; + u32 phy_port_num; + u32 num_attached_nodes; + u32 node_mgmt:8; + u32 ip_version:8; + u32 udp_tcp_port_num:16; + u32 ip_address[4]; + u32 reserved:16; + u32 vendor_specific:16; +}; + +struct fc_rnid_acc_s{ + struct fc_els_cmd_s els_cmd; + u32 node_id_data_format:8; + u32 common_id_data_length:8; + u32 reserved:8; + u32 specific_id_data_length:8; + struct fc_rnid_common_id_data_s common_id_data; + struct fc_rnid_general_topology_data_s gen_topology_data; +}; + +#define RNID_ASSOCIATED_TYPE_UNKNOWN 0x00000001 +#define RNID_ASSOCIATED_TYPE_OTHER 0x00000002 +#define RNID_ASSOCIATED_TYPE_HUB 0x00000003 +#define RNID_ASSOCIATED_TYPE_SWITCH 0x00000004 +#define RNID_ASSOCIATED_TYPE_GATEWAY 0x00000005 +#define RNID_ASSOCIATED_TYPE_STORAGE_DEVICE 0x00000009 +#define RNID_ASSOCIATED_TYPE_HOST 0x0000000A +#define RNID_ASSOCIATED_TYPE_STORAGE_SUBSYSTEM 0x0000000B +#define RNID_ASSOCIATED_TYPE_STORAGE_ACCESS_DEVICE 0x0000000E +#define RNID_ASSOCIATED_TYPE_NAS_SERVER 0x00000011 +#define RNID_ASSOCIATED_TYPE_BRIDGE 0x00000002 +#define RNID_ASSOCIATED_TYPE_VIRTUALIZATION_DEVICE 0x00000003 +#define RNID_ASSOCIATED_TYPE_MULTI_FUNCTION_DEVICE 0x000000FF + +enum fc_rpsc_speed_cap{ + RPSC_SPEED_CAP_1G = 0x8000, + RPSC_SPEED_CAP_2G = 0x4000, + RPSC_SPEED_CAP_4G = 0x2000, + RPSC_SPEED_CAP_10G = 0x1000, + RPSC_SPEED_CAP_8G = 0x0800, + RPSC_SPEED_CAP_16G = 0x0400, + + RPSC_SPEED_CAP_UNKNOWN = 0x0001, +}; + +enum fc_rpsc_op_speed_s{ + RPSC_OP_SPEED_1G = 0x8000, + RPSC_OP_SPEED_2G = 0x4000, + RPSC_OP_SPEED_4G = 0x2000, + RPSC_OP_SPEED_10G = 0x1000, + RPSC_OP_SPEED_8G = 0x0800, + RPSC_OP_SPEED_16G = 0x0400, + + RPSC_OP_SPEED_NOT_EST = 0x0001, /*! speed not established */ +}; + +struct fc_rpsc_speed_info_s{ + u16 port_speed_cap; /*! see fc_rpsc_speed_cap_t */ + u16 port_op_speed; /*! see fc_rpsc_op_speed_t */ +}; + +enum link_e2e_beacon_subcmd{ + LINK_E2E_BEACON_ON = 1, + LINK_E2E_BEACON_OFF = 2 +}; + +enum beacon_type{ + BEACON_TYPE_NORMAL = 1, /*! Normal Beaconing. Green */ + BEACON_TYPE_WARN = 2, /*! Warning Beaconing. Yellow/Amber */ + BEACON_TYPE_CRITICAL = 3 /*! Critical Beaconing. Red */ +}; + +struct link_e2e_beacon_param_s { + u8 beacon_type; /* Beacon Type. See beacon_type_t */ + u8 beacon_frequency; + /* Beacon frequency. Number of blinks + * per 10 seconds + */ + u16 beacon_duration;/* Beacon duration (in Seconds). The + * command operation should be + * terminated at the end of this + * timeout value. + * + * Ignored if diag_sub_cmd is + * LINK_E2E_BEACON_OFF. + * + * If 0, beaconing will continue till a + * BEACON OFF request is received + */ +}; + +/* + * Link E2E beacon request/good response format. For LS_RJTs use fc_ls_rjt_t + */ +struct link_e2e_beacon_req_s{ + u32 ls_code; /*! FC_ELS_E2E_LBEACON in requests * + *or FC_ELS_ACC in good replies */ + u32 ls_sub_cmd; /*! See link_e2e_beacon_subcmd_t */ + struct link_e2e_beacon_param_s beacon_parm; +}; + +/** + * If RPSC request is sent to the Domain Controller, the request is for + * all the ports within that domain (TODO - I don't think FOS implements + * this...). + */ +struct fc_rpsc_cmd_s{ + struct fc_els_cmd_s els_cmd; +}; + +/* + * RPSC Acc + */ +struct fc_rpsc_acc_s{ + u32 command:8; + u32 rsvd:8; + u32 num_entries:16; + + struct fc_rpsc_speed_info_s speed_info[1]; +}; + +/** + * If RPSC2 request is sent to the Domain Controller, + */ +#define FC_BRCD_TOKEN 0x42524344 + +struct fc_rpsc2_cmd_s{ + struct fc_els_cmd_s els_cmd; + u32 token; + u16 resvd; + u16 num_pids; /* Number of pids in the request */ + struct { + u32 rsvd1:8; + u32 pid:24; /* port identifier */ + } pid_list[1]; +}; + +enum fc_rpsc2_port_type{ + RPSC2_PORT_TYPE_UNKNOWN = 0, + RPSC2_PORT_TYPE_NPORT = 1, + RPSC2_PORT_TYPE_NLPORT = 2, + RPSC2_PORT_TYPE_NPIV_PORT = 0x5f, + RPSC2_PORT_TYPE_NPORT_TRUNK = 0x6f, +}; + +/* + * RPSC2 portInfo entry structure + */ +struct fc_rpsc2_port_info_s{ + u32 pid; /* PID */ + u16 resvd1; + u16 index; /* port number / index */ + u8 resvd2; + u8 type; /* port type N/NL/... */ + u16 speed; /* port Operating Speed */ +}; + +/* + * RPSC2 Accept payload + */ +struct fc_rpsc2_acc_s{ + u8 els_cmd; + u8 resvd; + u16 num_pids; /* Number of pids in the request */ + struct fc_rpsc2_port_info_s port_info[1]; /* port information */ +}; + +/** + * bit fields so that multiple classes can be specified + */ +enum fc_cos{ + FC_CLASS_2 = 0x04, + FC_CLASS_3 = 0x08, + FC_CLASS_2_3 = 0x0C, +}; + +/* + * symbolic name + */ +struct fc_symname_s{ + u8 symname[FC_SYMNAME_MAX]; +}; + +struct fc_alpabm_s{ + u8 alpa_bm[FC_ALPA_MAX / 8]; +}; + +/* + * protocol default timeout values + */ +#define FC_ED_TOV 2 +#define FC_REC_TOV (FC_ED_TOV + 1) +#define FC_RA_TOV 10 +#define FC_ELS_TOV (2 * FC_RA_TOV) + +/* + * virtual fabric related defines + */ +#define FC_VF_ID_NULL 0 /* must not be used as VF_ID */ +#define FC_VF_ID_MIN 1 +#define FC_VF_ID_MAX 0xEFF +#define FC_VF_ID_CTL 0xFEF /* control VF_ID */ + +/** + * Virtual Fabric Tagging header format + * @caution This is defined only in BIG ENDIAN format. + */ +struct fc_vft_s{ + u32 r_ctl:8; + u32 ver:2; + u32 type:4; + u32 res_a:2; + u32 priority:3; + u32 vf_id:12; + u32 res_b:1; + u32 hopct:8; + u32 res_c:24; +}; + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/include/protocol/fc_sp.h b/drivers/scsi/bfa/include/protocol/fc_sp.h new file mode 100644 index 000000000000..55bb0b31d04b --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/fc_sp.h @@ -0,0 +1,224 @@ +/* + * 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. + */ + +#ifndef __FC_SP_H__ +#define __FC_SP_H__ + +#include + +#pragma pack(1) + +enum auth_els_flags{ + FC_AUTH_ELS_MORE_FRAGS_FLAG = 0x80, /*! bit-7. More Fragments + * Follow + */ + FC_AUTH_ELS_CONCAT_FLAG = 0x40, /*! bit-6. Concatenation Flag */ + FC_AUTH_ELS_SEQ_NUM_FLAG = 0x01 /*! bit-0. Sequence Number */ +}; + +enum auth_msg_codes{ + FC_AUTH_MC_AUTH_RJT = 0x0A, /*! Auth Reject */ + FC_AUTH_MC_AUTH_NEG = 0x0B, /*! Auth Negotiate */ + FC_AUTH_MC_AUTH_DONE = 0x0C, /*! Auth Done */ + + FC_AUTH_MC_DHCHAP_CHAL = 0x10, /*! DHCHAP Challenge */ + FC_AUTH_MC_DHCHAP_REPLY = 0x11, /*! DHCHAP Reply */ + FC_AUTH_MC_DHCHAP_SUCC = 0x12, /*! DHCHAP Success */ + + FC_AUTH_MC_FCAP_REQ = 0x13, /*! FCAP Request */ + FC_AUTH_MC_FCAP_ACK = 0x14, /*! FCAP Acknowledge */ + FC_AUTH_MC_FCAP_CONF = 0x15, /*! FCAP Confirm */ + + FC_AUTH_MC_FCPAP_INIT = 0x16, /*! FCPAP Init */ + FC_AUTH_MC_FCPAP_ACC = 0x17, /*! FCPAP Accept */ + FC_AUTH_MC_FCPAP_COMP = 0x18, /*! FCPAP Complete */ + + FC_AUTH_MC_IKE_SA_INIT = 0x22, /*! IKE SA INIT */ + FC_AUTH_MC_IKE_SA_AUTH = 0x23, /*! IKE SA Auth */ + FC_AUTH_MC_IKE_CREATE_CHILD_SA = 0x24, /*! IKE Create Child SA */ + FC_AUTH_MC_IKE_INFO = 0x25, /*! IKE informational */ +}; + +enum auth_proto_version{ + FC_AUTH_PROTO_VER_1 = 1, /*! Protocol Version 1 */ +}; + +enum { + FC_AUTH_ELS_COMMAND_CODE = 0x90,/*! Authentication ELS Command code */ + FC_AUTH_PROTO_PARAM_LEN_SZ = 4, /*! Size of Proto Parameter Len Field */ + FC_AUTH_PROTO_PARAM_VAL_SZ = 4, /*! Size of Proto Parameter Val Field */ + FC_MAX_AUTH_SECRET_LEN = 256, + /*! Maximum secret string length */ + FC_AUTH_NUM_USABLE_PROTO_LEN_SZ = 4, + /*! Size of usable protocols field */ + FC_AUTH_RESP_VALUE_LEN_SZ = 4, + /*! Size of response value length */ + FC_MAX_CHAP_KEY_LEN = 256, /*! Maximum md5 digest length */ + FC_MAX_AUTH_RETRIES = 3, /*! Maximum number of retries */ + FC_MD5_DIGEST_LEN = 16, /*! MD5 digest length */ + FC_SHA1_DIGEST_LEN = 20, /*! SHA1 digest length */ + FC_MAX_DHG_SUPPORTED = 1, /*! Maximum DH Groups supported */ + FC_MAX_ALG_SUPPORTED = 1, /*! Maximum algorithms supported */ + FC_MAX_PROTO_SUPPORTED = 1, /*! Maximum protocols supported */ + FC_START_TXN_ID = 2, /*! Starting transaction ID */ +}; + +enum auth_proto_id{ + FC_AUTH_PROTO_DHCHAP = 0x00000001, + FC_AUTH_PROTO_FCAP = 0x00000002, + FC_AUTH_PROTO_FCPAP = 0x00000003, + FC_AUTH_PROTO_IKEv2 = 0x00000004, + FC_AUTH_PROTO_IKEv2_AUTH = 0x00000005, +}; + +struct auth_name_s{ + u16 name_tag; /*! Name Tag = 1 for Authentication */ + u16 name_len; /*! Name Length = 8 for Authentication + */ + wwn_t name; /*! Name. TODO - is this PWWN */ +}; + + +enum auth_hash_func{ + FC_AUTH_HASH_FUNC_MD5 = 0x00000005, + FC_AUTH_HASH_FUNC_SHA_1 = 0x00000006, +}; + +enum auth_dh_gid{ + FC_AUTH_DH_GID_0_DHG_NULL = 0x00000000, + FC_AUTH_DH_GID_1_DHG_1024 = 0x00000001, + FC_AUTH_DH_GID_2_DHG_1280 = 0x00000002, + FC_AUTH_DH_GID_3_DHG_1536 = 0x00000003, + FC_AUTH_DH_GID_4_DHG_2048 = 0x00000004, + FC_AUTH_DH_GID_6_DHG_3072 = 0x00000006, + FC_AUTH_DH_GID_7_DHG_4096 = 0x00000007, + FC_AUTH_DH_GID_8_DHG_6144 = 0x00000008, + FC_AUTH_DH_GID_9_DHG_8192 = 0x00000009, +}; + +struct auth_els_msg_s { + u8 auth_els_code; /* Authentication ELS Code (0x90) */ + u8 auth_els_flag; /* Authentication ELS Flags */ + u8 auth_msg_code; /* Authentication Message Code */ + u8 proto_version; /* Protocol Version */ + u32 msg_len; /* Message Length */ + u32 trans_id; /* Transaction Identifier (T_ID) */ + + /* Msg payload follows... */ +}; + + +enum auth_neg_param_tags { + FC_AUTH_NEG_DHCHAP_HASHLIST = 0x0001, + FC_AUTH_NEG_DHCHAP_DHG_ID_LIST = 0x0002, +}; + + +struct dhchap_param_format_s { + u16 tag; /*! Parameter Tag. See + * auth_neg_param_tags_t + */ + u16 word_cnt; + + /* followed by variable length parameter value... */ +}; + +struct auth_proto_params_s { + u32 proto_param_len; + u32 proto_id; + + /* + * Followed by variable length Protocol specific parameters. DH-CHAP + * uses dhchap_param_format_t + */ +}; + +struct auth_neg_msg_s { + struct auth_name_s auth_ini_name; + u32 usable_auth_protos; + struct auth_proto_params_s proto_params[1]; /*! (1..usable_auth_proto) + * protocol params + */ +}; + +struct auth_dh_val_s { + u32 dh_val_len; + u32 dh_val[1]; +}; + +struct auth_dhchap_chal_msg_s { + struct auth_els_msg_s hdr; + struct auth_name_s auth_responder_name; /* TODO VRK - is auth_name_t + * type OK? + */ + u32 hash_id; + u32 dh_grp_id; + u32 chal_val_len; + char chal_val[1]; + + /* ...followed by variable Challenge length/value and DH length/value */ +}; + + +enum auth_rjt_codes { + FC_AUTH_RJT_CODE_AUTH_FAILURE = 0x01, + FC_AUTH_RJT_CODE_LOGICAL_ERR = 0x02, +}; + +enum auth_rjt_code_exps { + FC_AUTH_CEXP_AUTH_MECH_NOT_USABLE = 0x01, + FC_AUTH_CEXP_DH_GROUP_NOT_USABLE = 0x02, + FC_AUTH_CEXP_HASH_FUNC_NOT_USABLE = 0x03, + FC_AUTH_CEXP_AUTH_XACT_STARTED = 0x04, + FC_AUTH_CEXP_AUTH_FAILED = 0x05, + FC_AUTH_CEXP_INCORRECT_PLD = 0x06, + FC_AUTH_CEXP_INCORRECT_PROTO_MSG = 0x07, + FC_AUTH_CEXP_RESTART_AUTH_PROTO = 0x08, + FC_AUTH_CEXP_AUTH_CONCAT_NOT_SUPP = 0x09, + FC_AUTH_CEXP_PROTO_VER_NOT_SUPP = 0x0A, +}; + +enum auth_status { + FC_AUTH_STATE_INPROGRESS = 0, /*! authentication in progress */ + FC_AUTH_STATE_FAILED = 1, /*! authentication failed */ + FC_AUTH_STATE_SUCCESS = 2 /*! authentication successful */ +}; + +struct auth_rjt_msg_s { + struct auth_els_msg_s hdr; + u8 reason_code; + u8 reason_code_exp; + u8 rsvd[2]; +}; + + +struct auth_dhchap_neg_msg_s { + struct auth_els_msg_s hdr; + struct auth_neg_msg_s nego; +}; + +struct auth_dhchap_reply_msg_s { + struct auth_els_msg_s hdr; + + /* + * followed by response value length & Value + DH Value Length & Value + */ +}; + +#pragma pack() + +#endif /* __FC_SP_H__ */ diff --git a/drivers/scsi/bfa/include/protocol/fcp.h b/drivers/scsi/bfa/include/protocol/fcp.h new file mode 100644 index 000000000000..9ade68ad2853 --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/fcp.h @@ -0,0 +1,186 @@ +/* + * 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. + */ + +#ifndef __FCPPROTO_H__ +#define __FCPPROTO_H__ + +#include + +#pragma pack(1) + +enum { + FCP_RJT = 0x01000000, /* SRR reject */ + FCP_SRR_ACCEPT = 0x02000000, /* SRR accept */ + FCP_SRR = 0x14000000, /* Sequence Retransmission Request */ +}; + +/* + * SRR FC-4 LS payload + */ +struct fc_srr_s{ + u32 ls_cmd; + u32 ox_id:16; /* ox-id */ + u32 rx_id:16; /* rx-id */ + u32 ro; /* relative offset */ + u32 r_ctl:8; /* R_CTL for I.U. */ + u32 res:24; +}; + + +/* + * FCP_CMND definitions + */ +#define FCP_CMND_CDB_LEN 16 +#define FCP_CMND_LUN_LEN 8 + +struct fcp_cmnd_s{ + lun_t lun; /* 64-bit LU number */ + u8 crn; /* command reference number */ +#ifdef __BIGENDIAN + u8 resvd:1, + priority:4, /* FCP-3: SAM-3 priority */ + taskattr:3; /* scsi task attribute */ +#else + u8 taskattr:3, /* scsi task attribute */ + priority:4, /* FCP-3: SAM-3 priority */ + resvd:1; +#endif + u8 tm_flags; /* task management flags */ +#ifdef __BIGENDIAN + u8 addl_cdb_len:6, /* additional CDB length words */ + iodir:2; /* read/write FCP_DATA IUs */ +#else + u8 iodir:2, /* read/write FCP_DATA IUs */ + addl_cdb_len:6; /* additional CDB length */ +#endif + struct scsi_cdb_s cdb; + + /* + * !!! additional cdb bytes follows here!!! + */ + u32 fcp_dl; /* bytes to be transferred */ +}; + +#define fcp_cmnd_cdb_len(_cmnd) ((_cmnd)->addl_cdb_len * 4 + FCP_CMND_CDB_LEN) +#define fcp_cmnd_fcpdl(_cmnd) ((&(_cmnd)->fcp_dl)[(_cmnd)->addl_cdb_len]) + +/* + * fcp_cmnd_t.iodir field values + */ +enum fcp_iodir{ + FCP_IODIR_NONE = 0, + FCP_IODIR_WRITE = 1, + FCP_IODIR_READ = 2, + FCP_IODIR_RW = 3, +}; + +/* + * Task attribute field + */ +enum { + FCP_TASK_ATTR_SIMPLE = 0, + FCP_TASK_ATTR_HOQ = 1, + FCP_TASK_ATTR_ORDERED = 2, + FCP_TASK_ATTR_ACA = 4, + FCP_TASK_ATTR_UNTAGGED = 5, /* obsolete in FCP-3 */ +}; + +/* + * Task management flags field - only one bit shall be set + */ +#ifndef BIT +#define BIT(_x) (1 << (_x)) +#endif +enum fcp_tm_cmnd{ + FCP_TM_ABORT_TASK_SET = BIT(1), + FCP_TM_CLEAR_TASK_SET = BIT(2), + FCP_TM_LUN_RESET = BIT(4), + FCP_TM_TARGET_RESET = BIT(5), /* obsolete in FCP-3 */ + FCP_TM_CLEAR_ACA = BIT(6), +}; + +/* + * FCP_XFER_RDY IU defines + */ +struct fcp_xfer_rdy_s{ + u32 data_ro; + u32 burst_len; + u32 reserved; +}; + +/* + * FCP_RSP residue flags + */ +enum fcp_residue{ + FCP_NO_RESIDUE = 0, /* no residue */ + FCP_RESID_OVER = 1, /* more data left that was not sent */ + FCP_RESID_UNDER = 2, /* less data than requested */ +}; + +enum { + FCP_RSPINFO_GOOD = 0, + FCP_RSPINFO_DATALEN_MISMATCH = 1, + FCP_RSPINFO_CMND_INVALID = 2, + FCP_RSPINFO_ROLEN_MISMATCH = 3, + FCP_RSPINFO_TM_NOT_SUPP = 4, + FCP_RSPINFO_TM_FAILED = 5, +}; + +struct fcp_rspinfo_s{ + u32 res0:24; + u32 rsp_code:8; /* response code (as above) */ + u32 res1; +}; + +struct fcp_resp_s{ + u32 reserved[2]; /* 2 words reserved */ + u16 reserved2; +#ifdef __BIGENDIAN + u8 reserved3:3; + u8 fcp_conf_req:1; /* FCP_CONF is requested */ + u8 resid_flags:2; /* underflow/overflow */ + u8 sns_len_valid:1;/* sense len is valid */ + u8 rsp_len_valid:1;/* response len is valid */ +#else + u8 rsp_len_valid:1;/* response len is valid */ + u8 sns_len_valid:1;/* sense len is valid */ + u8 resid_flags:2; /* underflow/overflow */ + u8 fcp_conf_req:1; /* FCP_CONF is requested */ + u8 reserved3:3; +#endif + u8 scsi_status; /* one byte SCSI status */ + u32 residue; /* residual data bytes */ + u32 sns_len; /* length od sense info */ + u32 rsp_len; /* length of response info */ +}; + +#define fcp_snslen(__fcprsp) ((__fcprsp)->sns_len_valid ? \ + (__fcprsp)->sns_len : 0) +#define fcp_rsplen(__fcprsp) ((__fcprsp)->rsp_len_valid ? \ + (__fcprsp)->rsp_len : 0) +#define fcp_rspinfo(__fcprsp) ((struct fcp_rspinfo_s *)((__fcprsp) + 1)) +#define fcp_snsinfo(__fcprsp) (((u8 *)fcp_rspinfo(__fcprsp)) + \ + fcp_rsplen(__fcprsp)) + +struct fcp_cmnd_fr_s{ + struct fchs_s fchs; + struct fcp_cmnd_s fcp; +}; + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/include/protocol/fdmi.h b/drivers/scsi/bfa/include/protocol/fdmi.h new file mode 100644 index 000000000000..6c05c268c71b --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/fdmi.h @@ -0,0 +1,163 @@ +/* + * 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. + */ + +#ifndef __FDMI_H__ +#define __FDMI_H__ + +#include +#include +#include + +#pragma pack(1) + +/* + * FDMI Command Codes + */ +#define FDMI_GRHL 0x0100 +#define FDMI_GHAT 0x0101 +#define FDMI_GRPL 0x0102 +#define FDMI_GPAT 0x0110 +#define FDMI_RHBA 0x0200 +#define FDMI_RHAT 0x0201 +#define FDMI_RPRT 0x0210 +#define FDMI_RPA 0x0211 +#define FDMI_DHBA 0x0300 +#define FDMI_DPRT 0x0310 + +/* + * FDMI reason codes + */ +#define FDMI_NO_ADDITIONAL_EXP 0x00 +#define FDMI_HBA_ALREADY_REG 0x10 +#define FDMI_HBA_ATTRIB_NOT_REG 0x11 +#define FDMI_HBA_ATTRIB_MULTIPLE 0x12 +#define FDMI_HBA_ATTRIB_LENGTH_INVALID 0x13 +#define FDMI_HBA_ATTRIB_NOT_PRESENT 0x14 +#define FDMI_PORT_ORIG_NOT_IN_LIST 0x15 +#define FDMI_PORT_HBA_NOT_IN_LIST 0x16 +#define FDMI_PORT_ATTRIB_NOT_REG 0x20 +#define FDMI_PORT_NOT_REG 0x21 +#define FDMI_PORT_ATTRIB_MULTIPLE 0x22 +#define FDMI_PORT_ATTRIB_LENGTH_INVALID 0x23 +#define FDMI_PORT_ALREADY_REGISTEREED 0x24 + +/* + * FDMI Transmission Speed Mask values + */ +#define FDMI_TRANS_SPEED_1G 0x00000001 +#define FDMI_TRANS_SPEED_2G 0x00000002 +#define FDMI_TRANS_SPEED_10G 0x00000004 +#define FDMI_TRANS_SPEED_4G 0x00000008 +#define FDMI_TRANS_SPEED_8G 0x00000010 +#define FDMI_TRANS_SPEED_16G 0x00000020 +#define FDMI_TRANS_SPEED_UNKNOWN 0x00008000 + +/* + * FDMI HBA attribute types + */ +enum fdmi_hba_attribute_type { + FDMI_HBA_ATTRIB_NODENAME = 1, /* 0x0001 */ + FDMI_HBA_ATTRIB_MANUFACTURER, /* 0x0002 */ + FDMI_HBA_ATTRIB_SERIALNUM, /* 0x0003 */ + FDMI_HBA_ATTRIB_MODEL, /* 0x0004 */ + FDMI_HBA_ATTRIB_MODEL_DESC, /* 0x0005 */ + FDMI_HBA_ATTRIB_HW_VERSION, /* 0x0006 */ + FDMI_HBA_ATTRIB_DRIVER_VERSION, /* 0x0007 */ + FDMI_HBA_ATTRIB_ROM_VERSION, /* 0x0008 */ + FDMI_HBA_ATTRIB_FW_VERSION, /* 0x0009 */ + FDMI_HBA_ATTRIB_OS_NAME, /* 0x000A */ + FDMI_HBA_ATTRIB_MAX_CT, /* 0x000B */ + + FDMI_HBA_ATTRIB_MAX_TYPE +}; + +/* + * FDMI Port attribute types + */ +enum fdmi_port_attribute_type { + FDMI_PORT_ATTRIB_FC4_TYPES = 1, /* 0x0001 */ + FDMI_PORT_ATTRIB_SUPP_SPEED, /* 0x0002 */ + FDMI_PORT_ATTRIB_PORT_SPEED, /* 0x0003 */ + FDMI_PORT_ATTRIB_FRAME_SIZE, /* 0x0004 */ + FDMI_PORT_ATTRIB_DEV_NAME, /* 0x0005 */ + FDMI_PORT_ATTRIB_HOST_NAME, /* 0x0006 */ + + FDMI_PORT_ATTR_MAX_TYPE +}; + +/* + * FDMI attribute + */ +struct fdmi_attr_s { + u16 type; + u16 len; + u8 value[1]; +}; + +/* + * HBA Attribute Block + */ +struct fdmi_hba_attr_s { + u32 attr_count; /* # of attributes */ + struct fdmi_attr_s hba_attr; /* n attributes */ +}; + +/* + * Registered Port List + */ +struct fdmi_port_list_s { + u32 num_ports; /* number Of Port Entries */ + wwn_t port_entry; /* one or more */ +}; + +/* + * Port Attribute Block + */ +struct fdmi_port_attr_s { + u32 attr_count; /* # of attributes */ + struct fdmi_attr_s port_attr; /* n attributes */ +}; + +/* + * FDMI Register HBA Attributes + */ +struct fdmi_rhba_s { + wwn_t hba_id; /* HBA Identifier */ + struct fdmi_port_list_s port_list; /* Registered Port List */ + struct fdmi_hba_attr_s hba_attr_blk; /* HBA attribute block */ +}; + +/* + * FDMI Register Port + */ +struct fdmi_rprt_s { + wwn_t hba_id; /* HBA Identifier */ + wwn_t port_name; /* Port wwn */ + struct fdmi_port_attr_s port_attr_blk; /* Port Attr Block */ +}; + +/* + * FDMI Register Port Attributes + */ +struct fdmi_rpa_s { + wwn_t port_name; /* port wwn */ + struct fdmi_port_attr_s port_attr_blk; /* Port Attr Block */ +}; + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/include/protocol/pcifw.h b/drivers/scsi/bfa/include/protocol/pcifw.h new file mode 100644 index 000000000000..6830dc3ee58a --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/pcifw.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +/** + * pcifw.h PCI FW related headers + */ + +#ifndef __PCIFW_H__ +#define __PCIFW_H__ + +#pragma pack(1) + +struct pnp_hdr_s{ + u32 signature; /* "$PnP" */ + u8 rev; /* Struct revision */ + u8 len; /* Header structure len in multiples + * of 16 bytes */ + u16 off; /* Offset to next header 00 if none */ + u8 rsvd; /* Reserved byte */ + u8 cksum; /* 8-bit checksum for this header */ + u32 pnp_dev_id; /* PnP Device Id */ + u16 mfstr; /* Pointer to manufacturer string */ + u16 prstr; /* Pointer to product string */ + u8 devtype[3]; /* Device Type Code */ + u8 devind; /* Device Indicator */ + u16 bcventr; /* Bootstrap entry vector */ + u16 rsvd2; /* Reserved */ + u16 sriv; /* Static resource information vector */ +}; + +struct pci_3_0_ds_s{ + u32 sig; /* Signature "PCIR" */ + u16 vendid; /* Vendor ID */ + u16 devid; /* Device ID */ + u16 devlistoff; /* Device List Offset */ + u16 len; /* PCI Data Structure Length */ + u8 rev; /* PCI Data Structure Revision */ + u8 clcode[3]; /* Class Code */ + u16 imglen; /* Code image length in multiples of + * 512 bytes */ + u16 coderev; /* Revision level of code/data */ + u8 codetype; /* Code type 0x00 - BIOS */ + u8 indr; /* Last image indicator */ + u16 mrtimglen; /* Max Run Time Image Length */ + u16 cuoff; /* Config Utility Code Header Offset */ + u16 dmtfclp; /* DMTF CLP entry point offset */ +}; + +struct pci_optrom_hdr_s{ + u16 sig; /* Signature 0x55AA */ + u8 len; /* Option ROM length in units of 512 bytes */ + u8 inivec[3]; /* Initialization vector */ + u8 rsvd[16]; /* Reserved field */ + u16 verptr; /* Pointer to version string - private */ + u16 pcids; /* Pointer to PCI data structure */ + u16 pnphdr; /* Pointer to PnP expansion header */ +}; + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/include/protocol/scsi.h b/drivers/scsi/bfa/include/protocol/scsi.h new file mode 100644 index 000000000000..b220e6b4f6e1 --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/scsi.h @@ -0,0 +1,1648 @@ +/* + * 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. + */ + +#ifndef __SCSI_H__ +#define __SCSI_H__ + +#include + +#pragma pack(1) + +/* + * generic SCSI cdb definition + */ +#define SCSI_MAX_CDBLEN 16 +struct scsi_cdb_s{ + u8 scsi_cdb[SCSI_MAX_CDBLEN]; +}; + +/* + * scsi lun serial number definition + */ +#define SCSI_LUN_SN_LEN 32 +struct scsi_lun_sn_s{ + u8 lun_sn[SCSI_LUN_SN_LEN]; +}; + +/* + * SCSI Direct Access Commands + */ +enum { + SCSI_OP_TEST_UNIT_READY = 0x00, + SCSI_OP_REQUEST_SENSE = 0x03, + SCSI_OP_FORMAT_UNIT = 0x04, + SCSI_OP_READ6 = 0x08, + SCSI_OP_WRITE6 = 0x0A, + SCSI_OP_WRITE_FILEMARKS = 0x10, + SCSI_OP_INQUIRY = 0x12, + SCSI_OP_MODE_SELECT6 = 0x15, + SCSI_OP_RESERVE6 = 0x16, + SCSI_OP_RELEASE6 = 0x17, + SCSI_OP_MODE_SENSE6 = 0x1A, + SCSI_OP_START_STOP_UNIT = 0x1B, + SCSI_OP_SEND_DIAGNOSTIC = 0x1D, + SCSI_OP_READ_CAPACITY = 0x25, + SCSI_OP_READ10 = 0x28, + SCSI_OP_WRITE10 = 0x2A, + SCSI_OP_VERIFY10 = 0x2F, + SCSI_OP_READ_DEFECT_DATA = 0x37, + SCSI_OP_LOG_SELECT = 0x4C, + SCSI_OP_LOG_SENSE = 0x4D, + SCSI_OP_MODE_SELECT10 = 0x55, + SCSI_OP_RESERVE10 = 0x56, + SCSI_OP_RELEASE10 = 0x57, + SCSI_OP_MODE_SENSE10 = 0x5A, + SCSI_OP_PER_RESERVE_IN = 0x5E, + SCSI_OP_PER_RESERVE_OUR = 0x5E, + SCSI_OP_READ16 = 0x88, + SCSI_OP_WRITE16 = 0x8A, + SCSI_OP_VERIFY16 = 0x8F, + SCSI_OP_READ_CAPACITY16 = 0x9E, + SCSI_OP_REPORT_LUNS = 0xA0, + SCSI_OP_READ12 = 0xA8, + SCSI_OP_WRITE12 = 0xAA, + SCSI_OP_UNDEF = 0xFF, +}; + +/* + * SCSI START_STOP_UNIT command + */ +struct scsi_start_stop_unit_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 reserved1:4; + u8 immed:1; +#else + u8 immed:1; + u8 reserved1:4; + u8 lun:3; +#endif + u8 reserved2; + u8 reserved3; +#ifdef __BIGENDIAN + u8 power_conditions:4; + u8 reserved4:2; + u8 loEj:1; + u8 start:1; +#else + u8 start:1; + u8 loEj:1; + u8 reserved4:2; + u8 power_conditions:4; +#endif + u8 control; +}; + +/* + * SCSI SEND_DIAGNOSTIC command + */ +struct scsi_send_diagnostic_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 self_test_code:3; + u8 pf:1; + u8 reserved1:1; + u8 self_test:1; + u8 dev_offl:1; + u8 unit_offl:1; +#else + u8 unit_offl:1; + u8 dev_offl:1; + u8 self_test:1; + u8 reserved1:1; + u8 pf:1; + u8 self_test_code:3; +#endif + u8 reserved2; + + u8 param_list_length[2]; /* MSB first */ + u8 control; + +}; + +/* + * SCSI READ10/WRITE10 commands + */ +struct scsi_rw10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 dpo:1; /* Disable Page Out */ + u8 fua:1; /* Force Unit Access */ + u8 reserved1:2; + u8 rel_adr:1; /* relative address */ +#else + u8 rel_adr:1; + u8 reserved1:2; + u8 fua:1; + u8 dpo:1; + u8 lun:3; +#endif + u8 lba0; /* logical block address - MSB */ + u8 lba1; + u8 lba2; + u8 lba3; /* LSB */ + u8 reserved3; + u8 xfer_length0; /* transfer length in blocks - MSB */ + u8 xfer_length1; /* LSB */ + u8 control; +}; + +#define SCSI_CDB10_GET_LBA(cdb) \ + (((cdb)->lba0 << 24) | ((cdb)->lba1 << 16) | \ + ((cdb)->lba2 << 8) | (cdb)->lba3) + +#define SCSI_CDB10_SET_LBA(cdb, lba) { \ + (cdb)->lba0 = lba >> 24; \ + (cdb)->lba1 = (lba >> 16) & 0xFF; \ + (cdb)->lba2 = (lba >> 8) & 0xFF; \ + (cdb)->lba3 = lba & 0xFF; \ +} + +#define SCSI_CDB10_GET_TL(cdb) \ + ((cdb)->xfer_length0 << 8 | (cdb)->xfer_length1) +#define SCSI_CDB10_SET_TL(cdb, tl) { \ + (cdb)->xfer_length0 = tl >> 8; \ + (cdb)->xfer_length1 = tl & 0xFF; \ +} + +/* + * SCSI READ6/WRITE6 commands + */ +struct scsi_rw6_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 lba0:5; /* MSb */ +#else + u8 lba0:5; /* MSb */ + u8 lun:3; +#endif + u8 lba1; + u8 lba2; /* LSB */ + u8 xfer_length; + u8 control; +}; + +#define SCSI_TAPE_CDB6_GET_TL(cdb) \ + (((cdb)->tl0 << 16) | ((cdb)->tl1 << 8) | (cdb)->tl2) + +#define SCSI_TAPE_CDB6_SET_TL(cdb, tl) { \ + (cdb)->tl0 = tl >> 16; \ + (cdb)->tl1 = (tl >> 8) & 0xFF; \ + (cdb)->tl2 = tl & 0xFF; \ +} + +/* + * SCSI sequential (TAPE) wrtie command + */ +struct scsi_tape_wr_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 rsvd:7; + u8 fixed:1; /* MSb */ +#else + u8 fixed:1; /* MSb */ + u8 rsvd:7; +#endif + u8 tl0; /* Msb */ + u8 tl1; + u8 tl2; /* Lsb */ + + u8 control; +}; + +#define SCSI_CDB6_GET_LBA(cdb) \ + (((cdb)->lba0 << 16) | ((cdb)->lba1 << 8) | (cdb)->lba2) + +#define SCSI_CDB6_SET_LBA(cdb, lba) { \ + (cdb)->lba0 = lba >> 16; \ + (cdb)->lba1 = (lba >> 8) & 0xFF; \ + (cdb)->lba2 = lba & 0xFF; \ +} + +#define SCSI_CDB6_GET_TL(cdb) ((cdb)->xfer_length) +#define SCSI_CDB6_SET_TL(cdb, tl) { \ + (cdb)->xfer_length = tl; \ +} + +/* + * SCSI sense data format + */ +struct scsi_sense_s{ +#ifdef __BIGENDIAN + u8 valid:1; + u8 rsp_code:7; +#else + u8 rsp_code:7; + u8 valid:1; +#endif + u8 seg_num; +#ifdef __BIGENDIAN + u8 file_mark:1; + u8 eom:1; /* end of media */ + u8 ili:1; /* incorrect length indicator */ + u8 reserved:1; + u8 sense_key:4; +#else + u8 sense_key:4; + u8 reserved:1; + u8 ili:1; /* incorrect length indicator */ + u8 eom:1; /* end of media */ + u8 file_mark:1; +#endif + u8 information[4]; /* device-type or command specific info + */ + u8 add_sense_length; + /* additional sense length */ + u8 command_info[4];/* command specific information + */ + u8 asc; /* additional sense code */ + u8 ascq; /* additional sense code qualifier */ + u8 fru_code; /* field replaceable unit code */ +#ifdef __BIGENDIAN + u8 sksv:1; /* sense key specific valid */ + u8 c_d:1; /* command/data bit */ + u8 res1:2; + u8 bpv:1; /* bit pointer valid */ + u8 bpointer:3; /* bit pointer */ +#else + u8 bpointer:3; /* bit pointer */ + u8 bpv:1; /* bit pointer valid */ + u8 res1:2; + u8 c_d:1; /* command/data bit */ + u8 sksv:1; /* sense key specific valid */ +#endif + u8 fpointer[2]; /* field pointer */ +}; + +#define SCSI_SENSE_CUR_ERR 0x70 +#define SCSI_SENSE_DEF_ERR 0x71 + +/* + * SCSI sense key values + */ +#define SCSI_SK_NO_SENSE 0x0 +#define SCSI_SK_REC_ERR 0x1 /* recovered error */ +#define SCSI_SK_NOT_READY 0x2 +#define SCSI_SK_MED_ERR 0x3 /* medium error */ +#define SCSI_SK_HW_ERR 0x4 /* hardware error */ +#define SCSI_SK_ILLEGAL_REQ 0x5 +#define SCSI_SK_UNIT_ATT 0x6 /* unit attention */ +#define SCSI_SK_DATA_PROTECT 0x7 +#define SCSI_SK_BLANK_CHECK 0x8 +#define SCSI_SK_VENDOR_SPEC 0x9 +#define SCSI_SK_COPY_ABORTED 0xA +#define SCSI_SK_ABORTED_CMND 0xB +#define SCSI_SK_VOL_OVERFLOW 0xD +#define SCSI_SK_MISCOMPARE 0xE + +/* + * SCSI additional sense codes + */ +#define SCSI_ASC_NO_ADD_SENSE 0x00 +#define SCSI_ASC_LUN_NOT_READY 0x04 +#define SCSI_ASC_LUN_COMMUNICATION 0x08 +#define SCSI_ASC_WRITE_ERROR 0x0C +#define SCSI_ASC_INVALID_CMND_CODE 0x20 +#define SCSI_ASC_BAD_LBA 0x21 +#define SCSI_ASC_INVALID_FIELD_IN_CDB 0x24 +#define SCSI_ASC_LUN_NOT_SUPPORTED 0x25 +#define SCSI_ASC_LUN_WRITE_PROTECT 0x27 +#define SCSI_ASC_POWERON_BDR 0x29 /* power on reset, bus reset, + * bus device reset + */ +#define SCSI_ASC_PARAMS_CHANGED 0x2A +#define SCSI_ASC_CMND_CLEARED_BY_A_I 0x2F +#define SCSI_ASC_SAVING_PARAM_NOTSUPP 0x39 +#define SCSI_ASC_TOCC 0x3F /* target operating condtions + * changed + */ +#define SCSI_ASC_PARITY_ERROR 0x47 +#define SCSI_ASC_CMND_PHASE_ERROR 0x4A +#define SCSI_ASC_DATA_PHASE_ERROR 0x4B +#define SCSI_ASC_VENDOR_SPEC 0x7F + +/* + * SCSI additional sense code qualifiers + */ +#define SCSI_ASCQ_CAUSE_NOT_REPORT 0x00 +#define SCSI_ASCQ_BECOMING_READY 0x01 +#define SCSI_ASCQ_INIT_CMD_REQ 0x02 +#define SCSI_ASCQ_FORMAT_IN_PROGRESS 0x04 +#define SCSI_ASCQ_OPERATION_IN_PROGRESS 0x07 +#define SCSI_ASCQ_SELF_TEST_IN_PROGRESS 0x09 +#define SCSI_ASCQ_WR_UNEXP_UNSOL_DATA 0x0C +#define SCSI_ASCQ_WR_NOTENG_UNSOL_DATA 0x0D + +#define SCSI_ASCQ_LBA_OUT_OF_RANGE 0x00 +#define SCSI_ASCQ_INVALID_ELEMENT_ADDR 0x01 + +#define SCSI_ASCQ_LUN_WRITE_PROTECTED 0x00 +#define SCSI_ASCQ_LUN_HW_WRITE_PROTECTED 0x01 +#define SCSI_ASCQ_LUN_SW_WRITE_PROTECTED 0x02 + +#define SCSI_ASCQ_POR 0x01 /* power on reset */ +#define SCSI_ASCQ_SBR 0x02 /* scsi bus reset */ +#define SCSI_ASCQ_BDR 0x03 /* bus device reset */ +#define SCSI_ASCQ_DIR 0x04 /* device internal reset */ + +#define SCSI_ASCQ_MODE_PARAMS_CHANGED 0x01 +#define SCSI_ASCQ_LOG_PARAMS_CHANGED 0x02 +#define SCSI_ASCQ_RESERVATIONS_PREEMPTED 0x03 +#define SCSI_ASCQ_RESERVATIONS_RELEASED 0x04 +#define SCSI_ASCQ_REGISTRATIONS_PREEMPTED 0x05 + +#define SCSI_ASCQ_MICROCODE_CHANGED 0x01 +#define SCSI_ASCQ_CHANGED_OPER_COND 0x02 +#define SCSI_ASCQ_INQ_CHANGED 0x03 /* inquiry data changed */ +#define SCSI_ASCQ_DI_CHANGED 0x05 /* device id changed */ +#define SCSI_ASCQ_RL_DATA_CHANGED 0x0E /* report luns data changed */ + +#define SCSI_ASCQ_DP_CRC_ERR 0x01 /* data phase crc error */ +#define SCSI_ASCQ_DP_SCSI_PARITY_ERR 0x02 /* data phase scsi parity error + */ +#define SCSI_ASCQ_IU_CRC_ERR 0x03 /* information unit crc error */ +#define SCSI_ASCQ_PROTO_SERV_CRC_ERR 0x05 + +#define SCSI_ASCQ_LUN_TIME_OUT 0x01 + +/* ------------------------------------------------------------ + * SCSI INQUIRY + * ------------------------------------------------------------*/ + +struct scsi_inquiry_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 reserved1:3; + u8 cmd_dt:1; + u8 evpd:1; +#else + u8 evpd:1; + u8 cmd_dt:1; + u8 reserved1:3; + u8 lun:3; +#endif + u8 page_code; + u8 reserved2; + u8 alloc_length; + u8 control; +}; + +struct scsi_inquiry_vendor_s{ + u8 vendor_id[8]; +}; + +struct scsi_inquiry_prodid_s{ + u8 product_id[16]; +}; + +struct scsi_inquiry_prodrev_s{ + u8 product_rev[4]; +}; + +struct scsi_inquiry_data_s{ +#ifdef __BIGENDIAN + u8 peripheral_qual:3; /* peripheral qualifier */ + u8 device_type:5; /* peripheral device type */ + + u8 rmb:1; /* removable medium bit */ + u8 device_type_mod:7; /* device type modifier */ + + u8 version; + + u8 aenc:1; /* async event notification capability + */ + u8 trm_iop:1; /* terminate I/O process */ + u8 norm_aca:1; /* normal ACA supported */ + u8 hi_support:1; /* SCSI-3: supports REPORT LUNS */ + u8 rsp_data_format:4; + + u8 additional_len; + u8 sccs:1; + u8 reserved1:7; + + u8 reserved2:1; + u8 enc_serv:1; /* enclosure service component */ + u8 reserved3:1; + u8 multi_port:1; /* multi-port device */ + u8 m_chngr:1; /* device in medium transport element */ + u8 ack_req_q:1; /* SIP specific bit */ + u8 addr32:1; /* SIP specific bit */ + u8 addr16:1; /* SIP specific bit */ + + u8 rel_adr:1; /* relative address */ + u8 w_bus32:1; + u8 w_bus16:1; + u8 synchronous:1; + u8 linked_commands:1; + u8 trans_dis:1; + u8 cmd_queue:1; /* command queueing supported */ + u8 soft_reset:1; /* soft reset alternative (VS) */ +#else + u8 device_type:5; /* peripheral device type */ + u8 peripheral_qual:3; + /* peripheral qualifier */ + + u8 device_type_mod:7; + /* device type modifier */ + u8 rmb:1; /* removable medium bit */ + + u8 version; + + u8 rsp_data_format:4; + u8 hi_support:1; /* SCSI-3: supports REPORT LUNS */ + u8 norm_aca:1; /* normal ACA supported */ + u8 terminate_iop:1;/* terminate I/O process */ + u8 aenc:1; /* async event notification capability + */ + + u8 additional_len; + u8 reserved1:7; + u8 sccs:1; + + u8 addr16:1; /* SIP specific bit */ + u8 addr32:1; /* SIP specific bit */ + u8 ack_req_q:1; /* SIP specific bit */ + u8 m_chngr:1; /* device in medium transport element */ + u8 multi_port:1; /* multi-port device */ + u8 reserved3:1; /* TBD - Vendor Specific */ + u8 enc_serv:1; /* enclosure service component */ + u8 reserved2:1; + + u8 soft_seset:1; /* soft reset alternative (VS) */ + u8 cmd_queue:1; /* command queueing supported */ + u8 trans_dis:1; + u8 linked_commands:1; + u8 synchronous:1; + u8 w_bus16:1; + u8 w_bus32:1; + u8 rel_adr:1; /* relative address */ +#endif + struct scsi_inquiry_vendor_s vendor_id; + struct scsi_inquiry_prodid_s product_id; + struct scsi_inquiry_prodrev_s product_rev; + u8 vendor_specific[20]; + u8 reserved4[40]; +}; + +/* + * inquiry.peripheral_qual field values + */ +#define SCSI_DEVQUAL_DEFAULT 0 +#define SCSI_DEVQUAL_NOT_CONNECTED 1 +#define SCSI_DEVQUAL_NOT_SUPPORTED 3 + +/* + * inquiry.device_type field values + */ +#define SCSI_DEVICE_DIRECT_ACCESS 0x00 +#define SCSI_DEVICE_SEQ_ACCESS 0x01 +#define SCSI_DEVICE_ARRAY_CONTROLLER 0x0C +#define SCSI_DEVICE_UNKNOWN 0x1F + +/* + * inquiry.version + */ +#define SCSI_VERSION_ANSI_X3131 2 /* ANSI X3.131 SCSI-2 */ +#define SCSI_VERSION_SPC 3 /* SPC (SCSI-3), ANSI X3.301:1997 */ +#define SCSI_VERSION_SPC_2 4 /* SPC-2 */ + +/* + * response data format + */ +#define SCSI_RSP_DATA_FORMAT 2 /* SCSI-2 & SPC */ + +/* + * SCSI inquiry page codes + */ +#define SCSI_INQ_PAGE_VPD_PAGES 0x00 /* supported vpd pages */ +#define SCSI_INQ_PAGE_USN_PAGE 0x80 /* unit serial number page */ +#define SCSI_INQ_PAGE_DEV_IDENT 0x83 /* device indentification page + */ +#define SCSI_INQ_PAGES_MAX 3 + +/* + * supported vital product data pages + */ +struct scsi_inq_page_vpd_pages_s{ +#ifdef __BIGENDIAN + u8 peripheral_qual:3; + u8 device_type:5; +#else + u8 device_type:5; + u8 peripheral_qual:3; +#endif + u8 page_code; + u8 reserved; + u8 page_length; + u8 pages[SCSI_INQ_PAGES_MAX]; +}; + +/* + * Unit serial number page + */ +#define SCSI_INQ_USN_LEN 32 + +struct scsi_inq_usn_s{ + char usn[SCSI_INQ_USN_LEN]; +}; + +struct scsi_inq_page_usn_s{ +#ifdef __BIGENDIAN + u8 peripheral_qual:3; + u8 device_type:5; +#else + u8 device_type:5; + u8 peripheral_qual:3; +#endif + u8 page_code; + u8 reserved1; + u8 page_length; + struct scsi_inq_usn_s usn; +}; + +enum { + SCSI_INQ_DIP_CODE_BINARY = 1, /* identifier has binary value */ + SCSI_INQ_DIP_CODE_ASCII = 2, /* identifier has ascii value */ +}; + +enum { + SCSI_INQ_DIP_ASSOC_LUN = 0, /* id is associated with device */ + SCSI_INQ_DIP_ASSOC_PORT = 1, /* id is associated with port that + * received the request + */ +}; + +enum { + SCSI_INQ_ID_TYPE_VENDOR = 1, + SCSI_INQ_ID_TYPE_IEEE = 2, + SCSI_INQ_ID_TYPE_FC_FS = 3, + SCSI_INQ_ID_TYPE_OTHER = 4, +}; + +struct scsi_inq_dip_desc_s{ +#ifdef __BIGENDIAN + u8 res0:4; + u8 code_set:4; + u8 res1:2; + u8 association:2; + u8 id_type:4; +#else + u8 code_set:4; + u8 res0:4; + u8 id_type:4; + u8 association:2; + u8 res1:2; +#endif + u8 res2; + u8 id_len; + struct scsi_lun_sn_s id; +}; + +/* + * Device indentification page + */ +struct scsi_inq_page_dev_ident_s{ +#ifdef __BIGENDIAN + u8 peripheral_qual:3; + u8 device_type:5; +#else + u8 device_type:5; + u8 peripheral_qual:3; +#endif + u8 page_code; + u8 reserved1; + u8 page_length; + struct scsi_inq_dip_desc_s desc; +}; + +/* ------------------------------------------------------------ + * READ CAPACITY + * ------------------------------------------------------------ + */ + +struct scsi_read_capacity_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 reserved1:4; + u8 rel_adr:1; +#else + u8 rel_adr:1; + u8 reserved1:4; + u8 lun:3; +#endif + u8 lba0; /* MSB */ + u8 lba1; + u8 lba2; + u8 lba3; /* LSB */ + u8 reserved2; + u8 reserved3; +#ifdef __BIGENDIAN + u8 reserved4:7; + u8 pmi:1; /* partial medium indicator */ +#else + u8 pmi:1; /* partial medium indicator */ + u8 reserved4:7; +#endif + u8 control; +}; + +struct scsi_read_capacity_data_s{ + u32 max_lba; /* maximum LBA available */ + u32 block_length; /* in bytes */ +}; + +struct scsi_read_capacity16_data_s{ + u64 lba; /* maximum LBA available */ + u32 block_length; /* in bytes */ +#ifdef __BIGENDIAN + u8 reserved1:4, + p_type:3, + prot_en:1; + u8 reserved2:4, + lb_pbe:4; /* logical blocks per physical block + * exponent */ + u16 reserved3:2, + lba_align:14; /* lowest aligned logical block + * address */ +#else + u16 lba_align:14, /* lowest aligned logical block + * address */ + reserved3:2; + u8 lb_pbe:4, /* logical blocks per physical block + * exponent */ + reserved2:4; + u8 prot_en:1, + p_type:3, + reserved1:4; +#endif + u64 reserved4; + u64 reserved5; +}; + +/* ------------------------------------------------------------ + * REPORT LUNS command + * ------------------------------------------------------------ + */ + +struct scsi_report_luns_s{ + u8 opcode; /* A0h - REPORT LUNS opCode */ + u8 reserved1[5]; + u8 alloc_length[4];/* allocation length MSB first */ + u8 reserved2; + u8 control; +}; + +#define SCSI_REPORT_LUN_ALLOC_LENGTH(rl) \ + ((rl->alloc_length[0] << 24) | (rl->alloc_length[1] << 16) | \ + (rl->alloc_length[2] << 8) | (rl->alloc_length[3])) + +#define SCSI_REPORT_LUNS_SET_ALLOCLEN(rl, alloc_len) { \ + (rl)->alloc_length[0] = (alloc_len) >> 24; \ + (rl)->alloc_length[1] = ((alloc_len) >> 16) & 0xFF; \ + (rl)->alloc_length[2] = ((alloc_len) >> 8) & 0xFF; \ + (rl)->alloc_length[3] = (alloc_len) & 0xFF; \ +} + +struct scsi_report_luns_data_s{ + u32 lun_list_length; /* length of LUN list length */ + u32 reserved; + lun_t lun[1]; /* first LUN in lun list */ +}; + +/* ------------------------------------------------------------- + * SCSI mode parameters + * ----------------------------------------------------------- + */ +enum { + SCSI_DA_MEDIUM_DEF = 0, /* direct access default medium type */ + SCSI_DA_MEDIUM_SS = 1, /* direct access single sided */ + SCSI_DA_MEDIUM_DS = 2, /* direct access double sided */ +}; + +/* + * SCSI Mode Select(6) cdb + */ +struct scsi_mode_select6_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 pf:1; /* page format */ + u8 reserved2:3; + u8 sp:1; /* save pages if set to 1 */ +#else + u8 sp:1; /* save pages if set to 1 */ + u8 reserved2:3; + u8 pf:1; /* page format */ + u8 reserved1:3; +#endif + u8 reserved3[2]; + u8 alloc_len; + u8 control; +}; + +/* + * SCSI Mode Select(10) cdb + */ +struct scsi_mode_select10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 pf:1; /* page format */ + u8 reserved2:3; + u8 sp:1; /* save pages if set to 1 */ +#else + u8 sp:1; /* save pages if set to 1 */ + u8 reserved2:3; + u8 pf:1; /* page format */ + u8 reserved1:3; +#endif + u8 reserved3[5]; + u8 alloc_len_msb; + u8 alloc_len_lsb; + u8 control; +}; + +/* + * SCSI Mode Sense(6) cdb + */ +struct scsi_mode_sense6_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:4; + u8 dbd:1; /* disable block discriptors if set to 1 */ + u8 reserved2:3; + + u8 pc:2; /* page control */ + u8 page_code:6; +#else + u8 reserved2:3; + u8 dbd:1; /* disable block descriptors if set to 1 */ + u8 reserved1:4; + + u8 page_code:6; + u8 pc:2; /* page control */ +#endif + u8 reserved3; + u8 alloc_len; + u8 control; +}; + +/* + * SCSI Mode Sense(10) cdb + */ +struct scsi_mode_sense10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 LLBAA:1; /* long LBA accepted if set to 1 */ + u8 dbd:1; /* disable block descriptors if set + * to 1 + */ + u8 reserved2:3; + + u8 pc:2; /* page control */ + u8 page_code:6; +#else + u8 reserved2:3; + u8 dbd:1; /* disable block descriptors if set to + * 1 + */ + u8 LLBAA:1; /* long LBA accepted if set to 1 */ + u8 reserved1:3; + + u8 page_code:6; + u8 pc:2; /* page control */ +#endif + u8 reserved3[4]; + u8 alloc_len_msb; + u8 alloc_len_lsb; + u8 control; +}; + +#define SCSI_CDB10_GET_AL(cdb) \ + ((cdb)->alloc_len_msb << 8 | (cdb)->alloc_len_lsb) + +#define SCSI_CDB10_SET_AL(cdb, al) { \ + (cdb)->alloc_len_msb = al >> 8; \ + (cdb)->alloc_len_lsb = al & 0xFF; \ +} + +#define SCSI_CDB6_GET_AL(cdb) ((cdb)->alloc_len) + +#define SCSI_CDB6_SET_AL(cdb, al) { \ + (cdb)->alloc_len = al; \ +} + +/* + * page control field values + */ +#define SCSI_PC_CURRENT_VALUES 0x0 +#define SCSI_PC_CHANGEABLE_VALUES 0x1 +#define SCSI_PC_DEFAULT_VALUES 0x2 +#define SCSI_PC_SAVED_VALUES 0x3 + +/* + * SCSI mode page codes + */ +#define SCSI_MP_VENDOR_SPEC 0x00 +#define SCSI_MP_DISC_RECN 0x02 /* disconnect-reconnect page */ +#define SCSI_MP_FORMAT_DEVICE 0x03 +#define SCSI_MP_RDG 0x04 /* rigid disk geometry page */ +#define SCSI_MP_FDP 0x05 /* flexible disk page */ +#define SCSI_MP_CACHING 0x08 /* caching page */ +#define SCSI_MP_CONTROL 0x0A /* control mode page */ +#define SCSI_MP_MED_TYPES_SUP 0x0B /* medium types supported page */ +#define SCSI_MP_INFO_EXCP_CNTL 0x1C /* informational exception control */ +#define SCSI_MP_ALL 0x3F /* return all pages - mode sense only */ + +/* + * mode parameter header + */ +struct scsi_mode_param_header6_s{ + u8 mode_datalen; + u8 medium_type; + + /* + * device specific parameters expanded for direct access devices + */ +#ifdef __BIGENDIAN + u32 wp:1; /* write protected */ + u32 reserved1:2; + u32 dpofua:1; /* disable page out + force unit access + */ + u32 reserved2:4; +#else + u32 reserved2:4; + u32 dpofua:1; /* disable page out + force unit access + */ + u32 reserved1:2; + u32 wp:1; /* write protected */ +#endif + + u8 block_desclen; +}; + +struct scsi_mode_param_header10_s{ + u32 mode_datalen:16; + u32 medium_type:8; + + /* + * device specific parameters expanded for direct access devices + */ +#ifdef __BIGENDIAN + u32 wp:1; /* write protected */ + u32 reserved1:2; + u32 dpofua:1; /* disable page out + force unit access + */ + u32 reserved2:4; +#else + u32 reserved2:4; + u32 dpofua:1; /* disable page out + force unit access + */ + u32 reserved1:2; + u32 wp:1; /* write protected */ +#endif + +#ifdef __BIGENDIAN + u32 reserved3:7; + u32 longlba:1; +#else + u32 longlba:1; + u32 reserved3:7; +#endif + u32 reserved4:8; + u32 block_desclen:16; +}; + +/* + * mode parameter block descriptor + */ +struct scsi_mode_param_desc_s{ + u32 nblks; + u32 density_code:8; + u32 block_length:24; +}; + +/* + * Disconnect-reconnect mode page format + */ +struct scsi_mp_disc_recn_s{ +#ifdef __BIGENDIAN + u8 ps:1; + u8 reserved1:1; + u8 page_code:6; +#else + u8 page_code:6; + u8 reserved1:1; + u8 ps:1; +#endif + u8 page_len; + u8 buf_full_ratio; + u8 buf_empty_ratio; + + u8 bil_msb; /* bus inactivity limit -MSB */ + u8 bil_lsb; /* bus inactivity limit -LSB */ + + u8 dtl_msb; /* disconnect time limit - MSB */ + u8 dtl_lsb; /* disconnect time limit - LSB */ + + u8 ctl_msb; /* connect time limit - MSB */ + u8 ctl_lsb; /* connect time limit - LSB */ + + u8 max_burst_len_msb; + u8 max_burst_len_lsb; +#ifdef __BIGENDIAN + u8 emdp:1; /* enable modify data pointers */ + u8 fa:3; /* fair arbitration */ + u8 dimm:1; /* disconnect immediate */ + u8 dtdc:3; /* data transfer disconnect control */ +#else + u8 dtdc:3; /* data transfer disconnect control */ + u8 dimm:1; /* disconnect immediate */ + u8 fa:3; /* fair arbitration */ + u8 emdp:1; /* enable modify data pointers */ +#endif + + u8 reserved3; + + u8 first_burst_len_msb; + u8 first_burst_len_lsb; +}; + +/* + * SCSI format device mode page + */ +struct scsi_mp_format_device_s{ +#ifdef __BIGENDIAN + u32 ps:1; + u32 reserved1:1; + u32 page_code:6; +#else + u32 page_code:6; + u32 reserved1:1; + u32 ps:1; +#endif + u32 page_len:8; + u32 tracks_per_zone:16; + + u32 a_sec_per_zone:16; + u32 a_tracks_per_zone:16; + + u32 a_tracks_per_lun:16; /* alternate tracks/lun-MSB */ + u32 sec_per_track:16; /* sectors/track-MSB */ + + u32 bytes_per_sector:16; + u32 interleave:16; + + u32 tsf:16; /* track skew factor-MSB */ + u32 csf:16; /* cylinder skew factor-MSB */ + +#ifdef __BIGENDIAN + u32 ssec:1; /* soft sector formatting */ + u32 hsec:1; /* hard sector formatting */ + u32 rmb:1; /* removable media */ + u32 surf:1; /* surface */ + u32 reserved2:4; +#else + u32 reserved2:4; + u32 surf:1; /* surface */ + u32 rmb:1; /* removable media */ + u32 hsec:1; /* hard sector formatting */ + u32 ssec:1; /* soft sector formatting */ +#endif + u32 reserved3:24; +}; + +/* + * SCSI rigid disk device geometry page + */ +struct scsi_mp_rigid_device_geometry_s{ +#ifdef __BIGENDIAN + u32 ps:1; + u32 reserved1:1; + u32 page_code:6; +#else + u32 page_code:6; + u32 reserved1:1; + u32 ps:1; +#endif + u32 page_len:8; + u32 num_cylinders0:8; + u32 num_cylinders1:8; + + u32 num_cylinders2:8; + u32 num_heads:8; + u32 scwp0:8; + u32 scwp1:8; + + u32 scwp2:8; + u32 scrwc0:8; + u32 scrwc1:8; + u32 scrwc2:8; + + u32 dsr:16; + u32 lscyl0:8; + u32 lscyl1:8; + + u32 lscyl2:8; +#ifdef __BIGENDIAN + u32 reserved2:6; + u32 rpl:2; /* rotational position locking */ +#else + u32 rpl:2; /* rotational position locking */ + u32 reserved2:6; +#endif + u32 rot_off:8; + u32 reserved3:8; + + u32 med_rot_rate:16; + u32 reserved4:16; +}; + +/* + * SCSI caching mode page + */ +struct scsi_mp_caching_s{ +#ifdef __BIGENDIAN + u8 ps:1; + u8 res1:1; + u8 page_code:6; +#else + u8 page_code:6; + u8 res1:1; + u8 ps:1; +#endif + u8 page_len; +#ifdef __BIGENDIAN + u8 ic:1; /* initiator control */ + u8 abpf:1; /* abort pre-fetch */ + u8 cap:1; /* caching analysis permitted */ + u8 disc:1; /* discontinuity */ + u8 size:1; /* size enable */ + u8 wce:1; /* write cache enable */ + u8 mf:1; /* multiplication factor */ + u8 rcd:1; /* read cache disable */ + + u8 drrp:4; /* demand read retention priority */ + u8 wrp:4; /* write retention priority */ +#else + u8 rcd:1; /* read cache disable */ + u8 mf:1; /* multiplication factor */ + u8 wce:1; /* write cache enable */ + u8 size:1; /* size enable */ + u8 disc:1; /* discontinuity */ + u8 cap:1; /* caching analysis permitted */ + u8 abpf:1; /* abort pre-fetch */ + u8 ic:1; /* initiator control */ + + u8 wrp:4; /* write retention priority */ + u8 drrp:4; /* demand read retention priority */ +#endif + u8 dptl[2];/* disable pre-fetch transfer length */ + u8 min_prefetch[2]; + u8 max_prefetch[2]; + u8 max_prefetch_limit[2]; +#ifdef __BIGENDIAN + u8 fsw:1; /* force sequential write */ + u8 lbcss:1;/* logical block cache segment size */ + u8 dra:1; /* disable read ahead */ + u8 vs:2; /* vendor specific */ + u8 res2:3; +#else + u8 res2:3; + u8 vs:2; /* vendor specific */ + u8 dra:1; /* disable read ahead */ + u8 lbcss:1;/* logical block cache segment size */ + u8 fsw:1; /* force sequential write */ +#endif + u8 num_cache_segs; + + u8 cache_seg_size[2]; + u8 res3; + u8 non_cache_seg_size[3]; +}; + +/* + * SCSI control mode page + */ +struct scsi_mp_control_page_s{ +#ifdef __BIGENDIAN +u8 ps:1; +u8 reserved1:1; +u8 page_code:6; +#else +u8 page_code:6; +u8 reserved1:1; +u8 ps:1; +#endif + u8 page_len; +#ifdef __BIGENDIAN + u8 tst:3; /* task set type */ + u8 reserved3:3; + u8 gltsd:1; /* global logging target save disable */ + u8 rlec:1; /* report log exception condition */ + + u8 qalgo_mod:4; /* queue alogorithm modifier */ + u8 reserved4:1; + u8 qerr:2; /* queue error management */ + u8 dque:1; /* disable queuing */ + + u8 reserved5:1; + u8 rac:1; /* report a check */ + u8 reserved6:2; + u8 swp:1; /* software write protect */ + u8 raerp:1; /* ready AER permission */ + u8 uaaerp:1; /* unit attenstion AER permission */ + u8 eaerp:1; /* error AER permission */ + + u8 reserved7:5; + u8 autoload_mod:3; +#else + u8 rlec:1; /* report log exception condition */ + u8 gltsd:1; /* global logging target save disable */ + u8 reserved3:3; + u8 tst:3; /* task set type */ + + u8 dque:1; /* disable queuing */ + u8 qerr:2; /* queue error management */ + u8 reserved4:1; + u8 qalgo_mod:4; /* queue alogorithm modifier */ + + u8 eaerp:1; /* error AER permission */ + u8 uaaerp:1; /* unit attenstion AER permission */ + u8 raerp:1; /* ready AER permission */ + u8 swp:1; /* software write protect */ + u8 reserved6:2; + u8 rac:1; /* report a check */ + u8 reserved5:1; + + u8 autoload_mod:3; + u8 reserved7:5; +#endif + u8 rahp_msb; /* ready AER holdoff period - MSB */ + u8 rahp_lsb; /* ready AER holdoff period - LSB */ + + u8 busy_timeout_period_msb; + u8 busy_timeout_period_lsb; + + u8 ext_selftest_compl_time_msb; + u8 ext_selftest_compl_time_lsb; +}; + +/* + * SCSI medium types supported mode page + */ +struct scsi_mp_medium_types_sup_s{ +#ifdef __BIGENDIAN + u8 ps:1; + u8 reserved1:1; + u8 page_code:6; +#else + u8 page_code:6; + u8 reserved1:1; + u8 ps:1; +#endif + u8 page_len; + + u8 reserved3[2]; + u8 med_type1_sup; /* medium type one supported */ + u8 med_type2_sup; /* medium type two supported */ + u8 med_type3_sup; /* medium type three supported */ + u8 med_type4_sup; /* medium type four supported */ +}; + +/* + * SCSI informational exception control mode page + */ +struct scsi_mp_info_excpt_cntl_s{ +#ifdef __BIGENDIAN + u8 ps:1; + u8 reserved1:1; + u8 page_code:6; +#else + u8 page_code:6; + u8 reserved1:1; + u8 ps:1; +#endif + u8 page_len; +#ifdef __BIGENDIAN + u8 perf:1; /* performance */ + u8 reserved3:1; + u8 ebf:1; /* enable background fucntion */ + u8 ewasc:1; /* enable warning */ + u8 dexcpt:1; /* disable exception control */ + u8 test:1; /* enable test device failure + * notification + */ + u8 reserved4:1; + u8 log_error:1; + + u8 reserved5:4; + u8 mrie:4; /* method of reporting info + * exceptions + */ +#else + u8 log_error:1; + u8 reserved4:1; + u8 test:1; /* enable test device failure + * notification + */ + u8 dexcpt:1; /* disable exception control */ + u8 ewasc:1; /* enable warning */ + u8 ebf:1; /* enable background fucntion */ + u8 reserved3:1; + u8 perf:1; /* performance */ + + u8 mrie:4; /* method of reporting info + * exceptions + */ + u8 reserved5:4; +#endif + u8 interval_timer_msb; + u8 interval_timer_lsb; + + u8 report_count_msb; + u8 report_count_lsb; +}; + +/* + * Methods of reporting informational exceptions + */ +#define SCSI_MP_IEC_NO_REPORT 0x0 /* no reporting of exceptions */ +#define SCSI_MP_IEC_AER 0x1 /* async event reporting */ +#define SCSI_MP_IEC_UNIT_ATTN 0x2 /* generate unit attenstion */ +#define SCSI_MO_IEC_COND_REC_ERR 0x3 /* conditionally generate recovered + * error + */ +#define SCSI_MP_IEC_UNCOND_REC_ERR 0x4 /* unconditionally generate recovered + * error + */ +#define SCSI_MP_IEC_NO_SENSE 0x5 /* generate no sense */ +#define SCSI_MP_IEC_ON_REQUEST 0x6 /* only report exceptions on request */ + +/* + * SCSI flexible disk page + */ +struct scsi_mp_flexible_disk_s{ +#ifdef __BIGENDIAN + u8 ps:1; + u8 reserved1:1; + u8 page_code:6; +#else + u8 page_code:6; + u8 reserved1:1; + u8 ps:1; +#endif + u8 page_len; + + u8 transfer_rate_msb; + u8 transfer_rate_lsb; + + u8 num_heads; + u8 num_sectors; + + u8 bytes_per_sector_msb; + u8 bytes_per_sector_lsb; + + u8 num_cylinders_msb; + u8 num_cylinders_lsb; + + u8 sc_wpc_msb; /* starting cylinder-write + * precompensation msb + */ + u8 sc_wpc_lsb; /* starting cylinder-write + * precompensation lsb + */ + u8 sc_rwc_msb; /* starting cylinder-reduced write + * current msb + */ + u8 sc_rwc_lsb; /* starting cylinder-reduced write + * current lsb + */ + + u8 dev_step_rate_msb; + u8 dev_step_rate_lsb; + + u8 dev_step_pulse_width; + + u8 head_sd_msb; /* head settle delay msb */ + u8 head_sd_lsb; /* head settle delay lsb */ + + u8 motor_on_delay; + u8 motor_off_delay; +#ifdef __BIGENDIAN + u8 trdy:1; /* true ready bit */ + u8 ssn:1; /* start sector number bit */ + u8 mo:1; /* motor on bit */ + u8 reserved3:5; + + u8 reserved4:4; + u8 spc:4; /* step pulse per cylinder */ +#else + u8 reserved3:5; + u8 mo:1; /* motor on bit */ + u8 ssn:1; /* start sector number bit */ + u8 trdy:1; /* true ready bit */ + + u8 spc:4; /* step pulse per cylinder */ + u8 reserved4:4; +#endif + u8 write_comp; + u8 head_load_delay; + u8 head_unload_delay; +#ifdef __BIGENDIAN + u8 pin34:4; /* pin34 usage */ + u8 pin2:4; /* pin2 usage */ + + u8 pin4:4; /* pin4 usage */ + u8 pin1:4; /* pin1 usage */ +#else + u8 pin2:4; /* pin2 usage */ + u8 pin34:4; /* pin34 usage */ + + u8 pin1:4; /* pin1 usage */ + u8 pin4:4; /* pin4 usage */ +#endif + u8 med_rot_rate_msb; + u8 med_rot_rate_lsb; + + u8 reserved5[2]; +}; + +struct scsi_mode_page_format_data6_s{ + struct scsi_mode_param_header6_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_format_device_s format; /* format device data */ +}; + +struct scsi_mode_page_format_data10_s{ + struct scsi_mode_param_header10_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_format_device_s format; /* format device data */ +}; + +struct scsi_mode_page_rdg_data6_s{ + struct scsi_mode_param_header6_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_rigid_device_geometry_s rdg; + /* rigid geometry data */ +}; + +struct scsi_mode_page_rdg_data10_s{ + struct scsi_mode_param_header10_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_rigid_device_geometry_s rdg; + /* rigid geometry data */ +}; + +struct scsi_mode_page_cache6_s{ + struct scsi_mode_param_header6_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_caching_s cache; /* cache page data */ +}; + +struct scsi_mode_page_cache10_s{ + struct scsi_mode_param_header10_s mph; /* mode page header */ + struct scsi_mode_param_desc_s desc; /* block descriptor */ + struct scsi_mp_caching_s cache; /* cache page data */ +}; + +/* -------------------------------------------------------------- + * Format Unit command + * ------------------------------------------------------------ + */ + +/* + * Format Unit CDB + */ +struct scsi_format_unit_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 res1:3; + u8 fmtdata:1; /* if set, data out phase has format + * data + */ + u8 cmplst:1; /* if set, defect list is complete */ + u8 def_list:3; /* format of defect descriptor is + * fmtdata =1 + */ +#else + u8 def_list:3; /* format of defect descriptor is + * fmtdata = 1 + */ + u8 cmplst:1; /* if set, defect list is complete */ + u8 fmtdata:1; /* if set, data out phase has format + * data + */ + u8 res1:3; +#endif + u8 interleave_msb; + u8 interleave_lsb; + u8 vendor_spec; + u8 control; +}; + +/* + * h + */ +struct scsi_reserve6_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved:3; + u8 obsolete:4; + u8 extent:1; +#else + u8 extent:1; + u8 obsolete:4; + u8 reserved:3; +#endif + u8 reservation_id; + u16 param_list_len; + u8 control; +}; + +/* + * h + */ +struct scsi_release6_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 obsolete:4; + u8 extent:1; +#else + u8 extent:1; + u8 obsolete:4; + u8 reserved1:3; +#endif + u8 reservation_id; + u16 reserved2; + u8 control; +}; + +/* + * h + */ +struct scsi_reserve10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 third_party:1; + u8 reserved2:2; + u8 long_id:1; + u8 extent:1; +#else + u8 extent:1; + u8 long_id:1; + u8 reserved2:2; + u8 third_party:1; + u8 reserved1:3; +#endif + u8 reservation_id; + u8 third_pty_dev_id; + u8 reserved3; + u8 reserved4; + u8 reserved5; + u16 param_list_len; + u8 control; +}; + +struct scsi_release10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 third_party:1; + u8 reserved2:2; + u8 long_id:1; + u8 extent:1; +#else + u8 extent:1; + u8 long_id:1; + u8 reserved2:2; + u8 third_party:1; + u8 reserved1:3; +#endif + u8 reservation_id; + u8 third_pty_dev_id; + u8 reserved3; + u8 reserved4; + u8 reserved5; + u16 param_list_len; + u8 control; +}; + +struct scsi_verify10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 dpo:1; + u8 reserved:2; + u8 bytchk:1; + u8 reladdr:1; +#else + u8 reladdr:1; + u8 bytchk:1; + u8 reserved:2; + u8 dpo:1; + u8 lun:3; +#endif + u8 lba0; + u8 lba1; + u8 lba2; + u8 lba3; + u8 reserved1; + u8 verification_len0; + u8 verification_len1; + u8 control_byte; +}; + +struct scsi_request_sense_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 lun:3; + u8 reserved:5; +#else + u8 reserved:5; + u8 lun:3; +#endif + u8 reserved0; + u8 reserved1; + u8 alloc_len; + u8 control_byte; +}; + +/* ------------------------------------------------------------ + * SCSI status byte values + * ------------------------------------------------------------ + */ +#define SCSI_STATUS_GOOD 0x00 +#define SCSI_STATUS_CHECK_CONDITION 0x02 +#define SCSI_STATUS_CONDITION_MET 0x04 +#define SCSI_STATUS_BUSY 0x08 +#define SCSI_STATUS_INTERMEDIATE 0x10 +#define SCSI_STATUS_ICM 0x14 /* intermediate condition met */ +#define SCSI_STATUS_RESERVATION_CONFLICT 0x18 +#define SCSI_STATUS_COMMAND_TERMINATED 0x22 +#define SCSI_STATUS_QUEUE_FULL 0x28 +#define SCSI_STATUS_ACA_ACTIVE 0x30 + +#define SCSI_MAX_ALLOC_LEN 0xFF /* maximum allocarion length + * in CDBs + */ + +#define SCSI_OP_WRITE_VERIFY10 0x2E +#define SCSI_OP_WRITE_VERIFY12 0xAE +#define SCSI_OP_UNDEF 0xFF + +/* + * SCSI WRITE-VERIFY(10) command + */ +struct scsi_write_verify10_s{ + u8 opcode; +#ifdef __BIGENDIAN + u8 reserved1:3; + u8 dpo:1; /* Disable Page Out */ + u8 reserved2:1; + u8 ebp:1; /* erse by-pass */ + u8 bytchk:1; /* byte check */ + u8 rel_adr:1; /* relative address */ +#else + u8 rel_adr:1; /* relative address */ + u8 bytchk:1; /* byte check */ + u8 ebp:1; /* erse by-pass */ + u8 reserved2:1; + u8 dpo:1; /* Disable Page Out */ + u8 reserved1:3; +#endif + u8 lba0; /* logical block address - MSB */ + u8 lba1; + u8 lba2; + u8 lba3; /* LSB */ + u8 reserved3; + u8 xfer_length0; /* transfer length in blocks - MSB */ + u8 xfer_length1; /* LSB */ + u8 control; +}; + +#pragma pack() + +#endif /* __SCSI_H__ */ diff --git a/drivers/scsi/bfa/include/protocol/types.h b/drivers/scsi/bfa/include/protocol/types.h new file mode 100644 index 000000000000..2875a6cced3b --- /dev/null +++ b/drivers/scsi/bfa/include/protocol/types.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +/** + * types.h Protocol defined base types + */ + +#ifndef __TYPES_H__ +#define __TYPES_H__ + +#include + +#define wwn_t u64 +#define lun_t u64 + +#define WWN_NULL (0) +#define FC_SYMNAME_MAX 256 /* max name server symbolic name size */ +#define FC_ALPA_MAX 128 + +#pragma pack(1) + +#define MAC_ADDRLEN (6) +struct mac_s { u8 mac[MAC_ADDRLEN]; }; +#define mac_t struct mac_s + +#pragma pack() + +#endif diff --git a/drivers/scsi/bfa/loop.c b/drivers/scsi/bfa/loop.c new file mode 100644 index 000000000000..a418dedebe9e --- /dev/null +++ b/drivers/scsi/bfa/loop.c @@ -0,0 +1,422 @@ +/* + * 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. + */ + +/** + * port_loop.c vport private loop implementation. + */ +#include +#include +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_trcmod.h" +#include "lport_priv.h" + +BFA_TRC_FILE(FCS, LOOP); + +/** + * ALPA to LIXA bitmap mapping + * + * ALPA 0x00 (Word 0, Bit 30) is invalid for N_Ports. Also Word 0 Bit 31 + * is for L_bit (login required) and is filled as ALPA 0x00 here. + */ +static const u8 port_loop_alpa_map[] = { + 0xEF, 0xE8, 0xE4, 0xE2, 0xE1, 0xE0, 0xDC, 0xDA, /* Word 3 Bits 0..7 */ + 0xD9, 0xD6, 0xD5, 0xD4, 0xD3, 0xD2, 0xD1, 0xCE, /* Word 3 Bits 8..15 */ + 0xCD, 0xCC, 0xCB, 0xCA, 0xC9, 0xC7, 0xC6, 0xC5, /* Word 3 Bits 16..23 */ + 0xC3, 0xBC, 0xBA, 0xB9, 0xB6, 0xB5, 0xB4, 0xB3, /* Word 3 Bits 24..31 */ + + 0xB2, 0xB1, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9, /* Word 2 Bits 0..7 */ + 0xA7, 0xA6, 0xA5, 0xA3, 0x9F, 0x9E, 0x9D, 0x9B, /* Word 2 Bits 8..15 */ + 0x98, 0x97, 0x90, 0x8F, 0x88, 0x84, 0x82, 0x81, /* Word 2 Bits 16..23 */ + 0x80, 0x7C, 0x7A, 0x79, 0x76, 0x75, 0x74, 0x73, /* Word 2 Bits 24..31 */ + + 0x72, 0x71, 0x6E, 0x6D, 0x6C, 0x6B, 0x6A, 0x69, /* Word 1 Bits 0..7 */ + 0x67, 0x66, 0x65, 0x63, 0x5C, 0x5A, 0x59, 0x56, /* Word 1 Bits 8..15 */ + 0x55, 0x54, 0x53, 0x52, 0x51, 0x4E, 0x4D, 0x4C, /* Word 1 Bits 16..23 */ + 0x4B, 0x4A, 0x49, 0x47, 0x46, 0x45, 0x43, 0x3C, /* Word 1 Bits 24..31 */ + + 0x3A, 0x39, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, /* Word 0 Bits 0..7 */ + 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x27, 0x26, /* Word 0 Bits 8..15 */ + 0x25, 0x23, 0x1F, 0x1E, 0x1D, 0x1B, 0x18, 0x17, /* Word 0 Bits 16..23 */ + 0x10, 0x0F, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00, /* Word 0 Bits 24..31 */ +}; + +/* + * Local Functions + */ +bfa_status_t bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, + u8 alpa); + +void bfa_fcs_port_loop_plogi_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); + +bfa_status_t bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port, + u8 alpa); + +void bfa_fcs_port_loop_adisc_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); + +bfa_status_t bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port, + u8 alpa); + +void bfa_fcs_port_loop_plogi_acc_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); + +bfa_status_t bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port, + u8 alpa); + +void bfa_fcs_port_loop_adisc_acc_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +/** + * Called by port to initializar in provate LOOP topology. + */ +void +bfa_fcs_port_loop_init(struct bfa_fcs_port_s *port) +{ +} + +/** + * Called by port to notify transition to online state. + */ +void +bfa_fcs_port_loop_online(struct bfa_fcs_port_s *port) +{ + + u8 num_alpa = port->port_topo.ploop.num_alpa; + u8 *alpa_pos_map = port->port_topo.ploop.alpa_pos_map; + struct bfa_fcs_rport_s *r_port; + int ii = 0; + + /* + * If the port role is Initiator Mode, create Rports. + */ + if (port->port_cfg.roles == BFA_PORT_ROLE_FCP_IM) { + /* + * Check if the ALPA positional bitmap is available. + * if not, we send PLOGI to all possible ALPAs. + */ + if (num_alpa > 0) { + for (ii = 0; ii < num_alpa; ii++) { + /* + * ignore ALPA of bfa port + */ + if (alpa_pos_map[ii] != port->pid) { + r_port = bfa_fcs_rport_create(port, + alpa_pos_map[ii]); + } + } + } else { + for (ii = 0; ii < MAX_ALPA_COUNT; ii++) { + /* + * ignore ALPA of bfa port + */ + if ((port_loop_alpa_map[ii] > 0) + && (port_loop_alpa_map[ii] != port->pid)) + bfa_fcs_port_loop_send_plogi(port, + port_loop_alpa_map[ii]); + /**TBD */ + } + } + } else { + /* + * TBD Target Mode ?? + */ + } + +} + +/** + * Called by port to notify transition to offline state. + */ +void +bfa_fcs_port_loop_offline(struct bfa_fcs_port_s *port) +{ + +} + +/** + * Called by port to notify a LIP on the loop. + */ +void +bfa_fcs_port_loop_lip(struct bfa_fcs_port_s *port) +{ +} + +/** + * Local Functions. + */ +bfa_status_t +bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, u8 alpa) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp = NULL; + int len; + + bfa_trc(port->fcs, alpa); + + fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL, + NULL); + bfa_assert(fcxp); + + len = fc_plogi_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, + bfa_fcs_port_loop_plogi_response, (void *)port, + FC_MAX_PDUSZ, FC_RA_TOV); + + return BFA_STATUS_OK; +} + +/** + * Called by fcxp to notify the Plogi response + */ +void +bfa_fcs_port_loop_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg; + struct fc_logi_s *plogi_resp; + struct fc_els_cmd_s *els_cmd; + + bfa_trc(port->fcs, req_status); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + /* + * @todo + * This could mean that the device with this APLA does not + * exist on the loop. + */ + + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + plogi_resp = (struct fc_logi_s *) els_cmd; + + if (els_cmd->els_code == FC_ELS_ACC) { + bfa_fcs_rport_start(port, rsp_fchs, plogi_resp); + } else { + bfa_trc(port->fcs, plogi_resp->els_cmd.els_code); + bfa_assert(0); + } +} + +bfa_status_t +bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port, u8 alpa) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(port->fcs, alpa); + + fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL, + NULL); + bfa_assert(fcxp); + + len = fc_plogi_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, + bfa_fcs_port_loop_plogi_acc_response, + (void *)port, FC_MAX_PDUSZ, 0); /* No response + * expected + */ + + return BFA_STATUS_OK; +} + +/* + * Plogi Acc Response + * We donot do any processing here. + */ +void +bfa_fcs_port_loop_plogi_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + + struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg; + + bfa_trc(port->fcs, port->pid); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + return; + } +} + +bfa_status_t +bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port, u8 alpa) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(port->fcs, alpa); + + fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL, + NULL); + bfa_assert(fcxp); + + len = fc_adisc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, + bfa_fcs_port_loop_adisc_response, (void *)port, + FC_MAX_PDUSZ, FC_RA_TOV); + + return BFA_STATUS_OK; +} + +/** + * Called by fcxp to notify the ADISC response + */ +void +bfa_fcs_port_loop_adisc_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg; + struct bfa_fcs_rport_s *rport; + struct fc_adisc_s *adisc_resp; + struct fc_els_cmd_s *els_cmd; + u32 pid = rsp_fchs->s_id; + + bfa_trc(port->fcs, req_status); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + /* + * TBD : we may need to retry certain requests + */ + bfa_fcxp_free(fcxp); + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + adisc_resp = (struct fc_adisc_s *) els_cmd; + + if (els_cmd->els_code == FC_ELS_ACC) { + } else { + bfa_trc(port->fcs, adisc_resp->els_cmd.els_code); + + /* + * TBD: we may need to check for reject codes and retry + */ + rport = bfa_fcs_port_get_rport_by_pid(port, pid); + if (rport) { + list_del(&rport->qe); + bfa_fcs_rport_delete(rport); + } + + } + return; +} + +bfa_status_t +bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port, u8 alpa) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(port->fcs, alpa); + + fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL, + NULL); + bfa_assert(fcxp); + + len = fc_adisc_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, + bfa_fcs_port_loop_adisc_acc_response, + (void *)port, FC_MAX_PDUSZ, 0); /* no reponse + * expected + */ + + return BFA_STATUS_OK; +} + +/* + * Adisc Acc Response + * We donot do any processing here. + */ +void +bfa_fcs_port_loop_adisc_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + + struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg; + + bfa_trc(port->fcs, port->pid); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + return; + } +} diff --git a/drivers/scsi/bfa/lport_api.c b/drivers/scsi/bfa/lport_api.c new file mode 100644 index 000000000000..8f51a83f1834 --- /dev/null +++ b/drivers/scsi/bfa/lport_api.c @@ -0,0 +1,291 @@ +/* + * 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. + */ + +/** + * port_api.c BFA FCS port + */ + +#include +#include +#include +#include "fcs_rport.h" +#include "fcs_fabric.h" +#include "fcs_trcmod.h" +#include "fcs_vport.h" + +BFA_TRC_FILE(FCS, PORT_API); + + + +/** + * fcs_port_api BFA FCS port API + */ + +void +bfa_fcs_cfg_base_port(struct bfa_fcs_s *fcs, struct bfa_port_cfg_s *port_cfg) +{ +} + +struct bfa_fcs_port_s * +bfa_fcs_get_base_port(struct bfa_fcs_s *fcs) +{ + return (&fcs->fabric.bport); +} + +wwn_t +bfa_fcs_port_get_rport(struct bfa_fcs_port_s *port, wwn_t wwn, int index, + int nrports, bfa_boolean_t bwwn) +{ + struct list_head *qh, *qe; + struct bfa_fcs_rport_s *rport = NULL; + int i; + struct bfa_fcs_s *fcs; + + if (port == NULL || nrports == 0) + return (wwn_t) 0; + + fcs = port->fcs; + bfa_trc(fcs, (u32) nrports); + + i = 0; + qh = &port->rport_q; + qe = bfa_q_first(qh); + + while ((qe != qh) && (i < nrports)) { + rport = (struct bfa_fcs_rport_s *)qe; + if (bfa_os_ntoh3b(rport->pid) > 0xFFF000) { + qe = bfa_q_next(qe); + bfa_trc(fcs, (u32) rport->pwwn); + bfa_trc(fcs, rport->pid); + bfa_trc(fcs, i); + continue; + } + + if (bwwn) { + if (!memcmp(&wwn, &rport->pwwn, 8)) + break; + } else { + if (i == index) + break; + } + + i++; + qe = bfa_q_next(qe); + } + + bfa_trc(fcs, i); + if (rport) { + return rport->pwwn; + } else { + return (wwn_t) 0; + } +} + +void +bfa_fcs_port_get_rports(struct bfa_fcs_port_s *port, wwn_t rport_wwns[], + int *nrports) +{ + struct list_head *qh, *qe; + struct bfa_fcs_rport_s *rport = NULL; + int i; + struct bfa_fcs_s *fcs; + + if (port == NULL || rport_wwns == NULL || *nrports == 0) + return; + + fcs = port->fcs; + bfa_trc(fcs, (u32) *nrports); + + i = 0; + qh = &port->rport_q; + qe = bfa_q_first(qh); + + while ((qe != qh) && (i < *nrports)) { + rport = (struct bfa_fcs_rport_s *)qe; + if (bfa_os_ntoh3b(rport->pid) > 0xFFF000) { + qe = bfa_q_next(qe); + bfa_trc(fcs, (u32) rport->pwwn); + bfa_trc(fcs, rport->pid); + bfa_trc(fcs, i); + continue; + } + + rport_wwns[i] = rport->pwwn; + + i++; + qe = bfa_q_next(qe); + } + + bfa_trc(fcs, i); + *nrports = i; + return; +} + +/* + * Iterate's through all the rport's in the given port to + * determine the maximum operating speed. + */ +enum bfa_pport_speed +bfa_fcs_port_get_rport_max_speed(struct bfa_fcs_port_s *port) +{ + struct list_head *qh, *qe; + struct bfa_fcs_rport_s *rport = NULL; + struct bfa_fcs_s *fcs; + enum bfa_pport_speed max_speed = 0; + struct bfa_pport_attr_s pport_attr; + enum bfa_pport_speed pport_speed; + + if (port == NULL) + return 0; + + fcs = port->fcs; + + /* + * Get Physical port's current speed + */ + bfa_pport_get_attr(port->fcs->bfa, &pport_attr); + pport_speed = pport_attr.speed; + bfa_trc(fcs, pport_speed); + + qh = &port->rport_q; + qe = bfa_q_first(qh); + + while (qe != qh) { + rport = (struct bfa_fcs_rport_s *)qe; + if ((bfa_os_ntoh3b(rport->pid) > 0xFFF000) + || (bfa_fcs_rport_get_state(rport) == BFA_RPORT_OFFLINE)) { + qe = bfa_q_next(qe); + continue; + } + + if ((rport->rpf.rpsc_speed == BFA_PPORT_SPEED_8GBPS) + || (rport->rpf.rpsc_speed > pport_speed)) { + max_speed = rport->rpf.rpsc_speed; + break; + } else if (rport->rpf.rpsc_speed > max_speed) { + max_speed = rport->rpf.rpsc_speed; + } + + qe = bfa_q_next(qe); + } + + bfa_trc(fcs, max_speed); + return max_speed; +} + +struct bfa_fcs_port_s * +bfa_fcs_lookup_port(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t lpwwn) +{ + struct bfa_fcs_vport_s *vport; + bfa_fcs_vf_t *vf; + + bfa_assert(fcs != NULL); + + vf = bfa_fcs_vf_lookup(fcs, vf_id); + if (vf == NULL) { + bfa_trc(fcs, vf_id); + return (NULL); + } + + if (!lpwwn || (vf->bport.port_cfg.pwwn == lpwwn)) + return (&vf->bport); + + vport = bfa_fcs_fabric_vport_lookup(vf, lpwwn); + if (vport) + return (&vport->lport); + + return (NULL); +} + +/* + * API corresponding to VmWare's NPIV_VPORT_GETINFO. + */ +void +bfa_fcs_port_get_info(struct bfa_fcs_port_s *port, + struct bfa_port_info_s *port_info) +{ + + bfa_trc(port->fcs, port->fabric->fabric_name); + + if (port->vport == NULL) { + /* + * This is a Physical port + */ + port_info->port_type = BFA_PORT_TYPE_PHYSICAL; + + /* + * @todo : need to fix the state & reason + */ + port_info->port_state = 0; + port_info->offline_reason = 0; + + port_info->port_wwn = bfa_fcs_port_get_pwwn(port); + port_info->node_wwn = bfa_fcs_port_get_nwwn(port); + + port_info->max_vports_supp = bfa_fcs_vport_get_max(port->fcs); + port_info->num_vports_inuse = + bfa_fcs_fabric_vport_count(port->fabric); + port_info->max_rports_supp = BFA_FCS_MAX_RPORTS_SUPP; + port_info->num_rports_inuse = port->num_rports; + } else { + /* + * This is a virtual port + */ + port_info->port_type = BFA_PORT_TYPE_VIRTUAL; + + /* + * @todo : need to fix the state & reason + */ + port_info->port_state = 0; + port_info->offline_reason = 0; + + port_info->port_wwn = bfa_fcs_port_get_pwwn(port); + port_info->node_wwn = bfa_fcs_port_get_nwwn(port); + } +} + +void +bfa_fcs_port_get_stats(struct bfa_fcs_port_s *fcs_port, + struct bfa_port_stats_s *port_stats) +{ + bfa_os_memcpy(port_stats, &fcs_port->stats, + sizeof(struct bfa_port_stats_s)); + return; +} + +void +bfa_fcs_port_clear_stats(struct bfa_fcs_port_s *fcs_port) +{ + bfa_os_memset(&fcs_port->stats, 0, sizeof(struct bfa_port_stats_s)); + return; +} + +void +bfa_fcs_port_enable_ipfc_roles(struct bfa_fcs_port_s *fcs_port) +{ + fcs_port->port_cfg.roles |= BFA_PORT_ROLE_FCP_IPFC; + return; +} + +void +bfa_fcs_port_disable_ipfc_roles(struct bfa_fcs_port_s *fcs_port) +{ + fcs_port->port_cfg.roles &= ~BFA_PORT_ROLE_FCP_IPFC; + return; +} + + diff --git a/drivers/scsi/bfa/lport_priv.h b/drivers/scsi/bfa/lport_priv.h new file mode 100644 index 000000000000..dbae370a599a --- /dev/null +++ b/drivers/scsi/bfa/lport_priv.h @@ -0,0 +1,82 @@ +/* + * 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. + */ + +#ifndef __VP_PRIV_H__ +#define __VP_PRIV_H__ + +#include +#include + +/* + * Functions exported by vps + */ +void bfa_fcs_vport_init(struct bfa_fcs_vport_s *vport); + +/* + * Functions exported by vps + */ +void bfa_fcs_vps_online(struct bfa_fcs_port_s *port); +void bfa_fcs_vps_offline(struct bfa_fcs_port_s *port); +void bfa_fcs_vps_lip(struct bfa_fcs_port_s *port); + +/* + * Functions exported by port_fab + */ +void bfa_fcs_port_fab_init(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_fab_online(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_fab_offline(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_fab_rx_frame(struct bfa_fcs_port_s *port, + u8 *rx_frame, u32 len); + +/* + * Functions exported by VP-NS. + */ +void bfa_fcs_port_ns_init(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_ns_offline(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_ns_online(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_ns_query(struct bfa_fcs_port_s *port); + +/* + * Functions exported by VP-SCN + */ +void bfa_fcs_port_scn_init(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_scn_offline(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_scn_online(struct bfa_fcs_port_s *vport); +void bfa_fcs_port_scn_process_rscn(struct bfa_fcs_port_s *port, + struct fchs_s *rx_frame, u32 len); + +/* + * Functions exported by VP-N2N + */ + +void bfa_fcs_port_n2n_init(struct bfa_fcs_port_s *port); +void bfa_fcs_port_n2n_online(struct bfa_fcs_port_s *port); +void bfa_fcs_port_n2n_offline(struct bfa_fcs_port_s *port); +void bfa_fcs_port_n2n_rx_frame(struct bfa_fcs_port_s *port, + u8 *rx_frame, u32 len); + +/* + * Functions exported by VP-LOOP + */ +void bfa_fcs_port_loop_init(struct bfa_fcs_port_s *port); +void bfa_fcs_port_loop_online(struct bfa_fcs_port_s *port); +void bfa_fcs_port_loop_offline(struct bfa_fcs_port_s *port); +void bfa_fcs_port_loop_lip(struct bfa_fcs_port_s *port); +void bfa_fcs_port_loop_rx_frame(struct bfa_fcs_port_s *port, + u8 *rx_frame, u32 len); + +#endif /* __VP_PRIV_H__ */ diff --git a/drivers/scsi/bfa/ms.c b/drivers/scsi/bfa/ms.c new file mode 100644 index 000000000000..c96b3ca007ae --- /dev/null +++ b/drivers/scsi/bfa/ms.c @@ -0,0 +1,759 @@ +/* + * 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. + */ + + +#include +#include +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "lport_priv.h" + +BFA_TRC_FILE(FCS, MS); + +#define BFA_FCS_MS_CMD_MAX_RETRIES 2 +/* + * forward declarations + */ +static void bfa_fcs_port_ms_send_plogi(void *ms_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ms_timeout(void *arg); +static void bfa_fcs_port_ms_plogi_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); + +static void bfa_fcs_port_ms_send_gmal(void *ms_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ms_gmal_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ms_send_gfn(void *ms_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ms_gfn_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +/** + * fcs_ms_sm FCS MS state machine + */ + +/** + * MS State Machine events + */ +enum port_ms_event { + MSSM_EVENT_PORT_ONLINE = 1, + MSSM_EVENT_PORT_OFFLINE = 2, + MSSM_EVENT_RSP_OK = 3, + MSSM_EVENT_RSP_ERROR = 4, + MSSM_EVENT_TIMEOUT = 5, + MSSM_EVENT_FCXP_SENT = 6, + MSSM_EVENT_PORT_FABRIC_RSCN = 7 +}; + +static void bfa_fcs_port_ms_sm_offline(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_plogi_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_plogi(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_plogi_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gmal_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gmal(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gmal_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gfn_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gfn(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_gfn_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +static void bfa_fcs_port_ms_sm_online(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event); +/** + * Start in offline state - awaiting NS to send start. + */ +static void +bfa_fcs_port_ms_sm_offline(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_PORT_ONLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_plogi_sending); + bfa_fcs_port_ms_send_plogi(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_plogi_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_FCXP_SENT: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_plogi); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ms->port), + &ms->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_plogi(struct bfa_fcs_port_ms_s *ms, enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_plogi_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ms->port), &ms->timer, + bfa_fcs_port_ms_timeout, ms, + BFA_FCS_RETRY_TIMEOUT); + break; + + case MSSM_EVENT_RSP_OK: + /* + * since plogi is done, now invoke MS related sub-modules + */ + bfa_fcs_port_fdmi_online(ms); + + /** + * if this is a Vport, go to online state. + */ + if (ms->port->vport) { + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_online); + break; + } + + /* + * For a base port we need to get the + * switch's IP address. + */ + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gmal_sending); + bfa_fcs_port_ms_send_gmal(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_discard(ms->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_plogi_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_plogi_sending); + bfa_fcs_port_ms_send_plogi(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_timer_stop(&ms->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_online(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + /* + * now invoke MS related sub-modules + */ + bfa_fcs_port_fdmi_offline(ms); + break; + + case MSSM_EVENT_PORT_FABRIC_RSCN: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn_sending); + ms->retry_cnt = 0; + bfa_fcs_port_ms_send_gfn(ms, NULL); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_gmal_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_FCXP_SENT: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gmal); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ms->port), + &ms->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_gmal(struct bfa_fcs_port_ms_s *ms, enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + if (ms->retry_cnt++ < BFA_FCS_MS_CMD_MAX_RETRIES) { + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gmal_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ms->port), + &ms->timer, bfa_fcs_port_ms_timeout, ms, + BFA_FCS_RETRY_TIMEOUT); + } else { + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn_sending); + bfa_fcs_port_ms_send_gfn(ms, NULL); + ms->retry_cnt = 0; + } + break; + + case MSSM_EVENT_RSP_OK: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn_sending); + bfa_fcs_port_ms_send_gfn(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_discard(ms->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_gmal_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gmal_sending); + bfa_fcs_port_ms_send_gmal(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_timer_stop(&ms->timer); + break; + + default: + bfa_assert(0); + } +} + +/** + * ms_pvt MS local functions + */ + +static void +bfa_fcs_port_ms_send_gmal(void *ms_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ms_s *ms = ms_cbarg; + struct bfa_fcs_port_s *port = ms->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &ms->fcxp_wqe, + bfa_fcs_port_ms_send_gmal, ms); + return; + } + ms->fcxp = fcxp; + + len = fc_gmal_req_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), + bfa_lps_get_peer_nwwn(port->fabric->lps)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ms_gmal_response, + (void *)ms, FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(ms, MSSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_port_ms_gmal_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ms_s *ms = (struct bfa_fcs_port_ms_s *)cbarg; + struct bfa_fcs_port_s *port = ms->port; + struct ct_hdr_s *cthdr = NULL; + struct fcgs_gmal_resp_s *gmal_resp; + struct fc_gmal_entry_s *gmal_entry; + u32 num_entries; + u8 *rsp_str; + + bfa_trc(port->fcs, req_status); + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + gmal_resp = (struct fcgs_gmal_resp_s *)(cthdr + 1); + num_entries = bfa_os_ntohl(gmal_resp->ms_len); + if (num_entries == 0) { + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + return; + } + /* + * The response could contain multiple Entries. + * Entries for SNMP interface, etc. + * We look for the entry with a telnet prefix. + * First "http://" entry refers to IP addr + */ + + gmal_entry = (struct fc_gmal_entry_s *)gmal_resp->ms_ma; + while (num_entries > 0) { + if (strncmp + (gmal_entry->prefix, CT_GMAL_RESP_PREFIX_HTTP, + sizeof(gmal_entry->prefix)) == 0) { + + /* + * if the IP address is terminating with a '/', + * remove it. *Byte 0 consists of the length + * of the string. + */ + rsp_str = &(gmal_entry->prefix[0]); + if (rsp_str[gmal_entry->len - 1] == '/') + rsp_str[gmal_entry->len - 1] = 0; + /* + * copy IP Address to fabric + */ + strncpy(bfa_fcs_port_get_fabric_ipaddr(port), + gmal_entry->ip_addr, + BFA_FCS_FABRIC_IPADDR_SZ); + break; + } else { + --num_entries; + ++gmal_entry; + } + } + + bfa_sm_send_event(ms, MSSM_EVENT_RSP_OK); + return; + } + + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); +} + +static void +bfa_fcs_port_ms_sm_gfn_sending(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_FCXP_SENT: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ms->port), + &ms->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_gfn(struct bfa_fcs_port_ms_s *ms, enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + if (ms->retry_cnt++ < BFA_FCS_MS_CMD_MAX_RETRIES) { + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn_retry); + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ms->port), + &ms->timer, bfa_fcs_port_ms_timeout, ms, + BFA_FCS_RETRY_TIMEOUT); + } else { + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_online); + ms->retry_cnt = 0; + } + break; + + case MSSM_EVENT_RSP_OK: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_online); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_fcxp_discard(ms->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ms_sm_gfn_retry(struct bfa_fcs_port_ms_s *ms, + enum port_ms_event event) +{ + bfa_trc(ms->port->fcs, ms->port->port_cfg.pwwn); + bfa_trc(ms->port->fcs, event); + + switch (event) { + case MSSM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_gfn_sending); + bfa_fcs_port_ms_send_gfn(ms, NULL); + break; + + case MSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + bfa_timer_stop(&ms->timer); + break; + + default: + bfa_assert(0); + } +} + +/** + * ms_pvt MS local functions + */ + +static void +bfa_fcs_port_ms_send_gfn(void *ms_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ms_s *ms = ms_cbarg; + struct bfa_fcs_port_s *port = ms->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &ms->fcxp_wqe, + bfa_fcs_port_ms_send_gfn, ms); + return; + } + ms->fcxp = fcxp; + + len = fc_gfn_req_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), + bfa_lps_get_peer_nwwn(port->fabric->lps)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ms_gfn_response, + (void *)ms, FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(ms, MSSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_port_ms_gfn_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ms_s *ms = (struct bfa_fcs_port_ms_s *)cbarg; + struct bfa_fcs_port_s *port = ms->port; + struct ct_hdr_s *cthdr = NULL; + wwn_t *gfn_resp; + + bfa_trc(port->fcs, req_status); + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + gfn_resp = (wwn_t *) (cthdr + 1); + /* + * check if it has actually changed + */ + if ((memcmp + ((void *)&bfa_fcs_port_get_fabric_name(port), gfn_resp, + sizeof(wwn_t)) != 0)) + bfa_fcs_fabric_set_fabric_name(port->fabric, *gfn_resp); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_OK); + return; + } + + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); +} + +/** + * ms_pvt MS local functions + */ + +static void +bfa_fcs_port_ms_send_plogi(void *ms_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ms_s *ms = ms_cbarg; + struct bfa_fcs_port_s *port = ms->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ms_plogi_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ms->fcxp_wqe, + bfa_fcs_port_ms_send_plogi, ms); + return; + } + ms->fcxp = fcxp; + + len = fc_plogi_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_os_hton3b(FC_MGMT_SERVER), + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ms_plogi_response, + (void *)ms, FC_MAX_PDUSZ, FC_RA_TOV); + + port->stats.ms_plogi_sent++; + bfa_sm_send_event(ms, MSSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_port_ms_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ms_s *ms = (struct bfa_fcs_port_ms_s *)cbarg; + + struct bfa_fcs_port_s *port = ms->port; + struct fc_els_cmd_s *els_cmd; + struct fc_ls_rjt_s *ls_rjt; + + bfa_trc(port->fcs, req_status); + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + port->stats.ms_plogi_rsp_err++; + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + + switch (els_cmd->els_code) { + + case FC_ELS_ACC: + if (rsp_len < sizeof(struct fc_logi_s)) { + bfa_trc(port->fcs, rsp_len); + port->stats.ms_plogi_acc_err++; + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + break; + } + port->stats.ms_plogi_accepts++; + bfa_sm_send_event(ms, MSSM_EVENT_RSP_OK); + break; + + case FC_ELS_LS_RJT: + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + + bfa_trc(port->fcs, ls_rjt->reason_code); + bfa_trc(port->fcs, ls_rjt->reason_code_expl); + + port->stats.ms_rejects++; + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + break; + + default: + port->stats.ms_plogi_unknown_rsp++; + bfa_trc(port->fcs, els_cmd->els_code); + bfa_sm_send_event(ms, MSSM_EVENT_RSP_ERROR); + } +} + +static void +bfa_fcs_port_ms_timeout(void *arg) +{ + struct bfa_fcs_port_ms_s *ms = (struct bfa_fcs_port_ms_s *)arg; + + ms->port->stats.ms_timeouts++; + bfa_sm_send_event(ms, MSSM_EVENT_TIMEOUT); +} + + +void +bfa_fcs_port_ms_init(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ms_s *ms = BFA_FCS_GET_MS_FROM_PORT(port); + + ms->port = port; + bfa_sm_set_state(ms, bfa_fcs_port_ms_sm_offline); + + /* + * Invoke init routines of sub modules. + */ + bfa_fcs_port_fdmi_init(ms); +} + +void +bfa_fcs_port_ms_offline(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ms_s *ms = BFA_FCS_GET_MS_FROM_PORT(port); + + ms->port = port; + bfa_sm_send_event(ms, MSSM_EVENT_PORT_OFFLINE); +} + +void +bfa_fcs_port_ms_online(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ms_s *ms = BFA_FCS_GET_MS_FROM_PORT(port); + + ms->port = port; + bfa_sm_send_event(ms, MSSM_EVENT_PORT_ONLINE); +} + +void +bfa_fcs_port_ms_fabric_rscn(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ms_s *ms = BFA_FCS_GET_MS_FROM_PORT(port); + + /* + * @todo. Handle this only when in Online state + */ + if (bfa_sm_cmp_state(ms, bfa_fcs_port_ms_sm_online)) + bfa_sm_send_event(ms, MSSM_EVENT_PORT_FABRIC_RSCN); +} diff --git a/drivers/scsi/bfa/n2n.c b/drivers/scsi/bfa/n2n.c new file mode 100644 index 000000000000..735456824346 --- /dev/null +++ b/drivers/scsi/bfa/n2n.c @@ -0,0 +1,105 @@ +/* + * 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. + */ + +/** + * n2n.c n2n implementation. + */ +#include +#include +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_trcmod.h" +#include "lport_priv.h" + +BFA_TRC_FILE(FCS, N2N); + +/** + * Called by fcs/port to initialize N2N topology. + */ +void +bfa_fcs_port_n2n_init(struct bfa_fcs_port_s *port) +{ +} + +/** + * Called by fcs/port to notify transition to online state. + */ +void +bfa_fcs_port_n2n_online(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_n2n_s *n2n_port = &port->port_topo.pn2n; + struct bfa_port_cfg_s *pcfg = &port->port_cfg; + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, pcfg->pwwn); + + /* + * If our PWWN is > than that of the r-port, we have to initiate PLOGI + * and assign an Address. if not, we need to wait for its PLOGI. + * + * If our PWWN is < than that of the remote port, it will send a PLOGI + * with the PIDs assigned. The rport state machine take care of this + * incoming PLOGI. + */ + if (memcmp + ((void *)&pcfg->pwwn, (void *)&n2n_port->rem_port_wwn, + sizeof(wwn_t)) > 0) { + port->pid = N2N_LOCAL_PID; + /** + * First, check if we know the device by pwwn. + */ + rport = bfa_fcs_port_get_rport_by_pwwn(port, + n2n_port->rem_port_wwn); + if (rport) { + bfa_trc(port->fcs, rport->pid); + bfa_trc(port->fcs, rport->pwwn); + rport->pid = N2N_REMOTE_PID; + bfa_fcs_rport_online(rport); + return; + } + + /* + * In n2n there can be only one rport. Delete the old one whose + * pid should be zero, because it is offline. + */ + if (port->num_rports > 0) { + rport = bfa_fcs_port_get_rport_by_pid(port, 0); + bfa_assert(rport != NULL); + if (rport) { + bfa_trc(port->fcs, rport->pwwn); + bfa_fcs_rport_delete(rport); + } + } + bfa_fcs_rport_create(port, N2N_REMOTE_PID); + } +} + +/** + * Called by fcs/port to notify transition to offline state. + */ +void +bfa_fcs_port_n2n_offline(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_n2n_s *n2n_port = &port->port_topo.pn2n; + + bfa_trc(port->fcs, port->pid); + port->pid = 0; + n2n_port->rem_port_wwn = 0; + n2n_port->reply_oxid = 0; +} + + diff --git a/drivers/scsi/bfa/ns.c b/drivers/scsi/bfa/ns.c new file mode 100644 index 000000000000..59fea99d67a4 --- /dev/null +++ b/drivers/scsi/bfa/ns.c @@ -0,0 +1,1243 @@ +/* + * 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. + */ + +/** + * @page ns_sm_info VPORT NS State Machine + * + * @section ns_sm_interactions VPORT NS State Machine Interactions + * + * @section ns_sm VPORT NS State Machine + * img ns_sm.jpg + */ +#include +#include +#include +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs.h" +#include "lport_priv.h" + +BFA_TRC_FILE(FCS, NS); + +/* + * forward declarations + */ +static void bfa_fcs_port_ns_send_plogi(void *ns_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ns_send_rspn_id(void *ns_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ns_send_rft_id(void *ns_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ns_send_rff_id(void *ns_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ns_send_gid_ft(void *ns_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_ns_timeout(void *arg); +static void bfa_fcs_port_ns_plogi_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ns_rspn_id_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ns_rft_id_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ns_rff_id_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ns_gid_ft_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_ns_process_gidft_pids(struct bfa_fcs_port_s *port, + u32 *pid_buf, + u32 n_pids); + +static void bfa_fcs_port_ns_boot_target_disc(struct bfa_fcs_port_s *port); +/** + * fcs_ns_sm FCS nameserver interface state machine + */ + +/** + * VPort NS State Machine events + */ +enum vport_ns_event { + NSSM_EVENT_PORT_ONLINE = 1, + NSSM_EVENT_PORT_OFFLINE = 2, + NSSM_EVENT_PLOGI_SENT = 3, + NSSM_EVENT_RSP_OK = 4, + NSSM_EVENT_RSP_ERROR = 5, + NSSM_EVENT_TIMEOUT = 6, + NSSM_EVENT_NS_QUERY = 7, + NSSM_EVENT_RSPNID_SENT = 8, + NSSM_EVENT_RFTID_SENT = 9, + NSSM_EVENT_RFFID_SENT = 10, + NSSM_EVENT_GIDFT_SENT = 11, +}; + +static void bfa_fcs_port_ns_sm_offline(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_plogi_sending(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_plogi(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_plogi_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_sending_rspn_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rspn_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rspn_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_sending_rft_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rft_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rft_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_sending_rff_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rff_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_rff_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_sending_gid_ft(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_gid_ft(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_gid_ft_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +static void bfa_fcs_port_ns_sm_online(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event); +/** + * Start in offline state - awaiting linkup + */ +static void +bfa_fcs_port_ns_sm_offline(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_PORT_ONLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_plogi_sending); + bfa_fcs_port_ns_send_plogi(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_plogi_sending(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_PLOGI_SENT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_plogi); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port), + &ns->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_plogi(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_plogi_retry); + ns->port->stats.ns_retries++; + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port), &ns->timer, + bfa_fcs_port_ns_timeout, ns, + BFA_FCS_RETRY_TIMEOUT); + break; + + case NSSM_EVENT_RSP_OK: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rspn_id); + bfa_fcs_port_ns_send_rspn_id(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_discard(ns->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_plogi_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_plogi_sending); + bfa_fcs_port_ns_send_plogi(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_timer_stop(&ns->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_sending_rspn_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSPNID_SENT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rspn_id); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port), + &ns->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rspn_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rspn_id_retry); + ns->port->stats.ns_retries++; + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port), &ns->timer, + bfa_fcs_port_ns_timeout, ns, + BFA_FCS_RETRY_TIMEOUT); + break; + + case NSSM_EVENT_RSP_OK: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rft_id); + bfa_fcs_port_ns_send_rft_id(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_fcxp_discard(ns->fcxp); + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rspn_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_TIMEOUT: + /* + * Retry Timer Expired. Re-send + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rspn_id); + bfa_fcs_port_ns_send_rspn_id(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_timer_stop(&ns->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_sending_rft_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RFTID_SENT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rft_id); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port), + &ns->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rft_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSP_OK: + /* + * Now move to register FC4 Features + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rff_id); + bfa_fcs_port_ns_send_rff_id(ns, NULL); + break; + + case NSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rft_id_retry); + ns->port->stats.ns_retries++; + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port), &ns->timer, + bfa_fcs_port_ns_timeout, ns, + BFA_FCS_RETRY_TIMEOUT); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_discard(ns->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rft_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_TIMEOUT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rft_id); + bfa_fcs_port_ns_send_rft_id(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_timer_stop(&ns->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_sending_rff_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RFFID_SENT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rff_id); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port), + &ns->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rff_id(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSP_OK: + + /* + * If min cfg mode is enabled, we donot initiate rport + * discovery with the fabric. Instead, we will retrieve the + * boot targets from HAL/FW. + */ + if (__fcs_min_cfg(ns->port->fcs)) { + bfa_fcs_port_ns_boot_target_disc(ns->port); + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_online); + return; + } + + /* + * If the port role is Initiator Mode issue NS query. + * If it is Target Mode, skip this and go to online. + */ + if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port)) { + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_gid_ft); + bfa_fcs_port_ns_send_gid_ft(ns, NULL); + } else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port)) { + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_online); + } + /* + * kick off mgmt srvr state machine + */ + bfa_fcs_port_ms_online(ns->port); + break; + + case NSSM_EVENT_RSP_ERROR: + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_rff_id_retry); + ns->port->stats.ns_retries++; + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port), &ns->timer, + bfa_fcs_port_ns_timeout, ns, + BFA_FCS_RETRY_TIMEOUT); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_discard(ns->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_rff_id_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_TIMEOUT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_rff_id); + bfa_fcs_port_ns_send_rff_id(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_timer_stop(&ns->timer); + break; + + default: + bfa_assert(0); + } +} +static void +bfa_fcs_port_ns_sm_sending_gid_ft(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_GIDFT_SENT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_gid_ft); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_walloc_cancel(BFA_FCS_GET_HAL_FROM_PORT(ns->port), + &ns->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_gid_ft(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_RSP_OK: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_online); + break; + + case NSSM_EVENT_RSP_ERROR: + /* + * TBD: for certain reject codes, we don't need to retry + */ + /* + * Start timer for a delayed retry + */ + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_gid_ft_retry); + ns->port->stats.ns_retries++; + bfa_timer_start(BFA_FCS_GET_HAL_FROM_PORT(ns->port), &ns->timer, + bfa_fcs_port_ns_timeout, ns, + BFA_FCS_RETRY_TIMEOUT); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_fcxp_discard(ns->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_gid_ft_retry(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_TIMEOUT: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_gid_ft); + bfa_fcs_port_ns_send_gid_ft(ns, NULL); + break; + + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + bfa_timer_stop(&ns->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_ns_sm_online(struct bfa_fcs_port_ns_s *ns, + enum vport_ns_event event) +{ + bfa_trc(ns->port->fcs, ns->port->port_cfg.pwwn); + bfa_trc(ns->port->fcs, event); + + switch (event) { + case NSSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); + break; + + case NSSM_EVENT_NS_QUERY: + /* + * If the port role is Initiator Mode issue NS query. + * If it is Target Mode, skip this and go to online. + */ + if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port)) { + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_sending_gid_ft); + bfa_fcs_port_ns_send_gid_ft(ns, NULL); + }; + break; + + default: + bfa_assert(0); + } +} + + + +/** + * ns_pvt Nameserver local functions + */ + +static void +bfa_fcs_port_ns_send_plogi(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ns_s *ns = ns_cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ns_plogi_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe, + bfa_fcs_port_ns_send_plogi, ns); + return; + } + ns->fcxp = fcxp; + + len = fc_plogi_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_os_hton3b(FC_NAME_SERVER), + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ns_plogi_response, + (void *)ns, FC_MAX_PDUSZ, FC_RA_TOV); + port->stats.ns_plogi_sent++; + + bfa_sm_send_event(ns, NSSM_EVENT_PLOGI_SENT); +} + +static void +bfa_fcs_port_ns_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)cbarg; + struct bfa_fcs_port_s *port = ns->port; + /* struct fc_logi_s *plogi_resp; */ + struct fc_els_cmd_s *els_cmd; + struct fc_ls_rjt_s *ls_rjt; + + bfa_trc(port->fcs, req_status); + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + port->stats.ns_plogi_rsp_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + + switch (els_cmd->els_code) { + + case FC_ELS_ACC: + if (rsp_len < sizeof(struct fc_logi_s)) { + bfa_trc(port->fcs, rsp_len); + port->stats.ns_plogi_acc_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + break; + } + port->stats.ns_plogi_accepts++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + break; + + case FC_ELS_LS_RJT: + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + + bfa_trc(port->fcs, ls_rjt->reason_code); + bfa_trc(port->fcs, ls_rjt->reason_code_expl); + + port->stats.ns_rejects++; + + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + break; + + default: + port->stats.ns_plogi_unknown_rsp++; + bfa_trc(port->fcs, els_cmd->els_code); + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + } +} + +/** + * Register the symbolic port name. + */ +static void +bfa_fcs_port_ns_send_rspn_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ns_s *ns = ns_cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + u8 symbl[256]; + u8 *psymbl = &symbl[0]; + + bfa_os_memset(symbl, 0, sizeof(symbl)); + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ns_rspnid_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe, + bfa_fcs_port_ns_send_rspn_id, ns); + return; + } + ns->fcxp = fcxp; + + /* + * for V-Port, form a Port Symbolic Name + */ + if (port->vport) { + /**For Vports, + * we append the vport's port symbolic name to that of the base port. + */ + + strncpy((char *)psymbl, + (char *) + &(bfa_fcs_port_get_psym_name + (bfa_fcs_get_base_port(port->fcs))), + strlen((char *) + &bfa_fcs_port_get_psym_name(bfa_fcs_get_base_port + (port->fcs)))); + + /* + * Ensure we have a null terminating string. + */ + ((char *) + psymbl)[strlen((char *) + &bfa_fcs_port_get_psym_name + (bfa_fcs_get_base_port(port->fcs)))] = 0; + + strncat((char *)psymbl, + (char *)&(bfa_fcs_port_get_psym_name(port)), + strlen((char *)&bfa_fcs_port_get_psym_name(port))); + } else { + psymbl = (u8 *) &(bfa_fcs_port_get_psym_name(port)); + } + + len = fc_rspnid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), 0, psymbl); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ns_rspn_id_response, + (void *)ns, FC_MAX_PDUSZ, FC_RA_TOV); + + port->stats.ns_rspnid_sent++; + + bfa_sm_send_event(ns, NSSM_EVENT_RSPNID_SENT); +} + +static void +bfa_fcs_port_ns_rspn_id_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + port->stats.ns_rspnid_rsp_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + port->stats.ns_rspnid_accepts++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + return; + } + + port->stats.ns_rspnid_rejects++; + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); +} + +/** + * Register FC4-Types + * TBD, Need to retrieve this from the OS driver, in case IPFC is enabled ? + */ +static void +bfa_fcs_port_ns_send_rft_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ns_s *ns = ns_cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ns_rftid_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe, + bfa_fcs_port_ns_send_rft_id, ns); + return; + } + ns->fcxp = fcxp; + + len = fc_rftid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.roles); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ns_rft_id_response, + (void *)ns, FC_MAX_PDUSZ, FC_RA_TOV); + + port->stats.ns_rftid_sent++; + bfa_sm_send_event(ns, NSSM_EVENT_RFTID_SENT); +} + +static void +bfa_fcs_port_ns_rft_id_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + port->stats.ns_rftid_rsp_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + port->stats.ns_rftid_accepts++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + return; + } + + port->stats.ns_rftid_rejects++; + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); +} + +/** +* Register FC4-Features : Should be done after RFT_ID + */ +static void +bfa_fcs_port_ns_send_rff_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ns_s *ns = ns_cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + u8 fc4_ftrs = 0; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ns_rffid_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe, + bfa_fcs_port_ns_send_rff_id, ns); + return; + } + ns->fcxp = fcxp; + + if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port)) { + fc4_ftrs = FC_GS_FCP_FC4_FEATURE_INITIATOR; + } else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port)) { + fc4_ftrs = FC_GS_FCP_FC4_FEATURE_TARGET; + } + + len = fc_rffid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), 0, FC_TYPE_FCP, + fc4_ftrs); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ns_rff_id_response, + (void *)ns, FC_MAX_PDUSZ, FC_RA_TOV); + + port->stats.ns_rffid_sent++; + bfa_sm_send_event(ns, NSSM_EVENT_RFFID_SENT); +} + +static void +bfa_fcs_port_ns_rff_id_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct ct_hdr_s *cthdr = NULL; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + port->stats.ns_rffid_rsp_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + port->stats.ns_rffid_accepts++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + return; + } + + port->stats.ns_rffid_rejects++; + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + + if (cthdr->reason_code == CT_RSN_NOT_SUPP) { + /* + * if this command is not supported, we don't retry + */ + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + } else { + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + } +} + +/** + * Query Fabric for FC4-Types Devices. + * +* TBD : Need to use a local (FCS private) response buffer, since the response + * can be larger than 2K. + */ +static void +bfa_fcs_port_ns_send_gid_ft(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_ns_s *ns = ns_cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + port->stats.ns_gidft_alloc_wait++; + bfa_fcxp_alloc_wait(port->fcs->bfa, &ns->fcxp_wqe, + bfa_fcs_port_ns_send_gid_ft, ns); + return; + } + ns->fcxp = fcxp; + + /* + * This query is only initiated for FCP initiator mode. + */ + len = fc_gid_ft_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), ns->port->pid, + FC_TYPE_FCP); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_ns_gid_ft_response, + (void *)ns, bfa_fcxp_get_maxrsp(port->fcs->bfa), + FC_RA_TOV); + + port->stats.ns_gidft_sent++; + + bfa_sm_send_event(ns, NSSM_EVENT_GIDFT_SENT); +} + +static void +bfa_fcs_port_ns_gid_ft_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)cbarg; + struct bfa_fcs_port_s *port = ns->port; + struct ct_hdr_s *cthdr = NULL; + u32 n_pids; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + port->stats.ns_gidft_rsp_err++; + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + return; + } + + if (resid_len != 0) { + /* + * TBD : we will need to allocate a larger buffer & retry the + * command + */ + bfa_trc(port->fcs, rsp_len); + bfa_trc(port->fcs, resid_len); + return; + } + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + switch (cthdr->cmd_rsp_code) { + + case CT_RSP_ACCEPT: + + port->stats.ns_gidft_accepts++; + n_pids = (fc_get_ctresp_pyld_len(rsp_len) / sizeof(u32)); + bfa_trc(port->fcs, n_pids); + bfa_fcs_port_ns_process_gidft_pids(port, + (u32 *) (cthdr + 1), + n_pids); + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + break; + + case CT_RSP_REJECT: + + /* + * Check the reason code & explanation. + * There may not have been any FC4 devices in the fabric + */ + port->stats.ns_gidft_rejects++; + bfa_trc(port->fcs, cthdr->reason_code); + bfa_trc(port->fcs, cthdr->exp_code); + + if ((cthdr->reason_code == CT_RSN_UNABLE_TO_PERF) + && (cthdr->exp_code == CT_NS_EXP_FT_NOT_REG)) { + + bfa_sm_send_event(ns, NSSM_EVENT_RSP_OK); + } else { + /* + * for all other errors, retry + */ + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + } + break; + + default: + port->stats.ns_gidft_unknown_rsp++; + bfa_trc(port->fcs, cthdr->cmd_rsp_code); + bfa_sm_send_event(ns, NSSM_EVENT_RSP_ERROR); + } +} + +/** + * This routine will be called by bfa_timer on timer timeouts. + * + * param[in] port - pointer to bfa_fcs_port_t. + * + * return + * void + * +* Special Considerations: + * + * note + */ +static void +bfa_fcs_port_ns_timeout(void *arg) +{ + struct bfa_fcs_port_ns_s *ns = (struct bfa_fcs_port_ns_s *)arg; + + ns->port->stats.ns_timeouts++; + bfa_sm_send_event(ns, NSSM_EVENT_TIMEOUT); +} + +/* + * Process the PID list in GID_FT response + */ +static void +bfa_fcs_port_ns_process_gidft_pids(struct bfa_fcs_port_s *port, + u32 *pid_buf, u32 n_pids) +{ + struct fcgs_gidft_resp_s *gidft_entry; + struct bfa_fcs_rport_s *rport; + u32 ii; + + for (ii = 0; ii < n_pids; ii++) { + gidft_entry = (struct fcgs_gidft_resp_s *) &pid_buf[ii]; + + if (gidft_entry->pid == port->pid) + continue; + + /* + * Check if this rport already exists + */ + rport = bfa_fcs_port_get_rport_by_pid(port, gidft_entry->pid); + if (rport == NULL) { + /* + * this is a new device. create rport + */ + rport = bfa_fcs_rport_create(port, gidft_entry->pid); + } else { + /* + * this rport already exists + */ + bfa_fcs_rport_scn(rport); + } + + bfa_trc(port->fcs, gidft_entry->pid); + + /* + * if the last entry bit is set, bail out. + */ + if (gidft_entry->last) + return; + } +} + +/** + * fcs_ns_public FCS nameserver public interfaces + */ + +/* + * Functions called by port/fab. + * These will send relevant Events to the ns state machine. + */ +void +bfa_fcs_port_ns_init(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ns_s *ns = BFA_FCS_GET_NS_FROM_PORT(port); + + ns->port = port; + bfa_sm_set_state(ns, bfa_fcs_port_ns_sm_offline); +} + +void +bfa_fcs_port_ns_offline(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ns_s *ns = BFA_FCS_GET_NS_FROM_PORT(port); + + ns->port = port; + bfa_sm_send_event(ns, NSSM_EVENT_PORT_OFFLINE); +} + +void +bfa_fcs_port_ns_online(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ns_s *ns = BFA_FCS_GET_NS_FROM_PORT(port); + + ns->port = port; + bfa_sm_send_event(ns, NSSM_EVENT_PORT_ONLINE); +} + +void +bfa_fcs_port_ns_query(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_ns_s *ns = BFA_FCS_GET_NS_FROM_PORT(port); + + bfa_trc(port->fcs, port->pid); + bfa_sm_send_event(ns, NSSM_EVENT_NS_QUERY); +} + +static void +bfa_fcs_port_ns_boot_target_disc(struct bfa_fcs_port_s *port) +{ + + struct bfa_fcs_rport_s *rport; + u8 nwwns; + wwn_t *wwns; + int ii; + + bfa_iocfc_get_bootwwns(port->fcs->bfa, &nwwns, &wwns); + + for (ii = 0; ii < nwwns; ++ii) { + rport = bfa_fcs_rport_create_by_wwn(port, wwns[ii]); + bfa_assert(rport); + } +} + + diff --git a/drivers/scsi/bfa/plog.c b/drivers/scsi/bfa/plog.c new file mode 100644 index 000000000000..86af818d17bb --- /dev/null +++ b/drivers/scsi/bfa/plog.c @@ -0,0 +1,184 @@ +/* + * 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. + */ + +#include +#include +#include + +static int +plkd_validate_logrec(struct bfa_plog_rec_s *pl_rec) +{ + if ((pl_rec->log_type != BFA_PL_LOG_TYPE_INT) + && (pl_rec->log_type != BFA_PL_LOG_TYPE_STRING)) + return 1; + + if ((pl_rec->log_type != BFA_PL_LOG_TYPE_INT) + && (pl_rec->log_num_ints > BFA_PL_INT_LOG_SZ)) + return 1; + + return 0; +} + +static void +bfa_plog_add(struct bfa_plog_s *plog, struct bfa_plog_rec_s *pl_rec) +{ + u16 tail; + struct bfa_plog_rec_s *pl_recp; + + if (plog->plog_enabled == 0) + return; + + if (plkd_validate_logrec(pl_rec)) { + bfa_assert(0); + return; + } + + tail = plog->tail; + + pl_recp = &(plog->plog_recs[tail]); + + bfa_os_memcpy(pl_recp, pl_rec, sizeof(struct bfa_plog_rec_s)); + + pl_recp->tv = BFA_TRC_TS(plog); + BFA_PL_LOG_REC_INCR(plog->tail); + + if (plog->head == plog->tail) + BFA_PL_LOG_REC_INCR(plog->head); +} + +void +bfa_plog_init(struct bfa_plog_s *plog) +{ + bfa_os_memset((char *)plog, 0, sizeof(struct bfa_plog_s)); + + bfa_os_memcpy(plog->plog_sig, BFA_PL_SIG_STR, BFA_PL_SIG_LEN); + plog->head = plog->tail = 0; + plog->plog_enabled = 1; +} + +void +bfa_plog_str(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, + u16 misc, char *log_str) +{ + struct bfa_plog_rec_s lp; + + if (plog->plog_enabled) { + bfa_os_memset(&lp, 0, sizeof(struct bfa_plog_rec_s)); + lp.mid = mid; + lp.eid = event; + lp.log_type = BFA_PL_LOG_TYPE_STRING; + lp.misc = misc; + strncpy(lp.log_entry.string_log, log_str, + BFA_PL_STRING_LOG_SZ - 1); + lp.log_entry.string_log[BFA_PL_STRING_LOG_SZ - 1] = '\0'; + bfa_plog_add(plog, &lp); + } +} + +void +bfa_plog_intarr(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, + u16 misc, u32 *intarr, u32 num_ints) +{ + struct bfa_plog_rec_s lp; + u32 i; + + if (num_ints > BFA_PL_INT_LOG_SZ) + num_ints = BFA_PL_INT_LOG_SZ; + + if (plog->plog_enabled) { + bfa_os_memset(&lp, 0, sizeof(struct bfa_plog_rec_s)); + lp.mid = mid; + lp.eid = event; + lp.log_type = BFA_PL_LOG_TYPE_INT; + lp.misc = misc; + + for (i = 0; i < num_ints; i++) + bfa_os_assign(lp.log_entry.int_log[i], + intarr[i]); + + lp.log_num_ints = (u8) num_ints; + + bfa_plog_add(plog, &lp); + } +} + +void +bfa_plog_fchdr(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, + u16 misc, struct fchs_s *fchdr) +{ + struct bfa_plog_rec_s lp; + u32 *tmp_int = (u32 *) fchdr; + u32 ints[BFA_PL_INT_LOG_SZ]; + + if (plog->plog_enabled) { + bfa_os_memset(&lp, 0, sizeof(struct bfa_plog_rec_s)); + + ints[0] = tmp_int[0]; + ints[1] = tmp_int[1]; + ints[2] = tmp_int[4]; + + bfa_plog_intarr(plog, mid, event, misc, ints, 3); + } +} + +void +bfa_plog_fchdr_and_pl(struct bfa_plog_s *plog, enum bfa_plog_mid mid, + enum bfa_plog_eid event, u16 misc, struct fchs_s *fchdr, + u32 pld_w0) +{ + struct bfa_plog_rec_s lp; + u32 *tmp_int = (u32 *) fchdr; + u32 ints[BFA_PL_INT_LOG_SZ]; + + if (plog->plog_enabled) { + bfa_os_memset(&lp, 0, sizeof(struct bfa_plog_rec_s)); + + ints[0] = tmp_int[0]; + ints[1] = tmp_int[1]; + ints[2] = tmp_int[4]; + ints[3] = pld_w0; + + bfa_plog_intarr(plog, mid, event, misc, ints, 4); + } +} + +void +bfa_plog_clear(struct bfa_plog_s *plog) +{ + plog->head = plog->tail = 0; +} + +void +bfa_plog_enable(struct bfa_plog_s *plog) +{ + plog->plog_enabled = 1; +} + +void +bfa_plog_disable(struct bfa_plog_s *plog) +{ + plog->plog_enabled = 0; +} + +bfa_boolean_t +bfa_plog_get_setting(struct bfa_plog_s *plog) +{ + return((bfa_boolean_t)plog->plog_enabled); +} diff --git a/drivers/scsi/bfa/rport.c b/drivers/scsi/bfa/rport.c new file mode 100644 index 000000000000..9cf58bb138dc --- /dev/null +++ b/drivers/scsi/bfa/rport.c @@ -0,0 +1,2618 @@ +/* + * 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. + */ + +/** + * rport.c Remote port implementation. + */ + +#include +#include +#include "fcbuild.h" +#include "fcs_vport.h" +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_fcpim.h" +#include "fcs_fcptm.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs.h" +#include +#include + +BFA_TRC_FILE(FCS, RPORT); + +#define BFA_FCS_RPORT_MAX_RETRIES (5) + +/* In millisecs */ +static u32 bfa_fcs_rport_del_timeout = + BFA_FCS_RPORT_DEF_DEL_TIMEOUT * 1000; + +/* + * forward declarations + */ +static struct bfa_fcs_rport_s *bfa_fcs_rport_alloc(struct bfa_fcs_port_s *port, + wwn_t pwwn, u32 rpid); +static void bfa_fcs_rport_free(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_hal_online(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_online_action(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_offline_action(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_update(struct bfa_fcs_rport_s *rport, + struct fc_logi_s *plogi); +static void bfa_fcs_rport_fc4_pause(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_fc4_resume(struct bfa_fcs_rport_s *rport); +static void bfa_fcs_rport_timeout(void *arg); +static void bfa_fcs_rport_send_plogi(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rport_send_plogiacc(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rport_plogi_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_rport_send_adisc(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rport_adisc_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_rport_send_gidpn(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rport_gidpn_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_rport_send_logo(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rport_send_logo_acc(void *rport_cbarg); +static void bfa_fcs_rport_process_prli(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u16 len); +static void bfa_fcs_rport_send_ls_rjt(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u8 reason_code, + u8 reason_code_expl); +static void bfa_fcs_rport_process_adisc(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u16 len); +/** + * fcs_rport_sm FCS rport state machine events + */ + +enum rport_event { + RPSM_EVENT_PLOGI_SEND = 1, /* new rport; start with PLOGI */ + RPSM_EVENT_PLOGI_RCVD = 2, /* Inbound PLOGI from remote port */ + RPSM_EVENT_PLOGI_COMP = 3, /* PLOGI completed to rport */ + RPSM_EVENT_LOGO_RCVD = 4, /* LOGO from remote device */ + RPSM_EVENT_LOGO_IMP = 5, /* implicit logo for SLER */ + RPSM_EVENT_FCXP_SENT = 6, /* Frame from has been sent */ + RPSM_EVENT_DELETE = 7, /* RPORT delete request */ + RPSM_EVENT_SCN = 8, /* state change notification */ + RPSM_EVENT_ACCEPTED = 9,/* Good response from remote device */ + RPSM_EVENT_FAILED = 10, /* Request to rport failed. */ + RPSM_EVENT_TIMEOUT = 11, /* Rport SM timeout event */ + RPSM_EVENT_HCB_ONLINE = 12, /* BFA rport online callback */ + RPSM_EVENT_HCB_OFFLINE = 13, /* BFA rport offline callback */ + RPSM_EVENT_FC4_OFFLINE = 14, /* FC-4 offline complete */ + RPSM_EVENT_ADDRESS_CHANGE = 15, /* Rport's PID has changed */ + RPSM_EVENT_ADDRESS_DISC = 16 /* Need to Discover rport's PID */ +}; + +static void bfa_fcs_rport_sm_uninit(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_plogi_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_plogiacc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_plogi_retry(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_hal_online(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_online(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsquery_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_fc4_logorcv(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_fc4_logosend(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_fc4_offline(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_hcb_offline(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_hcb_logorcv(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_hcb_logosend(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_logo_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_offline(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsdisc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsdisc_retry(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport, + enum rport_event event); + +static struct bfa_sm_table_s rport_sm_table[] = { + {BFA_SM(bfa_fcs_rport_sm_uninit), BFA_RPORT_UNINIT}, + {BFA_SM(bfa_fcs_rport_sm_plogi_sending), BFA_RPORT_PLOGI}, + {BFA_SM(bfa_fcs_rport_sm_plogiacc_sending), BFA_RPORT_ONLINE}, + {BFA_SM(bfa_fcs_rport_sm_plogi_retry), BFA_RPORT_PLOGI_RETRY}, + {BFA_SM(bfa_fcs_rport_sm_plogi), BFA_RPORT_PLOGI}, + {BFA_SM(bfa_fcs_rport_sm_hal_online), BFA_RPORT_ONLINE}, + {BFA_SM(bfa_fcs_rport_sm_online), BFA_RPORT_ONLINE}, + {BFA_SM(bfa_fcs_rport_sm_nsquery_sending), BFA_RPORT_NSQUERY}, + {BFA_SM(bfa_fcs_rport_sm_nsquery), BFA_RPORT_NSQUERY}, + {BFA_SM(bfa_fcs_rport_sm_adisc_sending), BFA_RPORT_ADISC}, + {BFA_SM(bfa_fcs_rport_sm_adisc), BFA_RPORT_ADISC}, + {BFA_SM(bfa_fcs_rport_sm_fc4_logorcv), BFA_RPORT_LOGORCV}, + {BFA_SM(bfa_fcs_rport_sm_fc4_logosend), BFA_RPORT_LOGO}, + {BFA_SM(bfa_fcs_rport_sm_fc4_offline), BFA_RPORT_OFFLINE}, + {BFA_SM(bfa_fcs_rport_sm_hcb_offline), BFA_RPORT_OFFLINE}, + {BFA_SM(bfa_fcs_rport_sm_hcb_logorcv), BFA_RPORT_LOGORCV}, + {BFA_SM(bfa_fcs_rport_sm_hcb_logosend), BFA_RPORT_LOGO}, + {BFA_SM(bfa_fcs_rport_sm_logo_sending), BFA_RPORT_LOGO}, + {BFA_SM(bfa_fcs_rport_sm_offline), BFA_RPORT_OFFLINE}, + {BFA_SM(bfa_fcs_rport_sm_nsdisc_sending), BFA_RPORT_NSDISC}, + {BFA_SM(bfa_fcs_rport_sm_nsdisc_retry), BFA_RPORT_NSDISC}, + {BFA_SM(bfa_fcs_rport_sm_nsdisc_sent), BFA_RPORT_NSDISC}, +}; + +/** + * Beginning state. + */ +static void +bfa_fcs_rport_sm_uninit(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_PLOGI_SEND: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending); + rport->plogi_retries = 0; + bfa_fcs_rport_send_plogi(rport, NULL); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_fcs_rport_hal_online(rport); + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + case RPSM_EVENT_ADDRESS_DISC: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + default: + bfa_assert(0); + } +} + +/** + * PLOGI is being sent. + */ +static void +bfa_fcs_rport_sm_plogi_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_SCN: + break; + + default: + bfa_assert(0); + } +} + +/** + * PLOGI is being sent. + */ +static void +bfa_fcs_rport_sm_plogiacc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_fcs_rport_hal_online(rport); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_SCN: + /** + * Ignore, SCN is possibly online notification. + */ + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_HCB_OFFLINE: + /** + * Ignore BFA callback, on a PLOGI receive we call bfa offline. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * PLOGI is sent. + */ +static void +bfa_fcs_rport_sm_plogi_retry(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_SCN: + bfa_timer_stop(&rport->timer); + /* + * !! fall through !! + */ + + case RPSM_EVENT_TIMEOUT: + rport->plogi_retries++; + if (rport->plogi_retries < BFA_FCS_RPORT_MAX_RETRIES) { + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending); + bfa_fcs_rport_send_plogi(rport, NULL); + } else { + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + } + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_timer_stop(&rport->timer); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_stop(&rport->timer); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_hal_online(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * PLOGI is sent. + */ +static void +bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_ACCEPTED: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + rport->plogi_retries = 0; + bfa_fcs_rport_hal_online(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_fcs_rport_send_logo_acc(rport); + bfa_fcxp_discard(rport->fcxp); + /* + * !! fall through !! + */ + case RPSM_EVENT_FAILED: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_retry); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + BFA_FCS_RETRY_TIMEOUT); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_discard(rport->fcxp); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_fcxp_discard(rport->fcxp); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_SCN: + /** + * Ignore SCN - wait for PLOGI response. + */ + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_hal_online(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * PLOGI is complete. Awaiting BFA rport online callback. FC-4s + * are offline. + */ +static void +bfa_fcs_rport_sm_hal_online(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_HCB_ONLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_online); + bfa_fcs_rport_online_action(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logorcv); + bfa_rport_offline(rport->bfa_rport); + break; + + case RPSM_EVENT_LOGO_IMP: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_offline); + bfa_rport_offline(rport->bfa_rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_rport_offline(rport->bfa_rport); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logosend); + bfa_rport_offline(rport->bfa_rport); + break; + + case RPSM_EVENT_SCN: + /** + * @todo + * Ignore SCN - PLOGI just completed, FC-4 login should detect + * device failures. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is ONLINE. FC-4s active. + */ +static void +bfa_fcs_rport_sm_online(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_SCN: + /** + * Pause FC-4 activity till rport is authenticated. + * In switched fabrics, check presence of device in nameserver + * first. + */ + bfa_fcs_rport_fc4_pause(rport); + + if (bfa_fcs_fabric_is_switched(rport->port->fabric)) { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsquery_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + } else { + bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_sending); + bfa_fcs_rport_send_adisc(rport, NULL); + } + break; + + case RPSM_EVENT_PLOGI_RCVD: + case RPSM_EVENT_LOGO_IMP: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_PLOGI_COMP: + break; + + default: + bfa_assert(0); + } +} + +/** + * An SCN event is received in ONLINE state. NS query is being sent + * prior to ADISC authentication with rport. FC-4s are paused. + */ +static void +bfa_fcs_rport_sm_nsquery_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsquery); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_SCN: + /** + * ignore SCN, wait for response to query itself + */ + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_PLOGI_RCVD: + case RPSM_EVENT_ADDRESS_CHANGE: + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * An SCN event is received in ONLINE state. NS query is sent to rport. + * FC-4s are paused. + */ +static void +bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_ACCEPTED: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_sending); + bfa_fcs_rport_send_adisc(rport, NULL); + break; + + case RPSM_EVENT_FAILED: + rport->ns_retries++; + if (rport->ns_retries < BFA_FCS_RPORT_MAX_RETRIES) { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsquery_sending); + bfa_fcs_rport_send_gidpn(rport, NULL); + } else { + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcs_rport_offline_action(rport); + } + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_SCN: + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_PLOGI_COMP: + case RPSM_EVENT_ADDRESS_CHANGE: + case RPSM_EVENT_PLOGI_RCVD: + case RPSM_EVENT_LOGO_IMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * An SCN event is received in ONLINE state. ADISC is being sent for + * authenticating with rport. FC-4s are paused. + */ +static void +bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_LOGO_IMP: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_SCN: + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_offline_action(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * An SCN event is received in ONLINE state. ADISC is to rport. + * FC-4s are paused. + */ +static void +bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_ACCEPTED: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_online); + bfa_fcs_rport_fc4_resume(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + /** + * Too complex to cleanup FC-4 & rport and then acc to PLOGI. + * At least go offline when a PLOGI is received. + */ + bfa_fcxp_discard(rport->fcxp); + /* + * !!! fall through !!! + */ + + case RPSM_EVENT_FAILED: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_SCN: + /** + * already processing RSCN + */ + break; + + case RPSM_EVENT_LOGO_IMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logorcv); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_offline_action(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport has sent LOGO. Awaiting FC-4 offline completion callback. + */ +static void +bfa_fcs_rport_sm_fc4_logorcv(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FC4_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logorcv); + bfa_rport_offline(rport->bfa_rport); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + break; + + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_ADDRESS_CHANGE: + break; + + default: + bfa_assert(0); + } +} + +/** + * LOGO needs to be sent to rport. Awaiting FC-4 offline completion + * callback. + */ +static void +bfa_fcs_rport_sm_fc4_logosend(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FC4_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logosend); + bfa_rport_offline(rport->bfa_rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is going offline. Awaiting FC-4 offline completion callback. + */ +static void +bfa_fcs_rport_sm_fc4_offline(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FC4_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_offline); + bfa_rport_offline(rport->bfa_rport); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_LOGO_IMP: + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_ADDRESS_CHANGE: + /** + * rport is already going offline. + * SCN - ignore and wait till transitioning to offline state + */ + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_logosend); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is offline. FC-4s are offline. Awaiting BFA rport offline + * callback. + */ +static void +bfa_fcs_rport_sm_hcb_offline(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_HCB_OFFLINE: + case RPSM_EVENT_ADDRESS_CHANGE: + if (bfa_fcs_port_is_online(rport->port)) { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + } else { + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + } + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_LOGO_RCVD: + /** + * Ignore, already offline. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is offline. FC-4s are offline. Awaiting BFA rport offline + * callback to send LOGO accept. + */ +static void +bfa_fcs_rport_sm_hcb_logorcv(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_HCB_OFFLINE: + case RPSM_EVENT_ADDRESS_CHANGE: + if (rport->pid) + bfa_fcs_rport_send_logo_acc(rport); + /* + * If the lport is online and if the rport is not a well known + * address port, we try to re-discover the r-port. + */ + if (bfa_fcs_port_is_online(rport->port) + && (!BFA_FCS_PID_IS_WKA(rport->pid))) { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + } else { + /* + * if it is not a well known address, reset the pid to + * + */ + if (!BFA_FCS_PID_IS_WKA(rport->pid)) + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + } + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_logosend); + break; + + case RPSM_EVENT_LOGO_IMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_offline); + break; + + case RPSM_EVENT_LOGO_RCVD: + /** + * Ignore - already processing a LOGO. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is being deleted. FC-4s are offline. Awaiting BFA rport offline + * callback to send LOGO. + */ +static void +bfa_fcs_rport_sm_hcb_logosend(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_HCB_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_logo_sending); + bfa_fcs_rport_send_logo(rport, NULL); + break; + + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_ADDRESS_CHANGE: + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is being deleted. FC-4s are offline. LOGO is being sent. + */ +static void +bfa_fcs_rport_sm_logo_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + /* + * Once LOGO is sent, we donot wait for the response + */ + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_ADDRESS_CHANGE: + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_free(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport is offline. FC-4s are offline. BFA rport is offline. + * Timer active to delete stale rport. + */ +static void +bfa_fcs_rport_sm_offline(struct bfa_fcs_rport_s *rport, enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_TIMEOUT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + bfa_timer_stop(&rport->timer); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_LOGO_IMP: + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_hal_online(rport); + break; + + case RPSM_EVENT_PLOGI_SEND: + bfa_timer_stop(&rport->timer); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending); + rport->plogi_retries = 0; + bfa_fcs_rport_send_plogi(rport, NULL); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport address has changed. Nameserver discovery request is being sent. + */ +static void +bfa_fcs_rport_sm_nsdisc_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sent); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_PLOGI_SEND: + break; + + case RPSM_EVENT_ADDRESS_CHANGE: + rport->ns_retries = 0; /* reset the retry count */ + break; + + case RPSM_EVENT_LOGO_IMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_hal_online(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Nameserver discovery failed. Waiting for timeout to retry. + */ +static void +bfa_fcs_rport_sm_nsdisc_retry(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_TIMEOUT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_SCN: + case RPSM_EVENT_ADDRESS_CHANGE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); + bfa_timer_stop(&rport->timer); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_stop(&rport->timer); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_LOGO_RCVD: + bfa_fcs_rport_send_logo_acc(rport); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_timer_stop(&rport->timer); + bfa_fcs_rport_hal_online(rport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Rport address has changed. Nameserver discovery request is sent. + */ +static void +bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_ACCEPTED: + case RPSM_EVENT_ADDRESS_CHANGE: + if (rport->pid) { + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending); + bfa_fcs_rport_send_plogi(rport, NULL); + } else { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsdisc_sending); + rport->ns_retries = 0; + bfa_fcs_rport_send_gidpn(rport, NULL); + } + break; + + case RPSM_EVENT_FAILED: + rport->ns_retries++; + if (rport->ns_retries < BFA_FCS_RPORT_MAX_RETRIES) { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_nsdisc_sending); + bfa_fcs_rport_send_gidpn(rport, NULL); + } else { + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + }; + break; + + case RPSM_EVENT_DELETE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_free(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_LOGO_IMP: + rport->pid = 0; + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_discard(rport->fcxp); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_SCN: + /** + * ignore, wait for NS query response + */ + break; + + case RPSM_EVENT_LOGO_RCVD: + /** + * Not logged-in yet. Accept LOGO. + */ + bfa_fcs_rport_send_logo_acc(rport); + break; + + case RPSM_EVENT_PLOGI_COMP: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_hal_online(rport); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * fcs_rport_private FCS RPORT provate functions + */ + +static void +bfa_fcs_rport_send_plogi(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(rport->fcs, rport->pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe, + bfa_fcs_rport_send_plogi, rport); + return; + } + rport->fcxp = fcxp; + + len = fc_plogi_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_rport_plogi_response, + (void *)rport, FC_MAX_PDUSZ, FC_RA_TOV); + + rport->stats.plogis++; + bfa_sm_send_event(rport, RPSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_rport_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + struct fc_logi_s *plogi_rsp; + struct fc_ls_rjt_s *ls_rjt; + struct bfa_fcs_rport_s *twin; + struct list_head *qe; + + bfa_trc(rport->fcs, rport->pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(rport->fcs, req_status); + rport->stats.plogi_failed++; + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); + return; + } + + plogi_rsp = (struct fc_logi_s *) BFA_FCXP_RSP_PLD(fcxp); + + /** + * Check for failure first. + */ + if (plogi_rsp->els_cmd.els_code != FC_ELS_ACC) { + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + + bfa_trc(rport->fcs, ls_rjt->reason_code); + bfa_trc(rport->fcs, ls_rjt->reason_code_expl); + + rport->stats.plogi_rejects++; + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); + return; + } + + /** + * PLOGI is complete. Make sure this device is not one of the known + * device with a new FC port address. + */ + list_for_each(qe, &rport->port->rport_q) { + twin = (struct bfa_fcs_rport_s *)qe; + if (twin == rport) + continue; + if (!rport->pwwn && (plogi_rsp->port_name == twin->pwwn)) { + bfa_trc(rport->fcs, twin->pid); + bfa_trc(rport->fcs, rport->pid); + + /* + * Update plogi stats in twin + */ + twin->stats.plogis += rport->stats.plogis; + twin->stats.plogi_rejects += rport->stats.plogi_rejects; + twin->stats.plogi_timeouts += + rport->stats.plogi_timeouts; + twin->stats.plogi_failed += rport->stats.plogi_failed; + twin->stats.plogi_rcvd += rport->stats.plogi_rcvd; + twin->stats.plogi_accs++; + + bfa_fcs_rport_delete(rport); + + bfa_fcs_rport_update(twin, plogi_rsp); + twin->pid = rsp_fchs->s_id; + bfa_sm_send_event(twin, RPSM_EVENT_PLOGI_COMP); + return; + } + } + + /** + * Normal login path -- no evil twins. + */ + rport->stats.plogi_accs++; + bfa_fcs_rport_update(rport, plogi_rsp); + bfa_sm_send_event(rport, RPSM_EVENT_ACCEPTED); +} + +static void +bfa_fcs_rport_send_plogiacc(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->reply_oxid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe, + bfa_fcs_rport_send_plogiacc, rport); + return; + } + rport->fcxp = fcxp; + + len = fc_plogi_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), rport->reply_oxid, + port->port_cfg.pwwn, port->port_cfg.nwwn, + bfa_pport_get_maxfrsize(port->fcs->bfa)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0); + + bfa_sm_send_event(rport, RPSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_rport_send_adisc(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(rport->fcs, rport->pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe, + bfa_fcs_rport_send_adisc, rport); + return; + } + rport->fcxp = fcxp; + + len = fc_adisc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), 0, + port->port_cfg.pwwn, port->port_cfg.nwwn); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_rport_adisc_response, + rport, FC_MAX_PDUSZ, FC_RA_TOV); + + rport->stats.adisc_sent++; + bfa_sm_send_event(rport, RPSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_rport_adisc_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + void *pld = bfa_fcxp_get_rspbuf(fcxp); + struct fc_ls_rjt_s *ls_rjt; + + if (req_status != BFA_STATUS_OK) { + bfa_trc(rport->fcs, req_status); + rport->stats.adisc_failed++; + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); + return; + } + + if (fc_adisc_rsp_parse((struct fc_adisc_s *)pld, rsp_len, rport->pwwn, + rport->nwwn) == FC_PARSE_OK) { + rport->stats.adisc_accs++; + bfa_sm_send_event(rport, RPSM_EVENT_ACCEPTED); + return; + } + + rport->stats.adisc_rejects++; + ls_rjt = pld; + bfa_trc(rport->fcs, ls_rjt->els_cmd.els_code); + bfa_trc(rport->fcs, ls_rjt->reason_code); + bfa_trc(rport->fcs, ls_rjt->reason_code_expl); + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); +} + +static void +bfa_fcs_rport_send_gidpn(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(rport->fcs, rport->pid); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe, + bfa_fcs_rport_send_gidpn, rport); + return; + } + rport->fcxp = fcxp; + + len = fc_gidpn_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_fcs_port_get_fcid(port), 0, rport->pwwn); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_rport_gidpn_response, + (void *)rport, FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(rport, RPSM_EVENT_FCXP_SENT); +} + +static void +bfa_fcs_rport_gidpn_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + struct bfa_fcs_rport_s *twin; + struct list_head *qe; + struct ct_hdr_s *cthdr; + struct fcgs_gidpn_resp_s *gidpn_rsp; + + bfa_trc(rport->fcs, rport->pwwn); + + cthdr = (struct ct_hdr_s *) BFA_FCXP_RSP_PLD(fcxp); + cthdr->cmd_rsp_code = bfa_os_ntohs(cthdr->cmd_rsp_code); + + if (cthdr->cmd_rsp_code == CT_RSP_ACCEPT) { + /* + * Check if the pid is the same as before. + */ + gidpn_rsp = (struct fcgs_gidpn_resp_s *) (cthdr + 1); + + if (gidpn_rsp->dap == rport->pid) { + /* + * Device is online + */ + bfa_sm_send_event(rport, RPSM_EVENT_ACCEPTED); + } else { + /* + * Device's PID has changed. We need to cleanup and + * re-login. If there is another device with the the + * newly discovered pid, send an scn notice so that its + * new pid can be discovered. + */ + list_for_each(qe, &rport->port->rport_q) { + twin = (struct bfa_fcs_rport_s *)qe; + if (twin == rport) + continue; + if (gidpn_rsp->dap == twin->pid) { + bfa_trc(rport->fcs, twin->pid); + bfa_trc(rport->fcs, rport->pid); + + twin->pid = 0; + bfa_sm_send_event(twin, + RPSM_EVENT_ADDRESS_CHANGE); + } + } + rport->pid = gidpn_rsp->dap; + bfa_sm_send_event(rport, RPSM_EVENT_ADDRESS_CHANGE); + } + return; + } + + /* + * Reject Response + */ + switch (cthdr->reason_code) { + case CT_RSN_LOGICAL_BUSY: + /* + * Need to retry + */ + bfa_sm_send_event(rport, RPSM_EVENT_TIMEOUT); + break; + + case CT_RSN_UNABLE_TO_PERF: + /* + * device doesn't exist : Start timer to cleanup this later. + */ + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); + break; + + default: + bfa_sm_send_event(rport, RPSM_EVENT_FAILED); + break; + } +} + +/** + * Called to send a logout to the rport. + */ +static void +bfa_fcs_rport_send_logo(void *rport_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + u16 len; + + bfa_trc(rport->fcs, rport->pid); + + port = rport->port; + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rport->fcxp_wqe, + bfa_fcs_rport_send_logo, rport); + return; + } + rport->fcxp = fcxp; + + len = fc_logo_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), 0, + bfa_fcs_port_get_pwwn(port)); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, rport, FC_MAX_PDUSZ, + FC_ED_TOV); + + rport->stats.logos++; + bfa_fcxp_discard(rport->fcxp); + bfa_sm_send_event(rport, RPSM_EVENT_FCXP_SENT); +} + +/** + * Send ACC for a LOGO received. + */ +static void +bfa_fcs_rport_send_logo_acc(void *rport_cbarg) +{ + struct bfa_fcs_rport_s *rport = rport_cbarg; + struct bfa_fcs_port_s *port; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + u16 len; + + bfa_trc(rport->fcs, rport->pid); + + port = rport->port; + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + rport->stats.logo_rcvd++; + len = fc_logo_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), rport->reply_oxid); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0); +} + +/** + * This routine will be called by bfa_timer on timer timeouts. + * + * param[in] rport - pointer to bfa_fcs_port_ns_t. + * param[out] rport_status - pointer to return vport status in + * + * return + * void + * +* Special Considerations: + * + * note + */ +static void +bfa_fcs_rport_timeout(void *arg) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)arg; + + rport->stats.plogi_timeouts++; + bfa_sm_send_event(rport, RPSM_EVENT_TIMEOUT); +} + +static void +bfa_fcs_rport_process_prli(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u16 len) +{ + struct bfa_fcxp_s *fcxp; + struct fchs_s fchs; + struct bfa_fcs_port_s *port = rport->port; + struct fc_prli_s *prli; + + bfa_trc(port->fcs, rx_fchs->s_id); + bfa_trc(port->fcs, rx_fchs->d_id); + + rport->stats.prli_rcvd++; + + if (BFA_FCS_VPORT_IS_TARGET_MODE(port)) { + /* + * Target Mode : Let the fcptm handle it + */ + bfa_fcs_tin_rx_prli(rport->tin, rx_fchs, len); + return; + } + + /* + * We are either in Initiator or ipfc Mode + */ + prli = (struct fc_prli_s *) (rx_fchs + 1); + + if (prli->parampage.servparams.initiator) { + bfa_trc(rport->fcs, prli->parampage.type); + rport->scsi_function = BFA_RPORT_INITIATOR; + bfa_fcs_itnim_is_initiator(rport->itnim); + } else { + /* + * @todo: PRLI from a target ? + */ + bfa_trc(port->fcs, rx_fchs->s_id); + rport->scsi_function = BFA_RPORT_TARGET; + } + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_prli_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, + port->port_cfg.roles); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0); +} + +static void +bfa_fcs_rport_process_rpsc(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u16 len) +{ + struct bfa_fcxp_s *fcxp; + struct fchs_s fchs; + struct bfa_fcs_port_s *port = rport->port; + struct fc_rpsc_speed_info_s speeds; + struct bfa_pport_attr_s pport_attr; + + bfa_trc(port->fcs, rx_fchs->s_id); + bfa_trc(port->fcs, rx_fchs->d_id); + + rport->stats.rpsc_rcvd++; + speeds.port_speed_cap = + RPSC_SPEED_CAP_1G | RPSC_SPEED_CAP_2G | RPSC_SPEED_CAP_4G | + RPSC_SPEED_CAP_8G; + + /* + * get curent speed from pport attributes from BFA + */ + bfa_pport_get_attr(port->fcs->bfa, &pport_attr); + + speeds.port_op_speed = fc_bfa_speed_to_rpsc_operspeed(pport_attr.speed); + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_rpsc_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, + &speeds); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0); +} + +static void +bfa_fcs_rport_process_adisc(struct bfa_fcs_rport_s *rport, + struct fchs_s *rx_fchs, u16 len) +{ + struct bfa_fcxp_s *fcxp; + struct fchs_s fchs; + struct bfa_fcs_port_s *port = rport->port; + struct fc_adisc_s *adisc; + + bfa_trc(port->fcs, rx_fchs->s_id); + bfa_trc(port->fcs, rx_fchs->d_id); + + rport->stats.adisc_rcvd++; + + if (BFA_FCS_VPORT_IS_TARGET_MODE(port)) { + /* + * @todo : Target Mode handling + */ + bfa_trc(port->fcs, rx_fchs->d_id); + bfa_assert(0); + return; + } + + adisc = (struct fc_adisc_s *) (rx_fchs + 1); + + /* + * Accept if the itnim for this rport is online. Else reject the ADISC + */ + if (bfa_fcs_itnim_get_online_state(rport->itnim) == BFA_STATUS_OK) { + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_adisc_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), + rx_fchs->ox_id, port->port_cfg.pwwn, + port->port_cfg.nwwn); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL, + FC_MAX_PDUSZ, 0); + } else { + rport->stats.adisc_rejected++; + bfa_fcs_rport_send_ls_rjt(rport, rx_fchs, + FC_LS_RJT_RSN_UNABLE_TO_PERF_CMD, + FC_LS_RJT_EXP_LOGIN_REQUIRED); + } + +} + +static void +bfa_fcs_rport_hal_online(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_port_s *port = rport->port; + struct bfa_rport_info_s rport_info; + + rport_info.pid = rport->pid; + rport_info.local_pid = port->pid; + rport_info.lp_tag = port->lp_tag; + rport_info.vf_id = port->fabric->vf_id; + rport_info.vf_en = port->fabric->is_vf; + rport_info.fc_class = rport->fc_cos; + rport_info.cisc = rport->cisc; + rport_info.max_frmsz = rport->maxfrsize; + bfa_rport_online(rport->bfa_rport, &rport_info); +} + +static void +bfa_fcs_rport_fc4_pause(struct bfa_fcs_rport_s *rport) +{ + if (bfa_fcs_port_is_initiator(rport->port)) + bfa_fcs_itnim_pause(rport->itnim); + + if (bfa_fcs_port_is_target(rport->port)) + bfa_fcs_tin_pause(rport->tin); +} + +static void +bfa_fcs_rport_fc4_resume(struct bfa_fcs_rport_s *rport) +{ + if (bfa_fcs_port_is_initiator(rport->port)) + bfa_fcs_itnim_resume(rport->itnim); + + if (bfa_fcs_port_is_target(rport->port)) + bfa_fcs_tin_resume(rport->tin); +} + +static struct bfa_fcs_rport_s * +bfa_fcs_rport_alloc(struct bfa_fcs_port_s *port, wwn_t pwwn, u32 rpid) +{ + struct bfa_fcs_s *fcs = port->fcs; + struct bfa_fcs_rport_s *rport; + struct bfad_rport_s *rport_drv; + + /** + * allocate rport + */ + if (bfa_fcb_rport_alloc(fcs->bfad, &rport, &rport_drv) + != BFA_STATUS_OK) { + bfa_trc(fcs, rpid); + return NULL; + } + + /* + * Initialize r-port + */ + rport->port = port; + rport->fcs = fcs; + rport->rp_drv = rport_drv; + rport->pid = rpid; + rport->pwwn = pwwn; + + /** + * allocate BFA rport + */ + rport->bfa_rport = bfa_rport_create(port->fcs->bfa, rport); + if (!rport->bfa_rport) { + bfa_trc(fcs, rpid); + kfree(rport_drv); + return NULL; + } + + /** + * allocate FC-4s + */ + bfa_assert(bfa_fcs_port_is_initiator(port) ^ + bfa_fcs_port_is_target(port)); + + if (bfa_fcs_port_is_initiator(port)) { + rport->itnim = bfa_fcs_itnim_create(rport); + if (!rport->itnim) { + bfa_trc(fcs, rpid); + bfa_rport_delete(rport->bfa_rport); + kfree(rport_drv); + return NULL; + } + } + + if (bfa_fcs_port_is_target(port)) { + rport->tin = bfa_fcs_tin_create(rport); + if (!rport->tin) { + bfa_trc(fcs, rpid); + bfa_rport_delete(rport->bfa_rport); + kfree(rport_drv); + return NULL; + } + } + + bfa_fcs_port_add_rport(port, rport); + + bfa_sm_set_state(rport, bfa_fcs_rport_sm_uninit); + + /* + * Initialize the Rport Features(RPF) Sub Module + */ + if (!BFA_FCS_PID_IS_WKA(rport->pid)) + bfa_fcs_rpf_init(rport); + + return rport; +} + + +static void +bfa_fcs_rport_free(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_port_s *port = rport->port; + + /** + * - delete FC-4s + * - delete BFA rport + * - remove from queue of rports + */ + if (bfa_fcs_port_is_initiator(port)) + bfa_fcs_itnim_delete(rport->itnim); + + if (bfa_fcs_port_is_target(port)) + bfa_fcs_tin_delete(rport->tin); + + bfa_rport_delete(rport->bfa_rport); + bfa_fcs_port_del_rport(port, rport); + kfree(rport->rp_drv); +} + +static void +bfa_fcs_rport_aen_post(struct bfa_fcs_rport_s *rport, + enum bfa_rport_aen_event event, + struct bfa_rport_aen_data_s *data) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = rport->fcs->logm; + wwn_t lpwwn = bfa_fcs_port_get_pwwn(rport->port); + wwn_t rpwwn = rport->pwwn; + char lpwwn_ptr[BFA_STRING_32]; + char rpwwn_ptr[BFA_STRING_32]; + char *prio_str[] = { "unknown", "high", "medium", "low" }; + + wwn2str(lpwwn_ptr, lpwwn); + wwn2str(rpwwn_ptr, rpwwn); + + switch (event) { + case BFA_RPORT_AEN_ONLINE: + bfa_log(logmod, BFA_AEN_RPORT_ONLINE, rpwwn_ptr, lpwwn_ptr); + break; + case BFA_RPORT_AEN_OFFLINE: + bfa_log(logmod, BFA_AEN_RPORT_OFFLINE, rpwwn_ptr, lpwwn_ptr); + break; + case BFA_RPORT_AEN_DISCONNECT: + bfa_log(logmod, BFA_AEN_RPORT_DISCONNECT, rpwwn_ptr, lpwwn_ptr); + break; + case BFA_RPORT_AEN_QOS_PRIO: + aen_data.rport.priv.qos = data->priv.qos; + bfa_log(logmod, BFA_AEN_RPORT_QOS_PRIO, + prio_str[aen_data.rport.priv.qos.qos_priority], + rpwwn_ptr, lpwwn_ptr); + break; + case BFA_RPORT_AEN_QOS_FLOWID: + aen_data.rport.priv.qos = data->priv.qos; + bfa_log(logmod, BFA_AEN_RPORT_QOS_FLOWID, + aen_data.rport.priv.qos.qos_flow_id, rpwwn_ptr, + lpwwn_ptr); + break; + default: + break; + } + + aen_data.rport.vf_id = rport->port->fabric->vf_id; + aen_data.rport.ppwwn = + bfa_fcs_port_get_pwwn(bfa_fcs_get_base_port(rport->fcs)); + aen_data.rport.lpwwn = lpwwn; + aen_data.rport.rpwwn = rpwwn; +} + +static void +bfa_fcs_rport_online_action(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_port_s *port = rport->port; + + rport->stats.onlines++; + + if (bfa_fcs_port_is_initiator(port)) { + bfa_fcs_itnim_rport_online(rport->itnim); + if (!BFA_FCS_PID_IS_WKA(rport->pid)) + bfa_fcs_rpf_rport_online(rport); + }; + + if (bfa_fcs_port_is_target(port)) + bfa_fcs_tin_rport_online(rport->tin); + + /* + * Don't post events for well known addresses + */ + if (!BFA_FCS_PID_IS_WKA(rport->pid)) + bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_ONLINE, NULL); +} + +static void +bfa_fcs_rport_offline_action(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_port_s *port = rport->port; + + rport->stats.offlines++; + + /* + * Don't post events for well known addresses + */ + if (!BFA_FCS_PID_IS_WKA(rport->pid)) { + if (bfa_fcs_port_is_online(rport->port) == BFA_TRUE) { + bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_DISCONNECT, + NULL); + } else { + bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_OFFLINE, + NULL); + } + } + + if (bfa_fcs_port_is_initiator(port)) { + bfa_fcs_itnim_rport_offline(rport->itnim); + if (!BFA_FCS_PID_IS_WKA(rport->pid)) + bfa_fcs_rpf_rport_offline(rport); + } + + if (bfa_fcs_port_is_target(port)) + bfa_fcs_tin_rport_offline(rport->tin); +} + +/** + * Update rport parameters from PLOGI or PLOGI accept. + */ +static void +bfa_fcs_rport_update(struct bfa_fcs_rport_s *rport, struct fc_logi_s *plogi) +{ + struct bfa_fcs_port_s *port = rport->port; + + /** + * - port name + * - node name + */ + rport->pwwn = plogi->port_name; + rport->nwwn = plogi->node_name; + + /** + * - class of service + */ + rport->fc_cos = 0; + if (plogi->class3.class_valid) + rport->fc_cos = FC_CLASS_3; + + if (plogi->class2.class_valid) + rport->fc_cos |= FC_CLASS_2; + + /** + * - CISC + * - MAX receive frame size + */ + rport->cisc = plogi->csp.cisc; + rport->maxfrsize = bfa_os_ntohs(plogi->class3.rxsz); + + bfa_trc(port->fcs, bfa_os_ntohs(plogi->csp.bbcred)); + bfa_trc(port->fcs, port->fabric->bb_credit); + /** + * Direct Attach P2P mode : + * This is to handle a bug (233476) in IBM targets in Direct Attach + * Mode. Basically, in FLOGI Accept the target would have erroneously + * set the BB Credit to the value used in the FLOGI sent by the HBA. + * It uses the correct value (its own BB credit) in PLOGI. + */ + if ((!bfa_fcs_fabric_is_switched(port->fabric)) + && (bfa_os_ntohs(plogi->csp.bbcred) < port->fabric->bb_credit)) { + + bfa_trc(port->fcs, bfa_os_ntohs(plogi->csp.bbcred)); + bfa_trc(port->fcs, port->fabric->bb_credit); + + port->fabric->bb_credit = bfa_os_ntohs(plogi->csp.bbcred); + bfa_pport_set_tx_bbcredit(port->fcs->bfa, + port->fabric->bb_credit); + } + +} + +/** + * Called to handle LOGO received from an existing remote port. + */ +static void +bfa_fcs_rport_process_logo(struct bfa_fcs_rport_s *rport, struct fchs_s *fchs) +{ + rport->reply_oxid = fchs->ox_id; + bfa_trc(rport->fcs, rport->reply_oxid); + + rport->stats.logo_rcvd++; + bfa_sm_send_event(rport, RPSM_EVENT_LOGO_RCVD); +} + + + +/** + * fcs_rport_public FCS rport public interfaces + */ + +/** + * Called by bport/vport to create a remote port instance for a discovered + * remote device. + * + * @param[in] port - base port or vport + * @param[in] rpid - remote port ID + * + * @return None + */ +struct bfa_fcs_rport_s * +bfa_fcs_rport_create(struct bfa_fcs_port_s *port, u32 rpid) +{ + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, rpid); + rport = bfa_fcs_rport_alloc(port, WWN_NULL, rpid); + if (!rport) + return NULL; + + bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_SEND); + return rport; +} + +/** + * Called to create a rport for which only the wwn is known. + * + * @param[in] port - base port + * @param[in] rpwwn - remote port wwn + * + * @return None + */ +struct bfa_fcs_rport_s * +bfa_fcs_rport_create_by_wwn(struct bfa_fcs_port_s *port, wwn_t rpwwn) +{ + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, rpwwn); + rport = bfa_fcs_rport_alloc(port, rpwwn, 0); + if (!rport) + return NULL; + + bfa_sm_send_event(rport, RPSM_EVENT_ADDRESS_DISC); + return rport; +} + +/** + * Called by bport in private loop topology to indicate that a + * rport has been discovered and plogi has been completed. + * + * @param[in] port - base port or vport + * @param[in] rpid - remote port ID + */ +void +bfa_fcs_rport_start(struct bfa_fcs_port_s *port, struct fchs_s *fchs, + struct fc_logi_s *plogi) +{ + struct bfa_fcs_rport_s *rport; + + rport = bfa_fcs_rport_alloc(port, WWN_NULL, fchs->s_id); + if (!rport) + return; + + bfa_fcs_rport_update(rport, plogi); + + bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_COMP); +} + +/** + * Called by bport/vport to handle PLOGI received from a new remote port. + * If an existing rport does a plogi, it will be handled separately. + */ +void +bfa_fcs_rport_plogi_create(struct bfa_fcs_port_s *port, struct fchs_s *fchs, + struct fc_logi_s *plogi) +{ + struct bfa_fcs_rport_s *rport; + + rport = bfa_fcs_rport_alloc(port, plogi->port_name, fchs->s_id); + if (!rport) + return; + + bfa_fcs_rport_update(rport, plogi); + + rport->reply_oxid = fchs->ox_id; + bfa_trc(rport->fcs, rport->reply_oxid); + + rport->stats.plogi_rcvd++; + bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_RCVD); +} + +static int +wwn_compare(wwn_t wwn1, wwn_t wwn2) +{ + u8 *b1 = (u8 *) &wwn1; + u8 *b2 = (u8 *) &wwn2; + int i; + + for (i = 0; i < sizeof(wwn_t); i++) { + if (b1[i] < b2[i]) + return -1; + if (b1[i] > b2[i]) + return 1; + } + return 0; +} + +/** + * Called by bport/vport to handle PLOGI received from an existing + * remote port. + */ +void +bfa_fcs_rport_plogi(struct bfa_fcs_rport_s *rport, struct fchs_s *rx_fchs, + struct fc_logi_s *plogi) +{ + /** + * @todo Handle P2P and initiator-initiator. + */ + + bfa_fcs_rport_update(rport, plogi); + + rport->reply_oxid = rx_fchs->ox_id; + bfa_trc(rport->fcs, rport->reply_oxid); + + /** + * In Switched fabric topology, + * PLOGI to each other. If our pwwn is smaller, ignore it, + * if it is not a well known address. + * If the link topology is N2N, + * this Plogi should be accepted. + */ + if ((wwn_compare(rport->port->port_cfg.pwwn, rport->pwwn) == -1) + && (bfa_fcs_fabric_is_switched(rport->port->fabric)) + && (!BFA_FCS_PID_IS_WKA(rport->pid))) { + bfa_trc(rport->fcs, rport->pid); + return; + } + + rport->stats.plogi_rcvd++; + bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_RCVD); +} + +/** + * Called by bport/vport to delete a remote port instance. + * +* Rport delete is called under the following conditions: + * - vport is deleted + * - vf is deleted + * - explicit request from OS to delete rport (vmware) + */ +void +bfa_fcs_rport_delete(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_DELETE); +} + +/** + * Called by bport/vport to when a target goes offline. + * + */ +void +bfa_fcs_rport_offline(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_LOGO_IMP); +} + +/** + * Called by bport in n2n when a target (attached port) becomes online. + * + */ +void +bfa_fcs_rport_online(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_PLOGI_SEND); +} + +/** + * Called by bport/vport to notify SCN for the remote port + */ +void +bfa_fcs_rport_scn(struct bfa_fcs_rport_s *rport) +{ + + rport->stats.rscns++; + bfa_sm_send_event(rport, RPSM_EVENT_SCN); +} + +/** + * Called by fcpim to notify that the ITN cleanup is done. + */ +void +bfa_fcs_rport_itnim_ack(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_FC4_OFFLINE); +} + +/** + * Called by fcptm to notify that the ITN cleanup is done. + */ +void +bfa_fcs_rport_tin_ack(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_FC4_OFFLINE); +} + +/** + * This routine BFA callback for bfa_rport_online() call. + * + * param[in] cb_arg - rport struct. + * + * return + * void + * +* Special Considerations: + * + * note + */ +void +bfa_cb_rport_online(void *cbarg) +{ + + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_sm_send_event(rport, RPSM_EVENT_HCB_ONLINE); +} + +/** + * This routine BFA callback for bfa_rport_offline() call. + * + * param[in] rport - + * + * return + * void + * + * Special Considerations: + * + * note + */ +void +bfa_cb_rport_offline(void *cbarg) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_sm_send_event(rport, RPSM_EVENT_HCB_OFFLINE); +} + +/** + * This routine is a static BFA callback when there is a QoS flow_id + * change notification + * + * @param[in] rport - + * + * @return void + * + * Special Considerations: + * + * @note + */ +void +bfa_cb_rport_qos_scn_flowid(void *cbarg, + struct bfa_rport_qos_attr_s old_qos_attr, + struct bfa_rport_qos_attr_s new_qos_attr) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + struct bfa_rport_aen_data_s aen_data; + + bfa_trc(rport->fcs, rport->pwwn); + aen_data.priv.qos = new_qos_attr; + bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_QOS_FLOWID, &aen_data); +} + +/** + * This routine is a static BFA callback when there is a QoS priority + * change notification + * + * @param[in] rport - + * + * @return void + * + * Special Considerations: + * + * @note + */ +void +bfa_cb_rport_qos_scn_prio(void *cbarg, struct bfa_rport_qos_attr_s old_qos_attr, + struct bfa_rport_qos_attr_s new_qos_attr) +{ + struct bfa_fcs_rport_s *rport = (struct bfa_fcs_rport_s *)cbarg; + struct bfa_rport_aen_data_s aen_data; + + bfa_trc(rport->fcs, rport->pwwn); + aen_data.priv.qos = new_qos_attr; + bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_QOS_PRIO, &aen_data); +} + +/** + * Called to process any unsolicted frames from this remote port + */ +void +bfa_fcs_rport_logo_imp(struct bfa_fcs_rport_s *rport) +{ + bfa_sm_send_event(rport, RPSM_EVENT_LOGO_IMP); +} + +/** + * Called to process any unsolicted frames from this remote port + */ +void +bfa_fcs_rport_uf_recv(struct bfa_fcs_rport_s *rport, struct fchs_s *fchs, + u16 len) +{ + struct bfa_fcs_port_s *port = rport->port; + struct fc_els_cmd_s *els_cmd; + + bfa_trc(rport->fcs, fchs->s_id); + bfa_trc(rport->fcs, fchs->d_id); + bfa_trc(rport->fcs, fchs->type); + + if (fchs->type != FC_TYPE_ELS) + return; + + els_cmd = (struct fc_els_cmd_s *) (fchs + 1); + + bfa_trc(rport->fcs, els_cmd->els_code); + + switch (els_cmd->els_code) { + case FC_ELS_LOGO: + bfa_fcs_rport_process_logo(rport, fchs); + break; + + case FC_ELS_ADISC: + bfa_fcs_rport_process_adisc(rport, fchs, len); + break; + + case FC_ELS_PRLO: + if (bfa_fcs_port_is_initiator(port)) + bfa_fcs_fcpim_uf_recv(rport->itnim, fchs, len); + + if (bfa_fcs_port_is_target(port)) + bfa_fcs_fcptm_uf_recv(rport->tin, fchs, len); + break; + + case FC_ELS_PRLI: + bfa_fcs_rport_process_prli(rport, fchs, len); + break; + + case FC_ELS_RPSC: + bfa_fcs_rport_process_rpsc(rport, fchs, len); + break; + + default: + bfa_fcs_rport_send_ls_rjt(rport, fchs, + FC_LS_RJT_RSN_CMD_NOT_SUPP, + FC_LS_RJT_EXP_NO_ADDL_INFO); + break; + } +} + +/* + * Send a LS reject + */ +static void +bfa_fcs_rport_send_ls_rjt(struct bfa_fcs_rport_s *rport, struct fchs_s *rx_fchs, + u8 reason_code, u8 reason_code_expl) +{ + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + int len; + + bfa_trc(rport->fcs, rx_fchs->s_id); + + fcxp = bfa_fcs_fcxp_alloc(rport->fcs); + if (!fcxp) + return; + + len = fc_ls_rjt_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id, + reason_code, reason_code_expl); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, NULL, NULL, FC_MAX_PDUSZ, 0); +} + +/** + * Module initialization + */ +void +bfa_fcs_rport_modinit(struct bfa_fcs_s *fcs) +{ +} + +/** + * Module cleanup + */ +void +bfa_fcs_rport_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} + +/** + * Return state of rport. + */ +int +bfa_fcs_rport_get_state(struct bfa_fcs_rport_s *rport) +{ + return bfa_sm_to_state(rport_sm_table, rport->sm); +} + +/** + * Called by the Driver to set rport delete/ageout timeout + * + * param[in] rport timeout value in seconds. + * + * return None + */ +void +bfa_fcs_rport_set_del_timeout(u8 rport_tmo) +{ + /* + * convert to Millisecs + */ + if (rport_tmo > 0) + bfa_fcs_rport_del_timeout = rport_tmo * 1000; +} diff --git a/drivers/scsi/bfa/rport_api.c b/drivers/scsi/bfa/rport_api.c new file mode 100644 index 000000000000..3dae1774181e --- /dev/null +++ b/drivers/scsi/bfa/rport_api.c @@ -0,0 +1,180 @@ +/* + * 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. + */ +#include +#include +#include "fcs_vport.h" +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_trcmod.h" + +BFA_TRC_FILE(FCS, RPORT_API); + +/** + * rport_api.c Remote port implementation. + */ + +/** + * fcs_rport_api FCS rport API. + */ + +/** + * Direct API to add a target by port wwn. This interface is used, for + * example, by bios when target pwwn is known from boot lun configuration. + */ +bfa_status_t +bfa_fcs_rport_add(struct bfa_fcs_port_s *port, wwn_t *pwwn, + struct bfa_fcs_rport_s *rport, + struct bfad_rport_s *rport_drv) +{ + bfa_trc(port->fcs, *pwwn); + + return BFA_STATUS_OK; +} + +/** + * Direct API to remove a target and its associated resources. This + * interface is used, for example, by vmware driver to remove target + * ports from the target list for a VM. + */ +bfa_status_t +bfa_fcs_rport_remove(struct bfa_fcs_rport_s *rport_in) +{ + + struct bfa_fcs_rport_s *rport; + + bfa_trc(rport_in->fcs, rport_in->pwwn); + + rport = bfa_fcs_port_get_rport_by_pwwn(rport_in->port, rport_in->pwwn); + if (rport == NULL) { + /* + * TBD Error handling + */ + bfa_trc(rport_in->fcs, rport_in->pid); + return BFA_STATUS_UNKNOWN_RWWN; + } + + /* + * TBD if this remote port is online, send a logo + */ + return BFA_STATUS_OK; + +} + +/** + * Remote device status for display/debug. + */ +void +bfa_fcs_rport_get_attr(struct bfa_fcs_rport_s *rport, + struct bfa_rport_attr_s *rport_attr) +{ + struct bfa_rport_qos_attr_s qos_attr; + struct bfa_fcs_port_s *port = rport->port; + + bfa_os_memset(rport_attr, 0, sizeof(struct bfa_rport_attr_s)); + + rport_attr->pid = rport->pid; + rport_attr->pwwn = rport->pwwn; + rport_attr->nwwn = rport->nwwn; + rport_attr->cos_supported = rport->fc_cos; + rport_attr->df_sz = rport->maxfrsize; + rport_attr->state = bfa_fcs_rport_get_state(rport); + rport_attr->fc_cos = rport->fc_cos; + rport_attr->cisc = rport->cisc; + rport_attr->scsi_function = rport->scsi_function; + rport_attr->curr_speed = rport->rpf.rpsc_speed; + rport_attr->assigned_speed = rport->rpf.assigned_speed; + + bfa_rport_get_qos_attr(rport->bfa_rport, &qos_attr); + rport_attr->qos_attr = qos_attr; + + rport_attr->trl_enforced = BFA_FALSE; + if (bfa_pport_is_ratelim(port->fcs->bfa)) { + if ((rport->rpf.rpsc_speed == BFA_PPORT_SPEED_UNKNOWN) || + (rport->rpf.rpsc_speed < + bfa_fcs_port_get_rport_max_speed(port))) + rport_attr->trl_enforced = BFA_TRUE; + } + + /* + * TODO + * rport->symname + */ +} + +/** + * Per remote device statistics. + */ +void +bfa_fcs_rport_get_stats(struct bfa_fcs_rport_s *rport, + struct bfa_rport_stats_s *stats) +{ + *stats = rport->stats; +} + +void +bfa_fcs_rport_clear_stats(struct bfa_fcs_rport_s *rport) +{ + bfa_os_memset((char *)&rport->stats, 0, + sizeof(struct bfa_rport_stats_s)); +} + +struct bfa_fcs_rport_s * +bfa_fcs_rport_lookup(struct bfa_fcs_port_s *port, wwn_t rpwwn) +{ + struct bfa_fcs_rport_s *rport; + + rport = bfa_fcs_port_get_rport_by_pwwn(port, rpwwn); + if (rport == NULL) { + /* + * TBD Error handling + */ + } + + return rport; +} + +struct bfa_fcs_rport_s * +bfa_fcs_rport_lookup_by_nwwn(struct bfa_fcs_port_s *port, wwn_t rnwwn) +{ + struct bfa_fcs_rport_s *rport; + + rport = bfa_fcs_port_get_rport_by_nwwn(port, rnwwn); + if (rport == NULL) { + /* + * TBD Error handling + */ + } + + return rport; +} + +/* + * This API is to set the Rport's speed. Should be used when RPSC is not + * supported by the rport. + */ +void +bfa_fcs_rport_set_speed(struct bfa_fcs_rport_s *rport, + enum bfa_pport_speed speed) +{ + rport->rpf.assigned_speed = speed; + + /* Set this speed in f/w only if the RPSC speed is not available */ + if (rport->rpf.rpsc_speed == BFA_PPORT_SPEED_UNKNOWN) + bfa_rport_speed(rport->bfa_rport, speed); +} + + diff --git a/drivers/scsi/bfa/rport_ftrs.c b/drivers/scsi/bfa/rport_ftrs.c new file mode 100644 index 000000000000..8a1f59d596c1 --- /dev/null +++ b/drivers/scsi/bfa/rport_ftrs.c @@ -0,0 +1,375 @@ +/* + * 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. + */ + +/** + * rport_ftrs.c Remote port features (RPF) implementation. + */ + +#include +#include +#include "fcbuild.h" +#include "fcs_rport.h" +#include "fcs_lport.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs.h" + +BFA_TRC_FILE(FCS, RPORT_FTRS); + +#define BFA_FCS_RPF_RETRIES (3) +#define BFA_FCS_RPF_RETRY_TIMEOUT (1000) /* 1 sec (In millisecs) */ + +static void bfa_fcs_rpf_send_rpsc2(void *rport_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_rpf_rpsc2_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_rpf_timeout(void *arg); + +/** + * fcs_rport_ftrs_sm FCS rport state machine events + */ + +enum rpf_event { + RPFSM_EVENT_RPORT_OFFLINE = 1, /* Rport offline */ + RPFSM_EVENT_RPORT_ONLINE = 2, /* Rport online */ + RPFSM_EVENT_FCXP_SENT = 3, /* Frame from has been sent */ + RPFSM_EVENT_TIMEOUT = 4, /* Rport SM timeout event */ + RPFSM_EVENT_RPSC_COMP = 5, + RPFSM_EVENT_RPSC_FAIL = 6, + RPFSM_EVENT_RPSC_ERROR = 7, +}; + +static void bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); +static void bfa_fcs_rpf_sm_rpsc_sending(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); +static void bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); +static void bfa_fcs_rpf_sm_rpsc_retry(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); +static void bfa_fcs_rpf_sm_offline(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); +static void bfa_fcs_rpf_sm_online(struct bfa_fcs_rpf_s *rpf, + enum rpf_event event); + +static void +bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_RPORT_ONLINE : + if (!BFA_FCS_PID_IS_WKA(rport->pid)) { + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); + rpf->rpsc_retries = 0; + bfa_fcs_rpf_send_rpsc2(rpf, NULL); + break; + }; + + case RPFSM_EVENT_RPORT_OFFLINE : + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_rpf_sm_rpsc_sending(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc); + break; + + case RPFSM_EVENT_RPORT_OFFLINE : + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rpf->fcxp_wqe); + rpf->rpsc_retries = 0; + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_RPSC_COMP: + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online); + /* Update speed info in f/w via BFA */ + if (rpf->rpsc_speed != BFA_PPORT_SPEED_UNKNOWN) { + bfa_rport_speed(rport->bfa_rport, rpf->rpsc_speed); + } else if (rpf->assigned_speed != BFA_PPORT_SPEED_UNKNOWN) { + bfa_rport_speed(rport->bfa_rport, rpf->assigned_speed); + } + break; + + case RPFSM_EVENT_RPSC_FAIL: + /* RPSC not supported by rport */ + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online); + break; + + case RPFSM_EVENT_RPSC_ERROR: + /* need to retry...delayed a bit. */ + if (rpf->rpsc_retries++ < BFA_FCS_RPF_RETRIES) { + bfa_timer_start(rport->fcs->bfa, &rpf->timer, + bfa_fcs_rpf_timeout, rpf, + BFA_FCS_RPF_RETRY_TIMEOUT); + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_retry); + } else { + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online); + } + break; + + case RPFSM_EVENT_RPORT_OFFLINE : + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); + bfa_fcxp_discard(rpf->fcxp); + rpf->rpsc_retries = 0; + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_rpf_sm_rpsc_retry(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_TIMEOUT : + /* re-send the RPSC */ + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); + bfa_fcs_rpf_send_rpsc2(rpf, NULL); + break; + + case RPFSM_EVENT_RPORT_OFFLINE : + bfa_timer_stop(&rpf->timer); + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); + rpf->rpsc_retries = 0; + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_rpf_sm_online(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_RPORT_OFFLINE : + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline); + rpf->rpsc_retries = 0; + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_rpf_sm_offline(struct bfa_fcs_rpf_s *rpf, enum rpf_event event) +{ + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPFSM_EVENT_RPORT_ONLINE : + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending); + bfa_fcs_rpf_send_rpsc2(rpf, NULL); + break; + + case RPFSM_EVENT_RPORT_OFFLINE : + break; + + default: + bfa_assert(0); + } +} +/** + * Called when Rport is created. + */ +void bfa_fcs_rpf_init(struct bfa_fcs_rport_s *rport) +{ + struct bfa_fcs_rpf_s *rpf = &rport->rpf; + + bfa_trc(rport->fcs, rport->pid); + rpf->rport = rport; + + bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_uninit); +} + +/** + * Called when Rport becomes online + */ +void bfa_fcs_rpf_rport_online(struct bfa_fcs_rport_s *rport) +{ + bfa_trc(rport->fcs, rport->pid); + + if (__fcs_min_cfg(rport->port->fcs)) + return; + + if (bfa_fcs_fabric_is_switched(rport->port->fabric)) + bfa_sm_send_event(&rport->rpf, RPFSM_EVENT_RPORT_ONLINE); +} + +/** + * Called when Rport becomes offline + */ +void bfa_fcs_rpf_rport_offline(struct bfa_fcs_rport_s *rport) +{ + bfa_trc(rport->fcs, rport->pid); + + if (__fcs_min_cfg(rport->port->fcs)) + return; + + bfa_sm_send_event(&rport->rpf, RPFSM_EVENT_RPORT_OFFLINE); +} + +static void +bfa_fcs_rpf_timeout(void *arg) +{ + struct bfa_fcs_rpf_s *rpf = (struct bfa_fcs_rpf_s *) arg; + struct bfa_fcs_rport_s *rport = rpf->rport; + + bfa_trc(rport->fcs, rport->pid); + bfa_sm_send_event(rpf, RPFSM_EVENT_TIMEOUT); +} + +static void +bfa_fcs_rpf_send_rpsc2(void *rpf_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_rpf_s *rpf = (struct bfa_fcs_rpf_s *)rpf_cbarg; + struct bfa_fcs_rport_s *rport = rpf->rport; + struct bfa_fcs_port_s *port = rport->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(rport->fcs, rport->pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &rpf->fcxp_wqe, + bfa_fcs_rpf_send_rpsc2, rpf); + return; + } + rpf->fcxp = fcxp; + + len = fc_rpsc2_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rport->pid, + bfa_fcs_port_get_fcid(port), &rport->pid, 1); + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_rpf_rpsc2_response, + rpf, FC_MAX_PDUSZ, FC_RA_TOV); + rport->stats.rpsc_sent++; + bfa_sm_send_event(rpf, RPFSM_EVENT_FCXP_SENT); + +} + +static void +bfa_fcs_rpf_rpsc2_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg, + bfa_status_t req_status, u32 rsp_len, + u32 resid_len, struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_rpf_s *rpf = (struct bfa_fcs_rpf_s *) cbarg; + struct bfa_fcs_rport_s *rport = rpf->rport; + struct fc_ls_rjt_s *ls_rjt; + struct fc_rpsc2_acc_s *rpsc2_acc; + u16 num_ents; + + bfa_trc(rport->fcs, req_status); + + if (req_status != BFA_STATUS_OK) { + bfa_trc(rport->fcs, req_status); + if (req_status == BFA_STATUS_ETIMER) + rport->stats.rpsc_failed++; + bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR); + return; + } + + rpsc2_acc = (struct fc_rpsc2_acc_s *) BFA_FCXP_RSP_PLD(fcxp); + if (rpsc2_acc->els_cmd == FC_ELS_ACC) { + rport->stats.rpsc_accs++; + num_ents = bfa_os_ntohs(rpsc2_acc->num_pids); + bfa_trc(rport->fcs, num_ents); + if (num_ents > 0) { + bfa_assert(rpsc2_acc->port_info[0].pid != rport->pid); + bfa_trc(rport->fcs, + bfa_os_ntohs(rpsc2_acc->port_info[0].pid)); + bfa_trc(rport->fcs, + bfa_os_ntohs(rpsc2_acc->port_info[0].speed)); + bfa_trc(rport->fcs, + bfa_os_ntohs(rpsc2_acc->port_info[0].index)); + bfa_trc(rport->fcs, + rpsc2_acc->port_info[0].type); + + if (rpsc2_acc->port_info[0].speed == 0) { + bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR); + return; + } + + rpf->rpsc_speed = fc_rpsc_operspeed_to_bfa_speed( + bfa_os_ntohs(rpsc2_acc->port_info[0].speed)); + + bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_COMP); + } + } else { + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + bfa_trc(rport->fcs, ls_rjt->reason_code); + bfa_trc(rport->fcs, ls_rjt->reason_code_expl); + rport->stats.rpsc_rejects++; + if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) { + bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_FAIL); + } else { + bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR); + } + } +} diff --git a/drivers/scsi/bfa/scn.c b/drivers/scsi/bfa/scn.c new file mode 100644 index 000000000000..bd4771ff62c8 --- /dev/null +++ b/drivers/scsi/bfa/scn.c @@ -0,0 +1,482 @@ +/* + * 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. + */ + +#include +#include +#include "fcs_lport.h" +#include "fcs_rport.h" +#include "fcs_ms.h" +#include "fcs_trcmod.h" +#include "fcs_fcxp.h" +#include "fcs.h" +#include "lport_priv.h" + +BFA_TRC_FILE(FCS, SCN); + +#define FC_QOS_RSCN_EVENT 0x0c +#define FC_FABRIC_NAME_RSCN_EVENT 0x0d + +/* + * forward declarations + */ +static void bfa_fcs_port_scn_send_scr(void *scn_cbarg, + struct bfa_fcxp_s *fcxp_alloced); +static void bfa_fcs_port_scn_scr_response(void *fcsarg, + struct bfa_fcxp_s *fcxp, + void *cbarg, + bfa_status_t req_status, + u32 rsp_len, + u32 resid_len, + struct fchs_s *rsp_fchs); +static void bfa_fcs_port_scn_send_ls_acc(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs); +static void bfa_fcs_port_scn_timeout(void *arg); + +/** + * fcs_scm_sm FCS SCN state machine + */ + +/** + * VPort SCN State Machine events + */ +enum port_scn_event { + SCNSM_EVENT_PORT_ONLINE = 1, + SCNSM_EVENT_PORT_OFFLINE = 2, + SCNSM_EVENT_RSP_OK = 3, + SCNSM_EVENT_RSP_ERROR = 4, + SCNSM_EVENT_TIMEOUT = 5, + SCNSM_EVENT_SCR_SENT = 6, +}; + +static void bfa_fcs_port_scn_sm_offline(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event); +static void bfa_fcs_port_scn_sm_sending_scr(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event); +static void bfa_fcs_port_scn_sm_scr(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event); +static void bfa_fcs_port_scn_sm_scr_retry(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event); +static void bfa_fcs_port_scn_sm_online(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event); + +/** + * Starting state - awaiting link up. + */ +static void +bfa_fcs_port_scn_sm_offline(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event) +{ + switch (event) { + case SCNSM_EVENT_PORT_ONLINE: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_sending_scr); + bfa_fcs_port_scn_send_scr(scn, NULL); + break; + + case SCNSM_EVENT_PORT_OFFLINE: + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_scn_sm_sending_scr(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event) +{ + switch (event) { + case SCNSM_EVENT_SCR_SENT: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_scr); + break; + + case SCNSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); + bfa_fcxp_walloc_cancel(scn->port->fcs->bfa, &scn->fcxp_wqe); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_scn_sm_scr(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event) +{ + struct bfa_fcs_port_s *port = scn->port; + + switch (event) { + case SCNSM_EVENT_RSP_OK: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_online); + break; + + case SCNSM_EVENT_RSP_ERROR: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_scr_retry); + bfa_timer_start(port->fcs->bfa, &scn->timer, + bfa_fcs_port_scn_timeout, scn, + BFA_FCS_RETRY_TIMEOUT); + break; + + case SCNSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); + bfa_fcxp_discard(scn->fcxp); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_scn_sm_scr_retry(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event) +{ + switch (event) { + case SCNSM_EVENT_TIMEOUT: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_sending_scr); + bfa_fcs_port_scn_send_scr(scn, NULL); + break; + + case SCNSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); + bfa_timer_stop(&scn->timer); + break; + + default: + bfa_assert(0); + } +} + +static void +bfa_fcs_port_scn_sm_online(struct bfa_fcs_port_scn_s *scn, + enum port_scn_event event) +{ + switch (event) { + case SCNSM_EVENT_PORT_OFFLINE: + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); + break; + + default: + bfa_assert(0); + } +} + + + +/** + * fcs_scn_private FCS SCN private functions + */ + +/** + * This routine will be called to send a SCR command. + */ +static void +bfa_fcs_port_scn_send_scr(void *scn_cbarg, struct bfa_fcxp_s *fcxp_alloced) +{ + struct bfa_fcs_port_scn_s *scn = scn_cbarg; + struct bfa_fcs_port_s *port = scn->port; + struct fchs_s fchs; + int len; + struct bfa_fcxp_s *fcxp; + + bfa_trc(port->fcs, port->pid); + bfa_trc(port->fcs, port->port_cfg.pwwn); + + fcxp = fcxp_alloced ? fcxp_alloced : bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) { + bfa_fcxp_alloc_wait(port->fcs->bfa, &scn->fcxp_wqe, + bfa_fcs_port_scn_send_scr, scn); + return; + } + scn->fcxp = fcxp; + + /* + * Handle VU registrations for Base port only + */ + if ((!port->vport) && bfa_ioc_get_fcmode(&port->fcs->bfa->ioc)) { + len = fc_scr_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), + bfa_lps_is_brcd_fabric(port->fabric->lps), + port->pid, 0); + } else { + len = fc_scr_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), BFA_FALSE, + port->pid, 0); + } + + bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE, + FC_CLASS_3, len, &fchs, bfa_fcs_port_scn_scr_response, + (void *)scn, FC_MAX_PDUSZ, FC_RA_TOV); + + bfa_sm_send_event(scn, SCNSM_EVENT_SCR_SENT); +} + +static void +bfa_fcs_port_scn_scr_response(void *fcsarg, struct bfa_fcxp_s *fcxp, + void *cbarg, bfa_status_t req_status, + u32 rsp_len, u32 resid_len, + struct fchs_s *rsp_fchs) +{ + struct bfa_fcs_port_scn_s *scn = (struct bfa_fcs_port_scn_s *)cbarg; + struct bfa_fcs_port_s *port = scn->port; + struct fc_els_cmd_s *els_cmd; + struct fc_ls_rjt_s *ls_rjt; + + bfa_trc(port->fcs, port->port_cfg.pwwn); + + /* + * Sanity Checks + */ + if (req_status != BFA_STATUS_OK) { + bfa_trc(port->fcs, req_status); + bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR); + return; + } + + els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp); + + switch (els_cmd->els_code) { + + case FC_ELS_ACC: + bfa_sm_send_event(scn, SCNSM_EVENT_RSP_OK); + break; + + case FC_ELS_LS_RJT: + + ls_rjt = (struct fc_ls_rjt_s *) BFA_FCXP_RSP_PLD(fcxp); + + bfa_trc(port->fcs, ls_rjt->reason_code); + bfa_trc(port->fcs, ls_rjt->reason_code_expl); + + bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR); + break; + + default: + bfa_sm_send_event(scn, SCNSM_EVENT_RSP_ERROR); + } +} + +/* + * Send a LS Accept + */ +static void +bfa_fcs_port_scn_send_ls_acc(struct bfa_fcs_port_s *port, + struct fchs_s *rx_fchs) +{ + struct fchs_s fchs; + struct bfa_fcxp_s *fcxp; + struct bfa_rport_s *bfa_rport = NULL; + int len; + + bfa_trc(port->fcs, rx_fchs->s_id); + + fcxp = bfa_fcs_fcxp_alloc(port->fcs); + if (!fcxp) + return; + + len = fc_ls_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), rx_fchs->s_id, + bfa_fcs_port_get_fcid(port), rx_fchs->ox_id); + + bfa_fcxp_send(fcxp, bfa_rport, port->fabric->vf_id, port->lp_tag, + BFA_FALSE, FC_CLASS_3, len, &fchs, NULL, NULL, + FC_MAX_PDUSZ, 0); +} + +/** + * This routine will be called by bfa_timer on timer timeouts. + * + * param[in] vport - pointer to bfa_fcs_port_t. + * param[out] vport_status - pointer to return vport status in + * + * return + * void + * +* Special Considerations: + * + * note + */ +static void +bfa_fcs_port_scn_timeout(void *arg) +{ + struct bfa_fcs_port_scn_s *scn = (struct bfa_fcs_port_scn_s *)arg; + + bfa_sm_send_event(scn, SCNSM_EVENT_TIMEOUT); +} + + + +/** + * fcs_scn_public FCS state change notification public interfaces + */ + +/* + * Functions called by port/fab + */ +void +bfa_fcs_port_scn_init(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port); + + scn->port = port; + bfa_sm_set_state(scn, bfa_fcs_port_scn_sm_offline); +} + +void +bfa_fcs_port_scn_offline(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port); + + scn->port = port; + bfa_sm_send_event(scn, SCNSM_EVENT_PORT_OFFLINE); +} + +void +bfa_fcs_port_scn_online(struct bfa_fcs_port_s *port) +{ + struct bfa_fcs_port_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port); + + scn->port = port; + bfa_sm_send_event(scn, SCNSM_EVENT_PORT_ONLINE); +} + +static void +bfa_fcs_port_scn_portid_rscn(struct bfa_fcs_port_s *port, u32 rpid) +{ + struct bfa_fcs_rport_s *rport; + + bfa_trc(port->fcs, rpid); + + /** + * If this is an unknown device, then it just came online. + * Otherwise let rport handle the RSCN event. + */ + rport = bfa_fcs_port_get_rport_by_pid(port, rpid); + if (rport == NULL) { + /* + * If min cfg mode is enabled, we donot need to + * discover any new rports. + */ + if (!__fcs_min_cfg(port->fcs)) + rport = bfa_fcs_rport_create(port, rpid); + } else { + bfa_fcs_rport_scn(rport); + } +} + +/** + * rscn format based PID comparison + */ +#define __fc_pid_match(__c0, __c1, __fmt) \ + (((__fmt) == FC_RSCN_FORMAT_FABRIC) || \ + (((__fmt) == FC_RSCN_FORMAT_DOMAIN) && \ + ((__c0)[0] == (__c1)[0])) || \ + (((__fmt) == FC_RSCN_FORMAT_AREA) && \ + ((__c0)[0] == (__c1)[0]) && \ + ((__c0)[1] == (__c1)[1]))) + +static void +bfa_fcs_port_scn_multiport_rscn(struct bfa_fcs_port_s *port, + enum fc_rscn_format format, u32 rscn_pid) +{ + struct bfa_fcs_rport_s *rport; + struct list_head *qe, *qe_next; + u8 *c0, *c1; + + bfa_trc(port->fcs, format); + bfa_trc(port->fcs, rscn_pid); + + c0 = (u8 *) &rscn_pid; + + list_for_each_safe(qe, qe_next, &port->rport_q) { + rport = (struct bfa_fcs_rport_s *)qe; + c1 = (u8 *) &rport->pid; + if (__fc_pid_match(c0, c1, format)) + bfa_fcs_rport_scn(rport); + } +} + +void +bfa_fcs_port_scn_process_rscn(struct bfa_fcs_port_s *port, struct fchs_s *fchs, + u32 len) +{ + struct fc_rscn_pl_s *rscn = (struct fc_rscn_pl_s *) (fchs + 1); + int num_entries; + u32 rscn_pid; + bfa_boolean_t nsquery = BFA_FALSE; + int i = 0; + + num_entries = + (bfa_os_ntohs(rscn->payldlen) - + sizeof(u32)) / sizeof(rscn->event[0]); + + bfa_trc(port->fcs, num_entries); + + port->stats.num_rscn++; + + bfa_fcs_port_scn_send_ls_acc(port, fchs); + + for (i = 0; i < num_entries; i++) { + rscn_pid = rscn->event[i].portid; + + bfa_trc(port->fcs, rscn->event[i].format); + bfa_trc(port->fcs, rscn_pid); + + switch (rscn->event[i].format) { + case FC_RSCN_FORMAT_PORTID: + if (rscn->event[i].qualifier == FC_QOS_RSCN_EVENT) { + /* + * Ignore this event. f/w would have processed + * it + */ + bfa_trc(port->fcs, rscn_pid); + } else { + port->stats.num_portid_rscn++; + bfa_fcs_port_scn_portid_rscn(port, rscn_pid); + } + break; + + case FC_RSCN_FORMAT_FABRIC: + if (rscn->event[i].qualifier == + FC_FABRIC_NAME_RSCN_EVENT) { + bfa_fcs_port_ms_fabric_rscn(port); + break; + } + /* + * !!!!!!!!! Fall Through !!!!!!!!!!!!! + */ + + case FC_RSCN_FORMAT_AREA: + case FC_RSCN_FORMAT_DOMAIN: + nsquery = BFA_TRUE; + bfa_fcs_port_scn_multiport_rscn(port, + rscn->event[i].format, + rscn_pid); + break; + + default: + bfa_assert(0); + nsquery = BFA_TRUE; + } + } + + /** + * If any of area, domain or fabric RSCN is received, do a fresh discovery + * to find new devices. + */ + if (nsquery) + bfa_fcs_port_ns_query(port); +} + + diff --git a/drivers/scsi/bfa/vfapi.c b/drivers/scsi/bfa/vfapi.c new file mode 100644 index 000000000000..31d81fe2fc48 --- /dev/null +++ b/drivers/scsi/bfa/vfapi.c @@ -0,0 +1,292 @@ +/* + * 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. + */ + +/** + * vfapi.c Fabric module implementation. + */ + +#include "fcs_fabric.h" +#include "fcs_trcmod.h" + +BFA_TRC_FILE(FCS, VFAPI); + +/** + * fcs_vf_api virtual fabrics API + */ + +/** + * Enable VF mode. + * + * @param[in] fcs fcs module instance + * @param[in] vf_id default vf_id of port, FC_VF_ID_NULL + * to use standard default vf_id of 1. + * + * @retval BFA_STATUS_OK vf mode is enabled + * @retval BFA_STATUS_BUSY Port is active. Port must be disabled + * before VF mode can be enabled. + */ +bfa_status_t +bfa_fcs_vf_mode_enable(struct bfa_fcs_s *fcs, u16 vf_id) +{ + return BFA_STATUS_OK; +} + +/** + * Disable VF mode. + * + * @param[in] fcs fcs module instance + * + * @retval BFA_STATUS_OK vf mode is disabled + * @retval BFA_STATUS_BUSY VFs are present and being used. All + * VFs must be deleted before disabling + * VF mode. + */ +bfa_status_t +bfa_fcs_vf_mode_disable(struct bfa_fcs_s *fcs) +{ + return BFA_STATUS_OK; +} + +/** + * Create a new VF instance. + * + * A new VF is created using the given VF configuration. A VF is identified + * by VF id. No duplicate VF creation is allowed with the same VF id. Once + * a VF is created, VF is automatically started after link initialization + * and EVFP exchange is completed. + * + * param[in] vf - FCS vf data structure. Memory is + * allocated by caller (driver) + * param[in] fcs - FCS module + * param[in] vf_cfg - VF configuration + * param[in] vf_drv - Opaque handle back to the driver's + * virtual vf structure + * + * retval BFA_STATUS_OK VF creation is successful + * retval BFA_STATUS_FAILED VF creation failed + * retval BFA_STATUS_EEXIST A VF exists with the given vf_id + */ +bfa_status_t +bfa_fcs_vf_create(bfa_fcs_vf_t *vf, struct bfa_fcs_s *fcs, u16 vf_id, + struct bfa_port_cfg_s *port_cfg, struct bfad_vf_s *vf_drv) +{ + bfa_trc(fcs, vf_id); + return BFA_STATUS_OK; +} + +/** + * Use this function to delete a BFA VF object. VF object should + * be stopped before this function call. + * + * param[in] vf - pointer to bfa_vf_t. + * + * retval BFA_STATUS_OK On vf deletion success + * retval BFA_STATUS_BUSY VF is not in a stopped state + * retval BFA_STATUS_INPROGRESS VF deletion in in progress + */ +bfa_status_t +bfa_fcs_vf_delete(bfa_fcs_vf_t *vf) +{ + bfa_trc(vf->fcs, vf->vf_id); + return BFA_STATUS_OK; +} + +/** + * Start participation in VF. This triggers login to the virtual fabric. + * + * param[in] vf - pointer to bfa_vf_t. + * + * return None + */ +void +bfa_fcs_vf_start(bfa_fcs_vf_t *vf) +{ + bfa_trc(vf->fcs, vf->vf_id); +} + +/** + * Logout with the virtual fabric. + * + * param[in] vf - pointer to bfa_vf_t. + * + * retval BFA_STATUS_OK On success. + * retval BFA_STATUS_INPROGRESS VF is being stopped. + */ +bfa_status_t +bfa_fcs_vf_stop(bfa_fcs_vf_t *vf) +{ + bfa_trc(vf->fcs, vf->vf_id); + return BFA_STATUS_OK; +} + +/** + * Returns attributes of the given VF. + * + * param[in] vf pointer to bfa_vf_t. + * param[out] vf_attr vf attributes returned + * + * return None + */ +void +bfa_fcs_vf_get_attr(bfa_fcs_vf_t *vf, struct bfa_vf_attr_s *vf_attr) +{ + bfa_trc(vf->fcs, vf->vf_id); +} + +/** + * Return statistics associated with the given vf. + * + * param[in] vf pointer to bfa_vf_t. + * param[out] vf_stats vf statistics returned + * + * @return None + */ +void +bfa_fcs_vf_get_stats(bfa_fcs_vf_t *vf, struct bfa_vf_stats_s *vf_stats) +{ + bfa_os_memcpy(vf_stats, &vf->stats, sizeof(struct bfa_vf_stats_s)); + return; +} + +void +/** + * clear statistics associated with the given vf. + * + * param[in] vf pointer to bfa_vf_t. + * + * @return None + */ +bfa_fcs_vf_clear_stats(bfa_fcs_vf_t *vf) +{ + bfa_os_memset(&vf->stats, 0, sizeof(struct bfa_vf_stats_s)); + return; +} + +/** + * Returns FCS vf structure for a given vf_id. + * + * param[in] vf_id - VF_ID + * + * return + * If lookup succeeds, retuns fcs vf object, otherwise returns NULL + */ +bfa_fcs_vf_t * +bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id) +{ + bfa_trc(fcs, vf_id); + if (vf_id == FC_VF_ID_NULL) + return (&fcs->fabric); + + /** + * @todo vf support + */ + + return NULL; +} + +/** + * Returns driver VF structure for a given FCS vf. + * + * param[in] vf - pointer to bfa_vf_t + * + * return Driver VF structure + */ +struct bfad_vf_s * +bfa_fcs_vf_get_drv_vf(bfa_fcs_vf_t *vf) +{ + bfa_assert(vf); + bfa_trc(vf->fcs, vf->vf_id); + return vf->vf_drv; +} + +/** + * Return the list of VFs configured. + * + * param[in] fcs fcs module instance + * param[out] vf_ids returned list of vf_ids + * param[in,out] nvfs in:size of vf_ids array, + * out:total elements present, + * actual elements returned is limited by the size + * + * return Driver VF structure + */ +void +bfa_fcs_vf_list(struct bfa_fcs_s *fcs, u16 *vf_ids, int *nvfs) +{ + bfa_trc(fcs, *nvfs); +} + +/** + * Return the list of all VFs visible from fabric. + * + * param[in] fcs fcs module instance + * param[out] vf_ids returned list of vf_ids + * param[in,out] nvfs in:size of vf_ids array, + * out:total elements present, + * actual elements returned is limited by the size + * + * return Driver VF structure + */ +void +bfa_fcs_vf_list_all(struct bfa_fcs_s *fcs, u16 *vf_ids, int *nvfs) +{ + bfa_trc(fcs, *nvfs); +} + +/** + * Return the list of local logical ports present in the given VF. + * + * param[in] vf vf for which logical ports are returned + * param[out] lpwwn returned logical port wwn list + * param[in,out] nlports in:size of lpwwn list; + * out:total elements present, + * actual elements returned is limited by the size + * + */ +void +bfa_fcs_vf_get_ports(bfa_fcs_vf_t *vf, wwn_t lpwwn[], int *nlports) +{ + struct list_head *qe; + struct bfa_fcs_vport_s *vport; + int i; + struct bfa_fcs_s *fcs; + + if (vf == NULL || lpwwn == NULL || *nlports == 0) + return; + + fcs = vf->fcs; + + bfa_trc(fcs, vf->vf_id); + bfa_trc(fcs, (u32) *nlports); + + i = 0; + lpwwn[i++] = vf->bport.port_cfg.pwwn; + + list_for_each(qe, &vf->vport_q) { + if (i >= *nlports) + break; + + vport = (struct bfa_fcs_vport_s *) qe; + lpwwn[i++] = vport->lport.port_cfg.pwwn; + } + + bfa_trc(fcs, i); + *nlports = i; + return; +} + + diff --git a/drivers/scsi/bfa/vport.c b/drivers/scsi/bfa/vport.c new file mode 100644 index 000000000000..c10af06c5714 --- /dev/null +++ b/drivers/scsi/bfa/vport.c @@ -0,0 +1,891 @@ +/* + * 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. + */ + +/** + * bfa_fcs_vport.c FCS virtual port state machine + */ + +#include +#include +#include +#include "fcs_fabric.h" +#include "fcs_lport.h" +#include "fcs_vport.h" +#include "fcs_trcmod.h" +#include "fcs.h" +#include + +BFA_TRC_FILE(FCS, VPORT); + +#define __vport_fcs(__vp) (__vp)->lport.fcs +#define __vport_pwwn(__vp) (__vp)->lport.port_cfg.pwwn +#define __vport_nwwn(__vp) (__vp)->lport.port_cfg.nwwn +#define __vport_bfa(__vp) (__vp)->lport.fcs->bfa +#define __vport_fcid(__vp) (__vp)->lport.pid +#define __vport_fabric(__vp) (__vp)->lport.fabric +#define __vport_vfid(__vp) (__vp)->lport.fabric->vf_id + +#define BFA_FCS_VPORT_MAX_RETRIES 5 +/* + * Forward declarations + */ +static void bfa_fcs_vport_do_fdisc(struct bfa_fcs_vport_s *vport); +static void bfa_fcs_vport_timeout(void *vport_arg); +static void bfa_fcs_vport_do_logo(struct bfa_fcs_vport_s *vport); +static void bfa_fcs_vport_free(struct bfa_fcs_vport_s *vport); + +/** + * fcs_vport_sm FCS virtual port state machine + */ + +/** + * VPort State Machine events + */ +enum bfa_fcs_vport_event { + BFA_FCS_VPORT_SM_CREATE = 1, /* vport create event */ + BFA_FCS_VPORT_SM_DELETE = 2, /* vport delete event */ + BFA_FCS_VPORT_SM_START = 3, /* vport start request */ + BFA_FCS_VPORT_SM_STOP = 4, /* stop: unsupported */ + BFA_FCS_VPORT_SM_ONLINE = 5, /* fabric online */ + BFA_FCS_VPORT_SM_OFFLINE = 6, /* fabric offline event */ + BFA_FCS_VPORT_SM_FRMSENT = 7, /* fdisc/logo sent events */ + BFA_FCS_VPORT_SM_RSP_OK = 8, /* good response */ + BFA_FCS_VPORT_SM_RSP_ERROR = 9, /* error/bad response */ + BFA_FCS_VPORT_SM_TIMEOUT = 10, /* delay timer event */ + BFA_FCS_VPORT_SM_DELCOMP = 11, /* lport delete completion */ + BFA_FCS_VPORT_SM_RSP_DUP_WWN = 12, /* Dup wnn error */ + BFA_FCS_VPORT_SM_RSP_FAILED = 13, /* non-retryable failure */ +}; + +static void bfa_fcs_vport_sm_uninit(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_created(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_offline(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_fdisc(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_fdisc_retry(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_online(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_deleting(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_cleanup(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_logo(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); +static void bfa_fcs_vport_sm_error(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event); + +static struct bfa_sm_table_s vport_sm_table[] = { + {BFA_SM(bfa_fcs_vport_sm_uninit), BFA_FCS_VPORT_UNINIT}, + {BFA_SM(bfa_fcs_vport_sm_created), BFA_FCS_VPORT_CREATED}, + {BFA_SM(bfa_fcs_vport_sm_offline), BFA_FCS_VPORT_OFFLINE}, + {BFA_SM(bfa_fcs_vport_sm_fdisc), BFA_FCS_VPORT_FDISC}, + {BFA_SM(bfa_fcs_vport_sm_fdisc_retry), BFA_FCS_VPORT_FDISC_RETRY}, + {BFA_SM(bfa_fcs_vport_sm_online), BFA_FCS_VPORT_ONLINE}, + {BFA_SM(bfa_fcs_vport_sm_deleting), BFA_FCS_VPORT_DELETING}, + {BFA_SM(bfa_fcs_vport_sm_cleanup), BFA_FCS_VPORT_CLEANUP}, + {BFA_SM(bfa_fcs_vport_sm_logo), BFA_FCS_VPORT_LOGO}, + {BFA_SM(bfa_fcs_vport_sm_error), BFA_FCS_VPORT_ERROR} +}; + +/** + * Beginning state. + */ +static void +bfa_fcs_vport_sm_uninit(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_CREATE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_created); + bfa_fcs_fabric_addvport(__vport_fabric(vport), vport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Created state - a start event is required to start up the state machine. + */ +static void +bfa_fcs_vport_sm_created(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_START: + if (bfa_fcs_fabric_is_online(__vport_fabric(vport)) + && bfa_fcs_fabric_npiv_capable(__vport_fabric(vport))) { + bfa_sm_set_state(vport, bfa_fcs_vport_sm_fdisc); + bfa_fcs_vport_do_fdisc(vport); + } else { + /** + * Fabric is offline or not NPIV capable, stay in + * offline state. + */ + vport->vport_stats.fab_no_npiv++; + bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline); + } + break; + + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup); + bfa_fcs_port_delete(&vport->lport); + break; + + case BFA_FCS_VPORT_SM_ONLINE: + case BFA_FCS_VPORT_SM_OFFLINE: + /** + * Ignore ONLINE/OFFLINE events from fabric till vport is started. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * Offline state - awaiting ONLINE event from fabric SM. + */ +static void +bfa_fcs_vport_sm_offline(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup); + bfa_fcs_port_delete(&vport->lport); + break; + + case BFA_FCS_VPORT_SM_ONLINE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_fdisc); + vport->fdisc_retries = 0; + bfa_fcs_vport_do_fdisc(vport); + break; + + case BFA_FCS_VPORT_SM_OFFLINE: + /* + * This can happen if the vport couldn't be initialzied due + * the fact that the npiv was not enabled on the switch. In + * that case we will put the vport in offline state. However, + * the link can go down and cause the this event to be sent when + * we are already offline. Ignore it. + */ + break; + + default: + bfa_assert(0); + } +} + +/** + * FDISC is sent and awaiting reply from fabric. + */ +static void +bfa_fcs_vport_sm_fdisc(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_logo); + bfa_lps_discard(vport->lps); + bfa_fcs_vport_do_logo(vport); + break; + + case BFA_FCS_VPORT_SM_OFFLINE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline); + bfa_lps_discard(vport->lps); + break; + + case BFA_FCS_VPORT_SM_RSP_OK: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_online); + bfa_fcs_port_online(&vport->lport); + break; + + case BFA_FCS_VPORT_SM_RSP_ERROR: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_fdisc_retry); + bfa_timer_start(__vport_bfa(vport), &vport->timer, + bfa_fcs_vport_timeout, vport, + BFA_FCS_RETRY_TIMEOUT); + break; + + case BFA_FCS_VPORT_SM_RSP_FAILED: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline); + break; + + case BFA_FCS_VPORT_SM_RSP_DUP_WWN: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_error); + break; + + default: + bfa_assert(0); + } +} + +/** + * FDISC attempt failed - a timer is active to retry FDISC. + */ +static void +bfa_fcs_vport_sm_fdisc_retry(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup); + bfa_timer_stop(&vport->timer); + bfa_fcs_port_delete(&vport->lport); + break; + + case BFA_FCS_VPORT_SM_OFFLINE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline); + bfa_timer_stop(&vport->timer); + break; + + case BFA_FCS_VPORT_SM_TIMEOUT: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_fdisc); + vport->vport_stats.fdisc_retries++; + vport->fdisc_retries++; + bfa_fcs_vport_do_fdisc(vport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Vport is online (FDISC is complete). + */ +static void +bfa_fcs_vport_sm_online(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_deleting); + bfa_fcs_port_delete(&vport->lport); + break; + + case BFA_FCS_VPORT_SM_OFFLINE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_offline); + bfa_lps_discard(vport->lps); + bfa_fcs_port_offline(&vport->lport); + break; + + default: + bfa_assert(0); + } +} + +/** + * Vport is being deleted - awaiting lport delete completion to send + * LOGO to fabric. + */ +static void +bfa_fcs_vport_sm_deleting(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + break; + + case BFA_FCS_VPORT_SM_DELCOMP: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_logo); + bfa_fcs_vport_do_logo(vport); + break; + + case BFA_FCS_VPORT_SM_OFFLINE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_cleanup); + break; + + default: + bfa_assert(0); + } +} + +/** + * Error State. + * This state will be set when the Vport Creation fails due to errors like + * Dup WWN. In this state only operation allowed is a Vport Delete. + */ +static void +bfa_fcs_vport_sm_error(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELETE: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_uninit); + bfa_fcs_vport_free(vport); + break; + + default: + bfa_trc(__vport_fcs(vport), event); + } +} + +/** + * Lport cleanup is in progress since vport is being deleted. Fabric is + * offline, so no LOGO is needed to complete vport deletion. + */ +static void +bfa_fcs_vport_sm_cleanup(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_DELCOMP: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_uninit); + bfa_fcs_vport_free(vport); + break; + + case BFA_FCS_VPORT_SM_DELETE: + break; + + default: + bfa_assert(0); + } +} + +/** + * LOGO is sent to fabric. Vport delete is in progress. Lport delete cleanup + * is done. + */ +static void +bfa_fcs_vport_sm_logo(struct bfa_fcs_vport_s *vport, + enum bfa_fcs_vport_event event) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), event); + + switch (event) { + case BFA_FCS_VPORT_SM_OFFLINE: + bfa_lps_discard(vport->lps); + /* + * !!! fall through !!! + */ + + case BFA_FCS_VPORT_SM_RSP_OK: + case BFA_FCS_VPORT_SM_RSP_ERROR: + bfa_sm_set_state(vport, bfa_fcs_vport_sm_uninit); + bfa_fcs_vport_free(vport); + break; + + case BFA_FCS_VPORT_SM_DELETE: + break; + + default: + bfa_assert(0); + } +} + + + +/** + * fcs_vport_private FCS virtual port private functions + */ + +/** + * Send AEN notification + */ +static void +bfa_fcs_vport_aen_post(bfa_fcs_lport_t *port, enum bfa_lport_aen_event event) +{ + union bfa_aen_data_u aen_data; + struct bfa_log_mod_s *logmod = port->fcs->logm; + enum bfa_port_role role = port->port_cfg.roles; + wwn_t lpwwn = bfa_fcs_port_get_pwwn(port); + char lpwwn_ptr[BFA_STRING_32]; + char *role_str[BFA_PORT_ROLE_FCP_MAX / 2 + 1] = + { "Initiator", "Target", "IPFC" }; + + wwn2str(lpwwn_ptr, lpwwn); + + bfa_assert(role <= BFA_PORT_ROLE_FCP_MAX); + + switch (event) { + case BFA_LPORT_AEN_NPIV_DUP_WWN: + bfa_log(logmod, BFA_AEN_LPORT_NPIV_DUP_WWN, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_NPIV_FABRIC_MAX: + bfa_log(logmod, BFA_AEN_LPORT_NPIV_FABRIC_MAX, lpwwn_ptr, + role_str[role / 2]); + break; + case BFA_LPORT_AEN_NPIV_UNKNOWN: + bfa_log(logmod, BFA_AEN_LPORT_NPIV_UNKNOWN, lpwwn_ptr, + role_str[role / 2]); + break; + default: + break; + } + + aen_data.lport.vf_id = port->fabric->vf_id; + aen_data.lport.roles = role; + aen_data.lport.ppwwn = + bfa_fcs_port_get_pwwn(bfa_fcs_get_base_port(port->fcs)); + aen_data.lport.lpwwn = lpwwn; +} + +/** + * This routine will be called to send a FDISC command. + */ +static void +bfa_fcs_vport_do_fdisc(struct bfa_fcs_vport_s *vport) +{ + bfa_lps_fdisc(vport->lps, vport, + bfa_pport_get_maxfrsize(__vport_bfa(vport)), + __vport_pwwn(vport), __vport_nwwn(vport)); + vport->vport_stats.fdisc_sent++; +} + +static void +bfa_fcs_vport_fdisc_rejected(struct bfa_fcs_vport_s *vport) +{ + u8 lsrjt_rsn = bfa_lps_get_lsrjt_rsn(vport->lps); + u8 lsrjt_expl = bfa_lps_get_lsrjt_expl(vport->lps); + + bfa_trc(__vport_fcs(vport), lsrjt_rsn); + bfa_trc(__vport_fcs(vport), lsrjt_expl); + + /* + * For certain reason codes, we don't want to retry. + */ + switch (bfa_lps_get_lsrjt_expl(vport->lps)) { + case FC_LS_RJT_EXP_INV_PORT_NAME: /* by brocade */ + case FC_LS_RJT_EXP_INVALID_NPORT_ID: /* by Cisco */ + if (vport->fdisc_retries < BFA_FCS_VPORT_MAX_RETRIES) + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + else { + bfa_fcs_vport_aen_post(&vport->lport, + BFA_LPORT_AEN_NPIV_DUP_WWN); + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_DUP_WWN); + } + break; + + case FC_LS_RJT_EXP_INSUFF_RES: + /* + * This means max logins per port/switch setting on the + * switch was exceeded. + */ + if (vport->fdisc_retries < BFA_FCS_VPORT_MAX_RETRIES) + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + else { + bfa_fcs_vport_aen_post(&vport->lport, + BFA_LPORT_AEN_NPIV_FABRIC_MAX); + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_FAILED); + } + break; + + default: + if (vport->fdisc_retries == 0) /* Print only once */ + bfa_fcs_vport_aen_post(&vport->lport, + BFA_LPORT_AEN_NPIV_UNKNOWN); + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + } +} + +/** + * Called to send a logout to the fabric. Used when a V-Port is + * deleted/stopped. + */ +static void +bfa_fcs_vport_do_logo(struct bfa_fcs_vport_s *vport) +{ + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + + vport->vport_stats.logo_sent++; + bfa_lps_fdisclogo(vport->lps); +} + +/** + * This routine will be called by bfa_timer on timer timeouts. + * + * param[in] vport - pointer to bfa_fcs_vport_t. + * param[out] vport_status - pointer to return vport status in + * + * return + * void + * +* Special Considerations: + * + * note + */ +static void +bfa_fcs_vport_timeout(void *vport_arg) +{ + struct bfa_fcs_vport_s *vport = (struct bfa_fcs_vport_s *)vport_arg; + + vport->vport_stats.fdisc_timeouts++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_TIMEOUT); +} + +static void +bfa_fcs_vport_free(struct bfa_fcs_vport_s *vport) +{ + bfa_fcs_fabric_delvport(__vport_fabric(vport), vport); + bfa_fcb_vport_delete(vport->vport_drv); + bfa_lps_delete(vport->lps); +} + + + +/** + * fcs_vport_public FCS virtual port public interfaces + */ + +/** + * Online notification from fabric SM. + */ +void +bfa_fcs_vport_online(struct bfa_fcs_vport_s *vport) +{ + vport->vport_stats.fab_online++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_ONLINE); +} + +/** + * Offline notification from fabric SM. + */ +void +bfa_fcs_vport_offline(struct bfa_fcs_vport_s *vport) +{ + vport->vport_stats.fab_offline++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_OFFLINE); +} + +/** + * Cleanup notification from fabric SM on link timer expiry. + */ +void +bfa_fcs_vport_cleanup(struct bfa_fcs_vport_s *vport) +{ + vport->vport_stats.fab_cleanup++; +} + +/** + * Delete completion callback from associated lport + */ +void +bfa_fcs_vport_delete_comp(struct bfa_fcs_vport_s *vport) +{ + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_DELCOMP); +} + +/** + * Module initialization + */ +void +bfa_fcs_vport_modinit(struct bfa_fcs_s *fcs) +{ +} + +/** + * Module cleanup + */ +void +bfa_fcs_vport_modexit(struct bfa_fcs_s *fcs) +{ + bfa_fcs_modexit_comp(fcs); +} + +u32 +bfa_fcs_vport_get_max(struct bfa_fcs_s *fcs) +{ + struct bfa_ioc_attr_s ioc_attr; + + bfa_get_attr(fcs->bfa, &ioc_attr); + + if (ioc_attr.pci_attr.device_id == BFA_PCI_DEVICE_ID_CT) + return (BFA_FCS_MAX_VPORTS_SUPP_CT); + else + return (BFA_FCS_MAX_VPORTS_SUPP_CB); +} + + + +/** + * fcs_vport_api Virtual port API + */ + +/** + * Use this function to instantiate a new FCS vport object. This + * function will not trigger any HW initialization process (which will be + * done in vport_start() call) + * + * param[in] vport - pointer to bfa_fcs_vport_t. This space + * needs to be allocated by the driver. + * param[in] fcs - FCS instance + * param[in] vport_cfg - vport configuration + * param[in] vf_id - VF_ID if vport is created within a VF. + * FC_VF_ID_NULL to specify base fabric. + * param[in] vport_drv - Opaque handle back to the driver's vport + * structure + * + * retval BFA_STATUS_OK - on success. + * retval BFA_STATUS_FAILED - on failure. + */ +bfa_status_t +bfa_fcs_vport_create(struct bfa_fcs_vport_s *vport, struct bfa_fcs_s *fcs, + u16 vf_id, struct bfa_port_cfg_s *vport_cfg, + struct bfad_vport_s *vport_drv) +{ + if (vport_cfg->pwwn == 0) + return (BFA_STATUS_INVALID_WWN); + + if (bfa_fcs_port_get_pwwn(&fcs->fabric.bport) == vport_cfg->pwwn) + return BFA_STATUS_VPORT_WWN_BP; + + if (bfa_fcs_vport_lookup(fcs, vf_id, vport_cfg->pwwn) != NULL) + return BFA_STATUS_VPORT_EXISTS; + + if (bfa_fcs_fabric_vport_count(&fcs->fabric) == + bfa_fcs_vport_get_max(fcs)) + return BFA_STATUS_VPORT_MAX; + + vport->lps = bfa_lps_alloc(fcs->bfa); + if (!vport->lps) + return BFA_STATUS_VPORT_MAX; + + vport->vport_drv = vport_drv; + bfa_sm_set_state(vport, bfa_fcs_vport_sm_uninit); + + bfa_fcs_lport_init(&vport->lport, fcs, vf_id, vport_cfg, vport); + + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_CREATE); + + return BFA_STATUS_OK; +} + +/** + * Use this function initialize the vport. + * + * @param[in] vport - pointer to bfa_fcs_vport_t. + * + * @returns None + */ +bfa_status_t +bfa_fcs_vport_start(struct bfa_fcs_vport_s *vport) +{ + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_START); + + return BFA_STATUS_OK; +} + +/** + * Use this function quiese the vport object. This function will return + * immediately, when the vport is actually stopped, the + * bfa_drv_vport_stop_cb() will be called. + * + * param[in] vport - pointer to bfa_fcs_vport_t. + * + * return None + */ +bfa_status_t +bfa_fcs_vport_stop(struct bfa_fcs_vport_s *vport) +{ + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_STOP); + + return BFA_STATUS_OK; +} + +/** + * Use this function to delete a vport object. Fabric object should + * be stopped before this function call. + * + * param[in] vport - pointer to bfa_fcs_vport_t. + * + * return None + */ +bfa_status_t +bfa_fcs_vport_delete(struct bfa_fcs_vport_s *vport) +{ + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_DELETE); + + return BFA_STATUS_OK; +} + +/** + * Use this function to get vport's current status info. + * + * param[in] vport pointer to bfa_fcs_vport_t. + * param[out] attr pointer to return vport attributes + * + * return None + */ +void +bfa_fcs_vport_get_attr(struct bfa_fcs_vport_s *vport, + struct bfa_vport_attr_s *attr) +{ + if (vport == NULL || attr == NULL) + return; + + bfa_os_memset(attr, 0, sizeof(struct bfa_vport_attr_s)); + + bfa_fcs_port_get_attr(&vport->lport, &attr->port_attr); + attr->vport_state = bfa_sm_to_state(vport_sm_table, vport->sm); +} + +/** + * Use this function to get vport's statistics. + * + * param[in] vport pointer to bfa_fcs_vport_t. + * param[out] stats pointer to return vport statistics in + * + * return None + */ +void +bfa_fcs_vport_get_stats(struct bfa_fcs_vport_s *vport, + struct bfa_vport_stats_s *stats) +{ + *stats = vport->vport_stats; +} + +/** + * Use this function to clear vport's statistics. + * + * param[in] vport pointer to bfa_fcs_vport_t. + * + * return None + */ +void +bfa_fcs_vport_clr_stats(struct bfa_fcs_vport_s *vport) +{ + bfa_os_memset(&vport->vport_stats, 0, sizeof(struct bfa_vport_stats_s)); +} + +/** + * Lookup a virtual port. Excludes base port from lookup. + */ +struct bfa_fcs_vport_s * +bfa_fcs_vport_lookup(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t vpwwn) +{ + struct bfa_fcs_vport_s *vport; + struct bfa_fcs_fabric_s *fabric; + + bfa_trc(fcs, vf_id); + bfa_trc(fcs, vpwwn); + + fabric = bfa_fcs_vf_lookup(fcs, vf_id); + if (!fabric) { + bfa_trc(fcs, vf_id); + return NULL; + } + + vport = bfa_fcs_fabric_vport_lookup(fabric, vpwwn); + return vport; +} + +/** + * FDISC Response + */ +void +bfa_cb_lps_fdisc_comp(void *bfad, void *uarg, bfa_status_t status) +{ + struct bfa_fcs_vport_s *vport = uarg; + + bfa_trc(__vport_fcs(vport), __vport_pwwn(vport)); + bfa_trc(__vport_fcs(vport), status); + + switch (status) { + case BFA_STATUS_OK: + /* + * Initialiaze the V-Port fields + */ + __vport_fcid(vport) = bfa_lps_get_pid(vport->lps); + vport->vport_stats.fdisc_accepts++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_OK); + break; + + case BFA_STATUS_INVALID_MAC: + /* + * Only for CNA + */ + vport->vport_stats.fdisc_acc_bad++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + + break; + + case BFA_STATUS_EPROTOCOL: + switch (bfa_lps_get_extstatus(vport->lps)) { + case BFA_EPROTO_BAD_ACCEPT: + vport->vport_stats.fdisc_acc_bad++; + break; + + case BFA_EPROTO_UNKNOWN_RSP: + vport->vport_stats.fdisc_unknown_rsp++; + break; + + default: + break; + } + + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + break; + + case BFA_STATUS_FABRIC_RJT: + vport->vport_stats.fdisc_rejects++; + bfa_fcs_vport_fdisc_rejected(vport); + break; + + default: + vport->vport_stats.fdisc_rsp_err++; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_ERROR); + } +} + +/** + * LOGO response + */ +void +bfa_cb_lps_fdisclogo_comp(void *bfad, void *uarg) +{ + struct bfa_fcs_vport_s *vport = uarg; + bfa_sm_send_event(vport, BFA_FCS_VPORT_SM_RSP_OK); +} + + -- cgit v1.2.3 From 7b936b02293b2891d899233d3f4bb45295e8c1f9 Mon Sep 17 00:00:00 2001 From: "Kashyap, Desai" Date: Fri, 25 Sep 2009 11:44:41 +0530 Subject: [SCSI] mpt2sas: Update driver to MPI2 REV K headers. Drivers header are updated to the MPI2 REV K headers. Renamed VF_ID to msix_index in all call back handlers. VF_ID is removed from all request descriptor. Signed-off-by: Kashyap Desai Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpi/mpi2.h | 103 +++++++++++----- drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h | 200 ++++++++++++++++++++++++++++++- drivers/scsi/mpt2sas/mpi/mpi2_init.h | 18 ++- drivers/scsi/mpt2sas/mpi/mpi2_ioc.h | 65 ++++++++-- drivers/scsi/mpt2sas/mpi/mpi2_tool.h | 134 ++++++++++++++++++++- drivers/scsi/mpt2sas/mpt2sas_base.c | 100 +++++++--------- drivers/scsi/mpt2sas/mpt2sas_base.h | 30 +++-- drivers/scsi/mpt2sas/mpt2sas_config.c | 10 +- drivers/scsi/mpt2sas/mpt2sas_ctl.c | 35 ++++-- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 152 ++++++++++++----------- drivers/scsi/mpt2sas/mpt2sas_transport.c | 12 +- 11 files changed, 652 insertions(+), 207 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h index 7bb2ece8b2e4..f9f6c0839276 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2.h @@ -8,7 +8,7 @@ * scatter/gather formats. * Creation Date: June 21, 2006 * - * mpi2.h Version: 02.00.11 + * mpi2.h Version: 02.00.12 * * Version History * --------------- @@ -45,6 +45,13 @@ * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT. * Moved LUN field defines from mpi2_init.h. * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT. + * 05-06-09 02.00.12 Bumped MPI2_HEADER_VERSION_UNIT. + * In all request and reply descriptors, replaced VF_ID + * field with MSIxIndex field. + * Removed DevHandle field from + * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those + * bytes reserved. + * Added RAID Accelerator functionality. * -------------------------------------------------------------------------- */ @@ -70,7 +77,7 @@ #define MPI2_VERSION_02_00 (0x0200) /* versioning for this MPI header set */ -#define MPI2_HEADER_VERSION_UNIT (0x0B) +#define MPI2_HEADER_VERSION_UNIT (0x0C) #define MPI2_HEADER_VERSION_DEV (0x00) #define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) #define MPI2_HEADER_VERSION_UNIT_SHIFT (8) @@ -257,7 +264,7 @@ typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR { U8 RequestFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U16 LMID; /* 0x04 */ U16 DescriptorTypeDependent; /* 0x06 */ @@ -271,6 +278,7 @@ typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR #define MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET (0x02) #define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06) #define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08) +#define MPI2_REQ_DESCRIPT_FLAGS_RAID_ACCELERATOR (0x0A) #define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01) @@ -279,7 +287,7 @@ typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR { U8 RequestFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U16 LMID; /* 0x04 */ U16 Reserved1; /* 0x06 */ @@ -293,7 +301,7 @@ typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR { U8 RequestFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U16 LMID; /* 0x04 */ U16 DevHandle; /* 0x06 */ @@ -306,7 +314,7 @@ typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR { U8 RequestFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U16 LMID; /* 0x04 */ U16 IoIndex; /* 0x06 */ @@ -315,14 +323,29 @@ typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR Mpi2SCSITargetRequestDescriptor_t, MPI2_POINTER pMpi2SCSITargetRequestDescriptor_t; + +/* RAID Accelerator Request Descriptor */ +typedef struct _MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR { + U8 RequestFlags; /* 0x00 */ + U8 MSIxIndex; /* 0x01 */ + U16 SMID; /* 0x02 */ + U16 LMID; /* 0x04 */ + U16 Reserved; /* 0x06 */ +} MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR, + MPI2_POINTER PTR_MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR, + Mpi2RAIDAcceleratorRequestDescriptor_t, + MPI2_POINTER pMpi2RAIDAcceleratorRequestDescriptor_t; + + /* union of Request Descriptors */ typedef union _MPI2_REQUEST_DESCRIPTOR_UNION { - MPI2_DEFAULT_REQUEST_DESCRIPTOR Default; - MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority; - MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO; - MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget; - U64 Words; + MPI2_DEFAULT_REQUEST_DESCRIPTOR Default; + MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority; + MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO; + MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget; + MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR RAIDAccelerator; + U64 Words; } MPI2_REQUEST_DESCRIPTOR_UNION, MPI2_POINTER PTR_MPI2_REQUEST_DESCRIPTOR_UNION, Mpi2RequestDescriptorUnion_t, MPI2_POINTER pMpi2RequestDescriptorUnion_t; @@ -333,19 +356,20 @@ typedef union _MPI2_REQUEST_DESCRIPTOR_UNION typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR { U8 ReplyFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 DescriptorTypeDependent1; /* 0x02 */ U32 DescriptorTypeDependent2; /* 0x04 */ } MPI2_DEFAULT_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_DEFAULT_REPLY_DESCRIPTOR, Mpi2DefaultReplyDescriptor_t, MPI2_POINTER pMpi2DefaultReplyDescriptor_t; /* defines for the ReplyFlags field */ -#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F) -#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00) -#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01) -#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02) -#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03) -#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F) +#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F) +#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00) +#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01) +#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02) +#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03) +#define MPI2_RPY_DESCRIPT_FLAGS_RAID_ACCELERATOR_SUCCESS (0x05) +#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F) /* values for marking a reply descriptor as unused */ #define MPI2_RPY_DESCRIPT_UNUSED_WORD0_MARK (0xFFFFFFFF) @@ -355,7 +379,7 @@ typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR { U8 ReplyFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U32 ReplyFrameAddress; /* 0x04 */ } MPI2_ADDRESS_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_ADDRESS_REPLY_DESCRIPTOR, @@ -368,10 +392,10 @@ typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR { U8 ReplyFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U16 TaskTag; /* 0x04 */ - U16 DevHandle; /* 0x06 */ + U16 Reserved1; /* 0x06 */ } MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, Mpi2SCSIIOSuccessReplyDescriptor_t, @@ -382,7 +406,7 @@ typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR { U8 ReplyFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U16 SMID; /* 0x02 */ U8 SequenceNumber; /* 0x04 */ U8 Reserved1; /* 0x05 */ @@ -397,7 +421,7 @@ typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR { U8 ReplyFlags; /* 0x00 */ - U8 VF_ID; /* 0x01 */ + U8 MSIxIndex; /* 0x01 */ U8 VP_ID; /* 0x02 */ U8 Flags; /* 0x03 */ U16 InitiatorDevHandle; /* 0x04 */ @@ -411,15 +435,28 @@ typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR #define MPI2_RPY_DESCRIPT_TCB_FLAGS_PHYNUM_MASK (0x3F) +/* RAID Accelerator Success Reply Descriptor */ +typedef struct _MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR { + U8 ReplyFlags; /* 0x00 */ + U8 MSIxIndex; /* 0x01 */ + U16 SMID; /* 0x02 */ + U32 Reserved; /* 0x04 */ +} MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR, + MPI2_POINTER PTR_MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR, + Mpi2RAIDAcceleratorSuccessReplyDescriptor_t, + MPI2_POINTER pMpi2RAIDAcceleratorSuccessReplyDescriptor_t; + + /* union of Reply Descriptors */ typedef union _MPI2_REPLY_DESCRIPTORS_UNION { - MPI2_DEFAULT_REPLY_DESCRIPTOR Default; - MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply; - MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess; - MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess; - MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer; - U64 Words; + MPI2_DEFAULT_REPLY_DESCRIPTOR Default; + MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply; + MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess; + MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess; + MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer; + MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR RAIDAcceleratorSuccess; + U64 Words; } MPI2_REPLY_DESCRIPTORS_UNION, MPI2_POINTER PTR_MPI2_REPLY_DESCRIPTORS_UNION, Mpi2ReplyDescriptorsUnion_t, MPI2_POINTER pMpi2ReplyDescriptorsUnion_t; @@ -458,6 +495,7 @@ typedef union _MPI2_REPLY_DESCRIPTORS_UNION #define MPI2_FUNCTION_DIAG_RELEASE (0x1E) /* Diagnostic Release */ #define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) /* Target Command Buffer Post Base */ #define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) /* Target Command Buffer Post List */ +#define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C) /* RAID Accelerator*/ @@ -555,12 +593,17 @@ typedef union _MPI2_REPLY_DESCRIPTORS_UNION #define MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0) +/**************************************************************************** +* RAID Accelerator values +****************************************************************************/ + +#define MPI2_IOCSTATUS_RAID_ACCEL_ERROR (0x00B0) /**************************************************************************** * IOCStatus flag to indicate that log info is available ****************************************************************************/ -#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000) +#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000) /**************************************************************************** * IOCLogInfo Types diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h index 2f27cf6d6c65..ab47c4679640 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h @@ -6,7 +6,7 @@ * Title: MPI Configuration messages and pages * Creation Date: November 10, 2006 * - * mpi2_cnfg.h Version: 02.00.10 + * mpi2_cnfg.h Version: 02.00.11 * * Version History * --------------- @@ -95,6 +95,11 @@ * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define. * Added PortGroups, DmaGroup, and ControlGroup fields to * SAS Device Page 0. + * 05-06-09 02.00.11 Added structures and defines for IO Unit Page 5 and IO + * Unit Page 6. + * Added expander reduced functionality data to SAS + * Expander Page 0. + * Added SAS PHY Page 2 and SAS PHY Page 3. * -------------------------------------------------------------------------- */ @@ -723,6 +728,65 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3 #define MPI2_IOUNITPAGE3_GPIO_SETTING_ON (0x0001) +/* IO Unit Page 5 */ + +/* + * Upper layer code (drivers, utilities, etc.) should leave this define set to + * one and check Header.PageLength or NumDmaEngines at runtime. + */ +#ifndef MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES +#define MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_5 { + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U64 RaidAcceleratorBufferBaseAddress; /* 0x04 */ + U64 RaidAcceleratorBufferSize; /* 0x0C */ + U64 RaidAcceleratorControlBaseAddress; /* 0x14 */ + U8 RAControlSize; /* 0x1C */ + U8 NumDmaEngines; /* 0x1D */ + U8 RAMinControlSize; /* 0x1E */ + U8 RAMaxControlSize; /* 0x1F */ + U32 Reserved1; /* 0x20 */ + U32 Reserved2; /* 0x24 */ + U32 Reserved3; /* 0x28 */ + U32 DmaEngineCapabilities + [MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES]; /* 0x2C */ +} MPI2_CONFIG_PAGE_IO_UNIT_5, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_5, + Mpi2IOUnitPage5_t, MPI2_POINTER pMpi2IOUnitPage5_t; + +#define MPI2_IOUNITPAGE5_PAGEVERSION (0x00) + +/* defines for IO Unit Page 5 DmaEngineCapabilities field */ +#define MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS (0xFF00) +#define MPI2_IOUNITPAGE5_DMA_CAP_SHIFT_MAX_REQUESTS (16) + +#define MPI2_IOUNITPAGE5_DMA_CAP_EEDP (0x0008) +#define MPI2_IOUNITPAGE5_DMA_CAP_PARITY_GENERATION (0x0004) +#define MPI2_IOUNITPAGE5_DMA_CAP_HASHING (0x0002) +#define MPI2_IOUNITPAGE5_DMA_CAP_ENCRYPTION (0x0001) + + +/* IO Unit Page 6 */ + +typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 { + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U16 Flags; /* 0x04 */ + U8 RAHostControlSize; /* 0x06 */ + U8 Reserved0; /* 0x07 */ + U64 RaidAcceleratorHostControlBaseAddress; /* 0x08 */ + U32 Reserved1; /* 0x10 */ + U32 Reserved2; /* 0x14 */ + U32 Reserved3; /* 0x18 */ +} MPI2_CONFIG_PAGE_IO_UNIT_6, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_6, + Mpi2IOUnitPage6_t, MPI2_POINTER pMpi2IOUnitPage6_t; + +#define MPI2_IOUNITPAGE6_PAGEVERSION (0x00) + +/* defines for IO Unit Page 6 Flags field */ +#define MPI2_IOUNITPAGE6_FLAGS_ENABLE_RAID_ACCELERATOR (0x0001) + + /**************************************************************************** * IOC Config Pages ****************************************************************************/ @@ -1709,10 +1773,14 @@ typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0 U64 ActiveZoneManagerSASAddress;/* 0x2C */ U16 ZoneLockInactivityLimit; /* 0x34 */ U16 Reserved1; /* 0x36 */ + U8 TimeToReducedFunc; /* 0x38 */ + U8 InitialTimeToReducedFunc; /* 0x39 */ + U8 MaxReducedFuncTime; /* 0x3A */ + U8 Reserved2; /* 0x3B */ } MPI2_CONFIG_PAGE_EXPANDER_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXPANDER_0, Mpi2ExpanderPage0_t, MPI2_POINTER pMpi2ExpanderPage0_t; -#define MPI2_SASEXPANDER0_PAGEVERSION (0x05) +#define MPI2_SASEXPANDER0_PAGEVERSION (0x06) /* values for SAS Expander Page 0 DiscoveryStatus field */ #define MPI2_SAS_EXPANDER0_DS_MAX_ENCLOSURES_EXCEED (0x80000000) @@ -1737,6 +1805,7 @@ typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0 #define MPI2_SAS_EXPANDER0_DS_LOOP_DETECTED (0x00000001) /* values for SAS Expander Page 0 Flags field */ +#define MPI2_SAS_EXPANDER0_FLAGS_REDUCED_FUNCTIONALITY (0x2000) #define MPI2_SAS_EXPANDER0_FLAGS_ZONE_LOCKED (0x1000) #define MPI2_SAS_EXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800) #define MPI2_SAS_EXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400) @@ -1944,6 +2013,133 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_1 #define MPI2_SASPHY1_PAGEVERSION (0x01) +/* SAS PHY Page 2 */ + +typedef struct _MPI2_SASPHY2_PHY_EVENT { + U8 PhyEventCode; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U32 PhyEventInfo; /* 0x04 */ +} MPI2_SASPHY2_PHY_EVENT, MPI2_POINTER PTR_MPI2_SASPHY2_PHY_EVENT, + Mpi2SasPhy2PhyEvent_t, MPI2_POINTER pMpi2SasPhy2PhyEvent_t; + +/* use MPI2_SASPHY3_EVENT_CODE_ for the PhyEventCode field */ + + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.ExtPageLength or NumPhyEvents at runtime. + */ +#ifndef MPI2_SASPHY2_PHY_EVENT_MAX +#define MPI2_SASPHY2_PHY_EVENT_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_2 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U8 NumPhyEvents; /* 0x0C */ + U8 Reserved2; /* 0x0D */ + U16 Reserved3; /* 0x0E */ + MPI2_SASPHY2_PHY_EVENT PhyEvent[MPI2_SASPHY2_PHY_EVENT_MAX]; + /* 0x10 */ +} MPI2_CONFIG_PAGE_SAS_PHY_2, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_2, + Mpi2SasPhyPage2_t, MPI2_POINTER pMpi2SasPhyPage2_t; + +#define MPI2_SASPHY2_PAGEVERSION (0x00) + + +/* SAS PHY Page 3 */ + +typedef struct _MPI2_SASPHY3_PHY_EVENT_CONFIG { + U8 PhyEventCode; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U8 CounterType; /* 0x04 */ + U8 ThresholdWindow; /* 0x05 */ + U8 TimeUnits; /* 0x06 */ + U8 Reserved3; /* 0x07 */ + U32 EventThreshold; /* 0x08 */ + U16 ThresholdFlags; /* 0x0C */ + U16 Reserved4; /* 0x0E */ +} MPI2_SASPHY3_PHY_EVENT_CONFIG, MPI2_POINTER PTR_MPI2_SASPHY3_PHY_EVENT_CONFIG, + Mpi2SasPhy3PhyEventConfig_t, MPI2_POINTER pMpi2SasPhy3PhyEventConfig_t; + +/* values for PhyEventCode field */ +#define MPI2_SASPHY3_EVENT_CODE_NO_EVENT (0x00) +#define MPI2_SASPHY3_EVENT_CODE_INVALID_DWORD (0x01) +#define MPI2_SASPHY3_EVENT_CODE_RUNNING_DISPARITY_ERROR (0x02) +#define MPI2_SASPHY3_EVENT_CODE_LOSS_DWORD_SYNC (0x03) +#define MPI2_SASPHY3_EVENT_CODE_PHY_RESET_PROBLEM (0x04) +#define MPI2_SASPHY3_EVENT_CODE_ELASTICITY_BUF_OVERFLOW (0x05) +#define MPI2_SASPHY3_EVENT_CODE_RX_ERROR (0x06) +#define MPI2_SASPHY3_EVENT_CODE_RX_ADDR_FRAME_ERROR (0x20) +#define MPI2_SASPHY3_EVENT_CODE_TX_AC_OPEN_REJECT (0x21) +#define MPI2_SASPHY3_EVENT_CODE_RX_AC_OPEN_REJECT (0x22) +#define MPI2_SASPHY3_EVENT_CODE_TX_RC_OPEN_REJECT (0x23) +#define MPI2_SASPHY3_EVENT_CODE_RX_RC_OPEN_REJECT (0x24) +#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_PARTIAL_WAITING_ON (0x25) +#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_CONNECT_WAITING_ON (0x26) +#define MPI2_SASPHY3_EVENT_CODE_TX_BREAK (0x27) +#define MPI2_SASPHY3_EVENT_CODE_RX_BREAK (0x28) +#define MPI2_SASPHY3_EVENT_CODE_BREAK_TIMEOUT (0x29) +#define MPI2_SASPHY3_EVENT_CODE_CONNECTION (0x2A) +#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_PATHWAY_BLOCKED (0x2B) +#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_ARB_WAIT_TIME (0x2C) +#define MPI2_SASPHY3_EVENT_CODE_PEAK_ARB_WAIT_TIME (0x2D) +#define MPI2_SASPHY3_EVENT_CODE_PEAK_CONNECT_TIME (0x2E) +#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_FRAMES (0x40) +#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_FRAMES (0x41) +#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_ERROR_FRAMES (0x42) +#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_ERROR_FRAMES (0x43) +#define MPI2_SASPHY3_EVENT_CODE_TX_CREDIT_BLOCKED (0x44) +#define MPI2_SASPHY3_EVENT_CODE_RX_CREDIT_BLOCKED (0x45) +#define MPI2_SASPHY3_EVENT_CODE_TX_SATA_FRAMES (0x50) +#define MPI2_SASPHY3_EVENT_CODE_RX_SATA_FRAMES (0x51) +#define MPI2_SASPHY3_EVENT_CODE_SATA_OVERFLOW (0x52) +#define MPI2_SASPHY3_EVENT_CODE_TX_SMP_FRAMES (0x60) +#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_FRAMES (0x61) +#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_ERROR_FRAMES (0x63) +#define MPI2_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xD0) +#define MPI2_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xD1) +#define MPI2_SASPHY3_EVENT_CODE_RX_AIP (0xD2) + +/* values for the CounterType field */ +#define MPI2_SASPHY3_COUNTER_TYPE_WRAPPING (0x00) +#define MPI2_SASPHY3_COUNTER_TYPE_SATURATING (0x01) +#define MPI2_SASPHY3_COUNTER_TYPE_PEAK_VALUE (0x02) + +/* values for the TimeUnits field */ +#define MPI2_SASPHY3_TIME_UNITS_10_MICROSECONDS (0x00) +#define MPI2_SASPHY3_TIME_UNITS_100_MICROSECONDS (0x01) +#define MPI2_SASPHY3_TIME_UNITS_1_MILLISECOND (0x02) +#define MPI2_SASPHY3_TIME_UNITS_10_MILLISECONDS (0x03) + +/* values for the ThresholdFlags field */ +#define MPI2_SASPHY3_TFLAGS_PHY_RESET (0x0002) +#define MPI2_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.ExtPageLength or NumPhyEvents at runtime. + */ +#ifndef MPI2_SASPHY3_PHY_EVENT_MAX +#define MPI2_SASPHY3_PHY_EVENT_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 { + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U8 NumPhyEvents; /* 0x0C */ + U8 Reserved2; /* 0x0D */ + U16 Reserved3; /* 0x0E */ + MPI2_SASPHY3_PHY_EVENT_CONFIG PhyEventConfig + [MPI2_SASPHY3_PHY_EVENT_MAX]; /* 0x10 */ +} MPI2_CONFIG_PAGE_SAS_PHY_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_3, + Mpi2SasPhyPage3_t, MPI2_POINTER pMpi2SasPhyPage3_t; + +#define MPI2_SASPHY3_PAGEVERSION (0x00) + + /**************************************************************************** * SAS Port Config Pages ****************************************************************************/ diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/drivers/scsi/mpt2sas/mpi/mpi2_init.h index f1115f0f0eb2..563e56d2e945 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_init.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_init.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2008 LSI Corporation. + * Copyright (c) 2000-2009 LSI Corporation. * * * Name: mpi2_init.h * Title: MPI SCSI initiator mode messages and structures * Creation Date: June 23, 2006 * - * mpi2_init.h Version: 02.00.06 + * mpi2_init.h Version: 02.00.07 * * Version History * --------------- @@ -23,6 +23,10 @@ * Control field Task Attribute flags. * Moved LUN field defines to mpi2.h becasue they are * common to many structures. + * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to + * Query Asynchronous Event. + * Defined two new bits in the SlotStatus field of the SCSI + * Enclosure Processor Request and Reply. * -------------------------------------------------------------------------- */ @@ -289,7 +293,11 @@ typedef struct _MPI2_SCSI_TASK_MANAGE_REQUEST #define MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07) #define MPI2_SCSITASKMGMT_TASKTYPE_CLR_ACA (0x08) #define MPI2_SCSITASKMGMT_TASKTYPE_QRY_TASK_SET (0x09) -#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION (0x0A) +#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT (0x0A) + +/* obsolete TaskType name */ +#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION \ + (MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT) /* MsgFlags bits */ @@ -375,6 +383,8 @@ typedef struct _MPI2_SEP_REQUEST #define MPI2_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100) #define MPI2_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080) #define MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040) +#define MPI2_SEP_REQ_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) +#define MPI2_SEP_REQ_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) #define MPI2_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004) #define MPI2_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002) #define MPI2_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001) @@ -410,6 +420,8 @@ typedef struct _MPI2_SEP_REPLY #define MPI2_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100) #define MPI2_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080) #define MPI2_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040) +#define MPI2_SEP_REPLY_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) +#define MPI2_SEP_REPLY_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) #define MPI2_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004) #define MPI2_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002) #define MPI2_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001) diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h index 8c5d81870c03..c294128bdeb4 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h @@ -6,7 +6,7 @@ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages * Creation Date: October 11, 2006 * - * mpi2_ioc.h Version: 02.00.10 + * mpi2_ioc.h Version: 02.00.11 * * Version History * --------------- @@ -79,6 +79,11 @@ * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE * define. * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define. + * 05-06-09 02.00.11 Added MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR define. + * Added MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX define. + * Added two new reason codes for SAS Device Status Change + * Event. + * Added new event: SAS PHY Counter. * -------------------------------------------------------------------------- */ @@ -261,6 +266,8 @@ typedef struct _MPI2_IOC_FACTS_REPLY /* ProductID field uses MPI2_FW_HEADER_PID_ */ /* IOCCapabilities */ +#define MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX (0x00008000) +#define MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR (0x00004000) #define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000) #define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID (0x00001000) #define MPI2_IOCFACTS_CAPABILITY_TLR (0x00000800) @@ -440,6 +447,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY #define MPI2_EVENT_IR_PHYSICAL_DISK (0x001F) #define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020) #define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021) +#define MPI2_EVENT_SAS_PHY_COUNTER (0x0022) /* Log Entry Added Event data */ @@ -502,17 +510,19 @@ typedef struct _MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE MPI2_POINTER pMpi2EventDataSasDeviceStatusChange_t; /* SAS Device Status Change Event data ReasonCode values */ -#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05) -#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07) -#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08) -#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09) -#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A) -#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B) -#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C) -#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D) -#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E) -#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F) -#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10) +#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05) +#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07) +#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08) +#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09) +#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A) +#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B) +#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C) +#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D) +#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E) +#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F) +#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10) +#define MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY (0x11) +#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY (0x12) /* Integrated RAID Operation Status Event data */ @@ -822,6 +832,37 @@ typedef struct _MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE #define MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING (0x02) +/* SAS PHY Counter Event data */ + +typedef struct _MPI2_EVENT_DATA_SAS_PHY_COUNTER { + U64 TimeStamp; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U8 PhyEventCode; /* 0x0C */ + U8 PhyNum; /* 0x0D */ + U16 Reserved2; /* 0x0E */ + U32 PhyEventInfo; /* 0x10 */ + U8 CounterType; /* 0x14 */ + U8 ThresholdWindow; /* 0x15 */ + U8 TimeUnits; /* 0x16 */ + U8 Reserved3; /* 0x17 */ + U32 EventThreshold; /* 0x18 */ + U16 ThresholdFlags; /* 0x1C */ + U16 Reserved4; /* 0x1E */ +} MPI2_EVENT_DATA_SAS_PHY_COUNTER, + MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_PHY_COUNTER, + Mpi2EventDataSasPhyCounter_t, MPI2_POINTER pMpi2EventDataSasPhyCounter_t; + +/* use MPI2_SASPHY3_EVENT_CODE_ values from mpi2_cnfg.h for the + * PhyEventCode field + * use MPI2_SASPHY3_COUNTER_TYPE_ values from mpi2_cnfg.h for the + * CounterType field + * use MPI2_SASPHY3_TIME_UNITS_ values from mpi2_cnfg.h for the + * TimeUnits field + * use MPI2_SASPHY3_TFLAGS_ values from mpi2_cnfg.h for the + * ThresholdFlags field + * */ + + /**************************************************************************** * EventAck message ****************************************************************************/ diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h index 2ff4e936bd39..007e950f7bfa 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2008 LSI Corporation. + * Copyright (c) 2000-2009 LSI Corporation. * * * Name: mpi2_tool.h * Title: MPI diagnostic tool structures and definitions * Creation Date: March 26, 2007 * - * mpi2_tool.h Version: 02.00.02 + * mpi2_tool.h Version: 02.00.03 * * Version History * --------------- @@ -17,6 +17,7 @@ * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release * structures and defines. * 02-29-08 02.00.02 Modified various names to make them 32-character unique. + * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool. * -------------------------------------------------------------------------- */ @@ -32,7 +33,10 @@ /* defines for the Tools */ #define MPI2_TOOLBOX_CLEAN_TOOL (0x00) #define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01) +#define MPI2_TOOLBOX_ISTWI_READ_WRITE_TOOL (0x03) #define MPI2_TOOLBOX_BEACON_TOOL (0x05) +#define MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL (0x06) + /**************************************************************************** * Toolbox reply @@ -111,6 +115,77 @@ typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST Mpi2ToolboxMemMoveRequest_t, MPI2_POINTER pMpi2ToolboxMemMoveRequest_t; +/**************************************************************************** +* Toolbox ISTWI Read Write Tool +****************************************************************************/ + +/* Toolbox ISTWI Read Write Tool request message */ +typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST { + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U32 Reserved5; /* 0x0C */ + U32 Reserved6; /* 0x10 */ + U8 DevIndex; /* 0x14 */ + U8 Action; /* 0x15 */ + U8 SGLFlags; /* 0x16 */ + U8 Reserved7; /* 0x17 */ + U16 TxDataLength; /* 0x18 */ + U16 RxDataLength; /* 0x1A */ + U32 Reserved8; /* 0x1C */ + U32 Reserved9; /* 0x20 */ + U32 Reserved10; /* 0x24 */ + U32 Reserved11; /* 0x28 */ + U32 Reserved12; /* 0x2C */ + MPI2_SGE_SIMPLE_UNION SGL; /* 0x30 */ +} MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST, + MPI2_POINTER PTR_MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST, + Mpi2ToolboxIstwiReadWriteRequest_t, + MPI2_POINTER pMpi2ToolboxIstwiReadWriteRequest_t; + +/* values for the Action field */ +#define MPI2_TOOL_ISTWI_ACTION_READ_DATA (0x01) +#define MPI2_TOOL_ISTWI_ACTION_WRITE_DATA (0x02) +#define MPI2_TOOL_ISTWI_ACTION_SEQUENCE (0x03) +#define MPI2_TOOL_ISTWI_ACTION_RESERVE_BUS (0x10) +#define MPI2_TOOL_ISTWI_ACTION_RELEASE_BUS (0x11) +#define MPI2_TOOL_ISTWI_ACTION_RESET (0x12) + +/* values for SGLFlags field are in the SGL section of mpi2.h */ + + +/* Toolbox ISTWI Read Write Tool reply message */ +typedef struct _MPI2_TOOLBOX_ISTWI_REPLY { + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U8 DevIndex; /* 0x14 */ + U8 Action; /* 0x15 */ + U8 IstwiStatus; /* 0x16 */ + U8 Reserved6; /* 0x17 */ + U16 TxDataCount; /* 0x18 */ + U16 RxDataCount; /* 0x1A */ +} MPI2_TOOLBOX_ISTWI_REPLY, MPI2_POINTER PTR_MPI2_TOOLBOX_ISTWI_REPLY, + Mpi2ToolboxIstwiReply_t, MPI2_POINTER pMpi2ToolboxIstwiReply_t; + + /**************************************************************************** * Toolbox Beacon Tool request ****************************************************************************/ @@ -139,6 +214,61 @@ typedef struct _MPI2_TOOLBOX_BEACON_REQUEST #define MPI2_TOOLBOX_FLAGS_BEACONMODE_ON (0x01) +/**************************************************************************** +* Toolbox Diagnostic CLI Tool +****************************************************************************/ + +#define MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH (0x5C) + +/* Toolbox Diagnostic CLI Tool request message */ +typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST { + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U8 SGLFlags; /* 0x0C */ + U8 Reserved5; /* 0x0D */ + U16 Reserved6; /* 0x0E */ + U32 DataLength; /* 0x10 */ + U8 DiagnosticCliCommand + [MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH]; /* 0x14 */ + MPI2_SGE_SIMPLE_UNION SGL; /* 0x70 */ +} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, + MPI2_POINTER PTR_MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, + Mpi2ToolboxDiagnosticCliRequest_t, + MPI2_POINTER pMpi2ToolboxDiagnosticCliRequest_t; + +/* values for SGLFlags field are in the SGL section of mpi2.h */ + + +/* Toolbox Diagnostic CLI Tool reply message */ +typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY { + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U32 ReturnedDataLength; /* 0x14 */ +} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY, + MPI2_POINTER PTR_MPI2_TOOLBOX_DIAG_CLI_REPLY, + Mpi2ToolboxDiagnosticCliReply_t, + MPI2_POINTER pMpi2ToolboxDiagnosticCliReply_t; + + /***************************************************************************** * * Diagnostic Buffer Messages diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index d95d2f274cb3..10de370c171e 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -543,13 +543,13 @@ mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code) * _base_display_reply_info - * @ioc: pointer to scsi command object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Return nothing. */ static void -_base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, +_base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; @@ -572,13 +572,14 @@ _base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, * mpt2sas_base_done - base internal command completion routine * @ioc: pointer to scsi command object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Return nothing. */ void -mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply) { MPI2DefaultReply_t *mpi_reply; @@ -601,13 +602,13 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) /** * _base_async_event - main callback handler for firmware asyn events * @ioc: pointer to scsi command object - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Return nothing. */ static void -_base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) +_base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) { Mpi2EventNotificationReply_t *mpi_reply; Mpi2EventAckRequest_t *ack_request; @@ -635,16 +636,17 @@ _base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) ack_request->Function = MPI2_FUNCTION_EVENT_ACK; ack_request->Event = mpi_reply->Event; ack_request->EventContext = mpi_reply->EventContext; - ack_request->VF_ID = VF_ID; - mpt2sas_base_put_smid_default(ioc, smid, VF_ID); + ack_request->VF_ID = 0; /* TODO */ + ack_request->VP_ID = 0; + mpt2sas_base_put_smid_default(ioc, smid); out: /* scsih callback handler */ - mpt2sas_scsih_event_callback(ioc, VF_ID, reply); + mpt2sas_scsih_event_callback(ioc, msix_index, reply); /* ctl callback handler */ - mpt2sas_ctl_event_callback(ioc, VF_ID, reply); + mpt2sas_ctl_event_callback(ioc, msix_index, reply); } /** @@ -712,7 +714,7 @@ _base_interrupt(int irq, void *bus_id) u16 smid; u8 cb_idx; u32 reply; - u8 VF_ID; + u8 msix_index; struct MPT2SAS_ADAPTER *ioc = bus_id; Mpi2ReplyDescriptorsUnion_t *rpf; @@ -733,7 +735,7 @@ _base_interrupt(int irq, void *bus_id) reply = 0; cb_idx = 0xFF; smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1); - VF_ID = rpf->Default.VF_ID; + msix_index = rpf->Default.MSIxIndex; if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) { reply = le32_to_cpu @@ -747,14 +749,14 @@ _base_interrupt(int irq, void *bus_id) if (smid) cb_idx = ioc->scsi_lookup[smid - 1].cb_idx; if (smid && cb_idx != 0xFF) { - mpt_callbacks[cb_idx](ioc, smid, VF_ID, reply); + mpt_callbacks[cb_idx](ioc, smid, msix_index, reply); if (reply) - _base_display_reply_info(ioc, smid, VF_ID, + _base_display_reply_info(ioc, smid, msix_index, reply); mpt2sas_base_free_smid(ioc, smid); } if (!smid) - _base_async_event(ioc, VF_ID, reply); + _base_async_event(ioc, msix_index, reply); /* reply free queue handling */ if (reply) { @@ -1352,21 +1354,19 @@ static inline void _base_writeq(__u64 b, volatile void __iomem *addr, * mpt2sas_base_put_smid_scsi_io - send SCSI_IO request to firmware * @ioc: per adapter object * @smid: system request message index - * @vf_id: virtual function id * @handle: device handle * * Return nothing. */ void -mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id, - u16 handle) +mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u16 handle) { Mpi2RequestDescriptorUnion_t descriptor; u64 *request = (u64 *)&descriptor; descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; - descriptor.SCSIIO.VF_ID = vf_id; + descriptor.SCSIIO.MSIxIndex = 0; /* TODO */ descriptor.SCSIIO.SMID = cpu_to_le16(smid); descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); descriptor.SCSIIO.LMID = 0; @@ -1379,20 +1379,18 @@ mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id, * mpt2sas_base_put_smid_hi_priority - send Task Managment request to firmware * @ioc: per adapter object * @smid: system request message index - * @vf_id: virtual function id * * Return nothing. */ void -mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u8 vf_id) +mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid) { Mpi2RequestDescriptorUnion_t descriptor; u64 *request = (u64 *)&descriptor; descriptor.HighPriority.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY; - descriptor.HighPriority.VF_ID = vf_id; + descriptor.HighPriority.MSIxIndex = 0; /* TODO */ descriptor.HighPriority.SMID = cpu_to_le16(smid); descriptor.HighPriority.LMID = 0; descriptor.HighPriority.Reserved1 = 0; @@ -1404,18 +1402,17 @@ mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid, * mpt2sas_base_put_smid_default - Default, primarily used for config pages * @ioc: per adapter object * @smid: system request message index - * @vf_id: virtual function id * * Return nothing. */ void -mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id) +mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid) { Mpi2RequestDescriptorUnion_t descriptor; u64 *request = (u64 *)&descriptor; descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; - descriptor.Default.VF_ID = vf_id; + descriptor.Default.MSIxIndex = 0; /* TODO */ descriptor.Default.SMID = cpu_to_le16(smid); descriptor.Default.LMID = 0; descriptor.Default.DescriptorTypeDependent = 0; @@ -1427,21 +1424,20 @@ mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id) * mpt2sas_base_put_smid_target_assist - send Target Assist/Status to firmware * @ioc: per adapter object * @smid: system request message index - * @vf_id: virtual function id * @io_index: value used to track the IO * * Return nothing. */ void mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u8 vf_id, u16 io_index) + u16 io_index) { Mpi2RequestDescriptorUnion_t descriptor; u64 *request = (u64 *)&descriptor; descriptor.SCSITarget.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET; - descriptor.SCSITarget.VF_ID = vf_id; + descriptor.SCSITarget.MSIxIndex = 0; /* TODO */ descriptor.SCSITarget.SMID = cpu_to_le16(smid); descriptor.SCSITarget.LMID = 0; descriptor.SCSITarget.IoIndex = cpu_to_le16(io_index); @@ -2454,7 +2450,7 @@ mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc, if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) ioc->ioc_link_reset_in_progress = 1; - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || @@ -2555,7 +2551,7 @@ mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, request = mpt2sas_base_get_msg_frame(ioc, smid); ioc->base_cmds.smid = smid; memcpy(request, mpi_request, sizeof(Mpi2SepReply_t)); - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { @@ -2701,13 +2697,12 @@ _base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) /** * _base_send_ioc_init - send ioc_init to firmware * @ioc: per adapter object - * @VF_ID: virtual function id * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) +_base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { Mpi2IOCInitRequest_t mpi_request; Mpi2IOCInitReply_t mpi_reply; @@ -2719,7 +2714,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) memset(&mpi_request, 0, sizeof(Mpi2IOCInitRequest_t)); mpi_request.Function = MPI2_FUNCTION_IOC_INIT; mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER; - mpi_request.VF_ID = VF_ID; + mpi_request.VF_ID = 0; /* TODO */ + mpi_request.VP_ID = 0; mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION); mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION); @@ -2795,13 +2791,12 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) /** * _base_send_port_enable - send port_enable(discovery stuff) to firmware * @ioc: per adapter object - * @VF_ID: virtual function id * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) +_base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { Mpi2PortEnableRequest_t *mpi_request; u32 ioc_state; @@ -2829,9 +2824,10 @@ _base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) ioc->base_cmds.smid = smid; memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; - mpi_request->VF_ID = VF_ID; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; - mpt2sas_base_put_smid_default(ioc, smid, VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 300*HZ); if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { @@ -2892,13 +2888,12 @@ _base_unmask_events(struct MPT2SAS_ADAPTER *ioc, u16 event) /** * _base_event_notification - send event notification * @ioc: per adapter object - * @VF_ID: virtual function id * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_event_notification(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) +_base_event_notification(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { Mpi2EventNotificationRequest_t *mpi_request; unsigned long timeleft; @@ -2926,11 +2921,12 @@ _base_event_notification(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) ioc->base_cmds.smid = smid; memset(mpi_request, 0, sizeof(Mpi2EventNotificationRequest_t)); mpi_request->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; - mpi_request->VF_ID = VF_ID; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) mpi_request->EventMasks[i] = le32_to_cpu(ioc->event_masks[i]); - mpt2sas_base_put_smid_default(ioc, smid, VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", @@ -2981,7 +2977,7 @@ mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type) return; mutex_lock(&ioc->base_cmds.mutex); - _base_event_notification(ioc, 0, CAN_SLEEP); + _base_event_notification(ioc, CAN_SLEEP); mutex_unlock(&ioc->base_cmds.mutex); } @@ -3160,14 +3156,12 @@ _base_make_ioc_ready(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, /** * _base_make_ioc_operational - put controller in OPERATIONAL state * @ioc: per adapter object - * @VF_ID: virtual function id * @sleep_flag: CAN_SLEEP or NO_SLEEP * * Returns 0 for success, non-zero for failure. */ static int -_base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - int sleep_flag) +_base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) { int r, i; unsigned long flags; @@ -3196,7 +3190,7 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, for (i = 0; i < ioc->reply_post_queue_depth; i++) ioc->reply_post_free[i].Words = ULLONG_MAX; - r = _base_send_ioc_init(ioc, VF_ID, sleep_flag); + r = _base_send_ioc_init(ioc, sleep_flag); if (r) return r; @@ -3207,14 +3201,14 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, writel(0, &ioc->chip->ReplyPostHostIndex); _base_unmask_interrupts(ioc); - r = _base_event_notification(ioc, VF_ID, sleep_flag); + r = _base_event_notification(ioc, sleep_flag); if (r) return r; if (sleep_flag == CAN_SLEEP) _base_static_config_pages(ioc); - r = _base_send_port_enable(ioc, VF_ID, sleep_flag); + r = _base_send_port_enable(ioc, sleep_flag); if (r) return r; @@ -3338,7 +3332,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) if (r) goto out_free_resources; } - r = _base_make_ioc_operational(ioc, 0, CAN_SLEEP); + r = _base_make_ioc_operational(ioc, CAN_SLEEP); if (r) goto out_free_resources; @@ -3490,7 +3484,7 @@ int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, enum reset_type type) { - int r, i; + int r; unsigned long flags; dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, @@ -3513,9 +3507,7 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, if (r) goto out; _base_reset_handler(ioc, MPT2_IOC_AFTER_RESET); - for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) - r = _base_make_ioc_operational(ioc, ioc->pfacts[i].VF_ID, - sleep_flag); + r = _base_make_ioc_operational(ioc, sleep_flag); if (!r) _base_reset_handler(ioc, MPT2_IOC_DONE_RESET); out: diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index 2faab1e690e9..39e50e93a83c 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -701,8 +701,8 @@ struct MPT2SAS_ADAPTER { u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT]; }; -typedef void (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, - u32 reply); +typedef void (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, + u8 msix_index, u32 reply); /* base shared API */ @@ -725,17 +725,18 @@ dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 sm u16 mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx); void mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid); -void mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id, +void mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u16 handle); -void mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id); +void mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid); void mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u8 vf_id, u16 io_index); -void mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id); + u16 io_index); +void mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid); void mpt2sas_base_initialize_callback_handler(void); u8 mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func); void mpt2sas_base_release_callback_handler(u8 cb_idx); -void mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +void mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply); void *mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr); u32 mpt2sas_base_get_iocstate(struct MPT2SAS_ADAPTER *ioc, int cooked); @@ -760,11 +761,13 @@ struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAP struct _sas_device *mpt2sas_scsih_sas_device_find_by_sas_address( struct MPT2SAS_ADAPTER *ioc, u64 sas_address); -void mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply); +void mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, + u32 reply); void mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); /* config shared API */ -void mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +void mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply); int mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys); int mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page); @@ -817,14 +820,17 @@ extern struct device_attribute *mpt2sas_host_attrs[]; extern struct device_attribute *mpt2sas_dev_attrs[]; void mpt2sas_ctl_init(void); void mpt2sas_ctl_exit(void); -void mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +void mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply); void mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); -void mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply); +void mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, + u32 reply); void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, Mpi2EventNotificationReply_t *mpi_reply); /* transport shared API */ -void mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +void mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, + u8 msix_index, u32 reply); struct _sas_port *mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, u16 parent_handle); void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c index ab8c560865d8..2e018c9f5982 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_config.c +++ b/drivers/scsi/mpt2sas/mpt2sas_config.c @@ -227,7 +227,7 @@ _config_free_config_dma_memory(struct MPT2SAS_ADAPTER *ioc, * mpt2sas_config_done - config page completion routine * @ioc: per adapter object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: none. * @@ -236,7 +236,8 @@ _config_free_config_dma_memory(struct MPT2SAS_ADAPTER *ioc, * Return nothing. */ void -mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply) { MPI2DefaultReply_t *mpi_reply; @@ -303,6 +304,9 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t retry_count = 0; memset(&mem, 0, sizeof(struct config_request)); + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; + if (config_page) { mpi_request->Header.PageVersion = mpi_reply->Header.PageVersion; mpi_request->Header.PageNumber = mpi_reply->Header.PageNumber; @@ -380,7 +384,7 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t _config_display_some_debug(ioc, smid, "config_request", NULL); #endif init_completion(&ioc->config_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid, config_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->config_cmds.done, timeout*HZ); if (!(ioc->config_cmds.status & MPT2_CMD_COMPLETE)) { diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index c2a51018910f..af269268febd 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -219,7 +219,7 @@ _ctl_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, * mpt2sas_ctl_done - ctl module completion routine * @ioc: per adapter object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: none. * @@ -228,7 +228,8 @@ _ctl_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, * Return nothing. */ void -mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply) { MPI2DefaultReply_t *mpi_reply; @@ -328,7 +329,7 @@ mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, /** * mpt2sas_ctl_event_callback - firmware event handler (called at ISR time) * @ioc: per adapter object - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: interrupt. * @@ -338,7 +339,8 @@ mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, * Return nothing. */ void -mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) +mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, + u32 reply) { Mpi2EventNotificationReply_t *mpi_reply; @@ -737,7 +739,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid); priv_sense = mpt2sas_base_get_sense_buffer(ioc, smid); memset(priv_sense, 0, SCSI_SENSE_BUFFERSIZE); - mpt2sas_base_put_smid_scsi_io(ioc, smid, 0, + mpt2sas_base_put_smid_scsi_io(ioc, smid, le16_to_cpu(mpi_request->FunctionDependent1)); break; } @@ -759,8 +761,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, mutex_lock(&ioc->tm_cmds.mutex); mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu( tm_request->DevHandle)); - mpt2sas_base_put_smid_hi_priority(ioc, smid, - mpi_request->VF_ID); + mpt2sas_base_put_smid_hi_priority(ioc, smid); break; } case MPI2_FUNCTION_SMP_PASSTHROUGH: @@ -781,7 +782,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, ioc->ioc_link_reset_in_progress = 1; ioc->ignore_loginfos = 1; } - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: @@ -795,11 +796,11 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, ioc->ioc_link_reset_in_progress = 1; ioc->ignore_loginfos = 1; } - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); break; } default: - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); break; } @@ -1371,6 +1372,8 @@ _ctl_diag_register(void __user *arg, enum block_state state) mpi_request->Flags = cpu_to_le32(karg.diagnostic_flags); mpi_request->BufferAddress = cpu_to_le64(request_data_dma); mpi_request->BufferLength = cpu_to_le32(request_data_sz); + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: diag_buffer(0x%p), " "dma(0x%llx), sz(%d)\n", ioc->name, __func__, request_data, @@ -1380,7 +1383,7 @@ _ctl_diag_register(void __user *arg, enum block_state state) mpi_request->ProductSpecific[i] = cpu_to_le32(ioc->product_specific[buffer_type][i]); - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); @@ -1643,8 +1646,10 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset) mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE; mpi_request->BufferType = buffer_type; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); @@ -1902,8 +1907,10 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) mpi_request->ProductSpecific[i] = cpu_to_le32(ioc->product_specific[buffer_type][i]); + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; - mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); @@ -2069,6 +2076,7 @@ static long _ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; + lock_kernel(); ret = _ctl_ioctl_main(file, cmd, (void __user *)arg); unlock_kernel(); @@ -2143,6 +2151,7 @@ static long _ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) { long ret; + lock_kernel(); if (cmd == MPT2COMMAND32) ret = _ctl_compat_mpt_command(file, cmd, arg); diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 774b34525bba..8277fa366898 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -109,6 +109,7 @@ struct sense_info { * @work: work object (ioc->fault_reset_work_q) * @ioc: per adapter object * @VF_ID: virtual function id + * @VP_ID: virtual port id * @host_reset_handling: handling events during host reset * @ignore: flag meaning this event has been marked to ignore * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h @@ -121,6 +122,7 @@ struct fw_event_work { struct work_struct work; struct MPT2SAS_ADAPTER *ioc; u8 VF_ID; + u8 VP_ID; u8 host_reset_handling; u8 ignore; u16 event; @@ -138,8 +140,10 @@ struct fw_event_work { * @lun: lun number * @cdb_length: cdb length * @cdb: cdb contents - * @valid_reply: flag set for reply message * @timeout: timeout for this command + * @VF_ID: virtual function id + * @VP_ID: virtual port id + * @valid_reply: flag set for reply message * @sense_length: sense length * @ioc_status: ioc status * @scsi_state: scsi state @@ -161,6 +165,8 @@ struct _scsi_io_transfer { u8 cdb_length; u8 cdb[32]; u8 timeout; + u8 VF_ID; + u8 VP_ID; u8 valid_reply; /* the following bits are only valid when 'valid_reply = 1' */ u32 sense_length; @@ -1679,7 +1685,7 @@ _scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code) * _scsih_tm_done - tm completion routine * @ioc: per adapter object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: none. * @@ -1688,7 +1694,7 @@ _scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code) * Return nothing. */ static void -_scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +_scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; @@ -1790,7 +1796,6 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, u16 smid = 0; u32 ioc_state; unsigned long timeleft; - u8 VF_ID = 0; if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_INFO_FMT "%s: tm_cmd busy!!!\n", @@ -1834,10 +1839,12 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, mpi_request->DevHandle = cpu_to_le16(handle); mpi_request->TaskType = type; mpi_request->TaskMID = cpu_to_le16(smid_task); + mpi_request->VP_ID = 0; /* TODO */ + mpi_request->VF_ID = 0; int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN); mpt2sas_scsih_set_tm_flag(ioc, handle); init_completion(&ioc->tm_cmds.done); - mpt2sas_base_put_smid_hi_priority(ioc, smid, VF_ID); + mpt2sas_base_put_smid_hi_priority(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); mpt2sas_scsih_clear_tm_flag(ioc, handle); if (!(ioc->tm_cmds.status & MPT2_CMD_COMPLETE)) { @@ -2643,7 +2650,8 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4; mpi_request->SGLFlags = cpu_to_le16(MPI2_SCSIIO_SGLFLAGS_TYPE_MPI + MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR); - + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *) mpi_request->LUN); memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); @@ -2658,7 +2666,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) } _scsih_scsi_lookup_set(ioc, smid, scmd); - mpt2sas_base_put_smid_scsi_io(ioc, smid, 0, + mpt2sas_base_put_smid_scsi_io(ioc, smid, sas_device_priv_data->sas_target->handle); return 0; @@ -2954,7 +2962,7 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) * _scsih_io_done - scsi request callback * @ioc: per adapter object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Callback handler when using scsih_qcmd. @@ -2962,7 +2970,7 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) * Return nothing. */ static void -_scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +_scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { Mpi2SCSIIORequest_t *mpi_request; Mpi2SCSIIOReply_t *mpi_reply; @@ -3690,7 +3698,8 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request.Operation = MPI2_SAS_OP_REMOVE_DEVICE; mpi_request.DevHandle = handle; - mpi_request.VF_ID = 0; + mpi_request.VF_ID = 0; /* TODO */ + mpi_request.VP_ID = 0; if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request)) != 0) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", @@ -3800,15 +3809,12 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc, /** * _scsih_sas_topology_change_event - handle topology changes * @ioc: per adapter object - * @VF_ID: - * @event_data: event data payload - * fw_event: + * @fw_event: The fw_event_work object * Context: user. * */ static void -_scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataSasTopologyChangeList_t *event_data, +_scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { int i; @@ -3818,6 +3824,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, struct _sas_node *sas_expander; unsigned long flags; u8 link_rate_; + Mpi2EventDataSasTopologyChangeList_t *event_data = fw_event->event_data; #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) @@ -3971,19 +3978,19 @@ _scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc, /** * _scsih_sas_device_status_change_event - handle device status change * @ioc: per adapter object - * @VF_ID: - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataSasDeviceStatusChange_t *event_data) +_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) - _scsih_sas_device_status_change_event_debug(ioc, event_data); + _scsih_sas_device_status_change_event_debug(ioc, + fw_event->event_data); #endif } @@ -4026,34 +4033,33 @@ _scsih_sas_enclosure_dev_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc, /** * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events * @ioc: per adapter object - * @VF_ID: - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void _scsih_sas_enclosure_dev_status_change_event(struct MPT2SAS_ADAPTER *ioc, - u8 VF_ID, Mpi2EventDataSasEnclDevStatusChange_t *event_data) + struct fw_event_work *fw_event) { #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) _scsih_sas_enclosure_dev_status_change_event_debug(ioc, - event_data); + fw_event->event_data); #endif } /** * _scsih_sas_broadcast_primative_event - handle broadcast events * @ioc: per adapter object - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataSasBroadcastPrimitive_t *event_data) +_scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { struct scsi_cmnd *scmd; u16 smid, handle; @@ -4062,11 +4068,12 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 termination_count; u32 query_count; Mpi2SCSITaskManagementReply_t *mpi_reply; - +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + Mpi2EventDataSasBroadcastPrimitive_t *event_data = fw_event->event_data; +#endif dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "broadcast primative: " "phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum, event_data->PortWidth)); - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, __func__)); @@ -4121,15 +4128,17 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, /** * _scsih_sas_discovery_event - handle discovery events * @ioc: per adapter object - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataSasDiscovery_t *event_data) +_scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { + Mpi2EventDataSasDiscovery_t *event_data = fw_event->event_data; + #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) { printk(MPT2SAS_DEBUG_FMT "discovery event: (%s)", ioc->name, @@ -4488,19 +4497,19 @@ _scsih_sas_ir_config_change_event_debug(struct MPT2SAS_ADAPTER *ioc, /** * _scsih_sas_ir_config_change_event - handle ir configuration change events * @ioc: per adapter object - * @VF_ID: - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataIrConfigChangeList_t *event_data) +_scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { Mpi2EventIrConfigElement_t *element; int i; u8 foreign_config; + Mpi2EventDataIrConfigChangeList_t *event_data = fw_event->event_data; #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) @@ -4543,14 +4552,14 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, /** * _scsih_sas_ir_volume_event - IR volume event * @ioc: per adapter object - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataIrVolume_t *event_data) +_scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { u64 wwid; unsigned long flags; @@ -4559,6 +4568,7 @@ _scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 state; int rc; struct MPT2SAS_TARGET *sas_target_priv_data; + Mpi2EventDataIrVolume_t *event_data = fw_event->event_data; if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) return; @@ -4628,14 +4638,14 @@ _scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, /** * _scsih_sas_ir_physical_disk_event - PD event * @ioc: per adapter object - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataIrPhysicalDisk_t *event_data) +_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { u16 handle; u32 state; @@ -4644,6 +4654,7 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, Mpi2ConfigReply_t mpi_reply; Mpi2SasDevicePage0_t sas_device_pg0; u32 ioc_status; + Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data; if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED) return; @@ -4743,33 +4754,33 @@ _scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc, /** * _scsih_sas_ir_operation_status_event - handle RAID operation events * @ioc: per adapter object - * @VF_ID: - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Return nothing. */ static void -_scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataIrOperationStatus_t *event_data) +_scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) { #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) - _scsih_sas_ir_operation_status_event_debug(ioc, event_data); + _scsih_sas_ir_operation_status_event_debug(ioc, + fw_event->event_data); #endif } /** * _scsih_task_set_full - handle task set full * @ioc: per adapter object - * @event_data: event data payload + * @fw_event: The fw_event_work object * Context: user. * * Throttle back qdepth. */ static void -_scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, - Mpi2EventDataTaskSetFull_t *event_data) +_scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work + *fw_event) { unsigned long flags; struct _sas_device *sas_device; @@ -4780,6 +4791,7 @@ _scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u16 handle; int id, channel; u64 sas_address; + Mpi2EventDataTaskSetFull_t *event_data = fw_event->event_data; current_depth = le16_to_cpu(event_data->CurrentDepth); handle = le16_to_cpu(event_data->DevHandle); @@ -5227,44 +5239,38 @@ _firmware_event_work(struct work_struct *work) switch (fw_event->event) { case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: - _scsih_sas_topology_change_event(ioc, fw_event->VF_ID, - fw_event->event_data, fw_event); + _scsih_sas_topology_change_event(ioc, fw_event); break; case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: - _scsih_sas_device_status_change_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_device_status_change_event(ioc, + fw_event); break; case MPI2_EVENT_SAS_DISCOVERY: - _scsih_sas_discovery_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_discovery_event(ioc, + fw_event); break; case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: - _scsih_sas_broadcast_primative_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_broadcast_primative_event(ioc, + fw_event); break; case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: _scsih_sas_enclosure_dev_status_change_event(ioc, - fw_event->VF_ID, fw_event->event_data); + fw_event); break; case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: - _scsih_sas_ir_config_change_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_ir_config_change_event(ioc, fw_event); break; case MPI2_EVENT_IR_VOLUME: - _scsih_sas_ir_volume_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_ir_volume_event(ioc, fw_event); break; case MPI2_EVENT_IR_PHYSICAL_DISK: - _scsih_sas_ir_physical_disk_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_ir_physical_disk_event(ioc, fw_event); break; case MPI2_EVENT_IR_OPERATION_STATUS: - _scsih_sas_ir_operation_status_event(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_sas_ir_operation_status_event(ioc, fw_event); break; case MPI2_EVENT_TASK_SET_FULL: - _scsih_task_set_full(ioc, fw_event->VF_ID, - fw_event->event_data); + _scsih_task_set_full(ioc, fw_event); break; } _scsih_fw_event_free(ioc, fw_event); @@ -5273,7 +5279,7 @@ _firmware_event_work(struct work_struct *work) /** * mpt2sas_scsih_event_callback - firmware event handler (called at ISR time) * @ioc: per adapter object - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * Context: interrupt. * @@ -5283,7 +5289,8 @@ _firmware_event_work(struct work_struct *work) * Return nothing. */ void -mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) +mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, + u32 reply) { struct fw_event_work *fw_event; Mpi2EventNotificationReply_t *mpi_reply; @@ -5355,7 +5362,8 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) memcpy(fw_event->event_data, mpi_reply->EventData, mpi_reply->EventDataLength*4); fw_event->ioc = ioc; - fw_event->VF_ID = VF_ID; + fw_event->VF_ID = mpi_reply->VF_ID; + fw_event->VP_ID = mpi_reply->VP_ID; fw_event->event = event; _scsih_fw_event_add(ioc, fw_event); } diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c index 742324a0a11e..4b0e90ee892b 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_transport.c +++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c @@ -212,7 +212,7 @@ _transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle, * mpt2sas_transport_done - internal transport layer callback handler. * @ioc: per adapter object * @smid: system request message index - * @VF_ID: virtual function id + * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * * Callback handler when sending internal generated transport cmds. @@ -221,7 +221,7 @@ _transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle, * Return nothing. */ void -mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, +mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; @@ -369,6 +369,8 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; mpi_request->PhysicalPort = 0xFF; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; sas_address_le = (u64 *)&mpi_request->SASAddress; *sas_address_le = cpu_to_le64(sas_address); mpi_request->RequestDataLength = sizeof(struct rep_manu_request); @@ -396,7 +398,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "report_manufacture - " "send to sas_addr(0x%016llx)\n", ioc->name, (unsigned long long)sas_address)); - mpt2sas_base_put_smid_default(ioc, smid, 0 /* VF_ID */); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); @@ -1106,6 +1108,8 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; mpi_request->PhysicalPort = 0xFF; + mpi_request->VF_ID = 0; /* TODO */ + mpi_request->VP_ID = 0; *((u64 *)&mpi_request->SASAddress) = (rphy) ? cpu_to_le64(rphy->identify.sas_address) : cpu_to_le64(ioc->sas_hba.sas_address); @@ -1147,7 +1151,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - " "sending smp request\n", ioc->name, __func__)); - mpt2sas_base_put_smid_default(ioc, smid, 0 /* VF_ID */); + mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); -- cgit v1.2.3 From 57442b16e52403d64f83840bd7694ea59e8469af Mon Sep 17 00:00:00 2001 From: "Kashyap, Desai" Date: Mon, 14 Sep 2009 11:00:43 +0530 Subject: [SCSI] mpt2sas: Added mpi2_history.txt for MPI2 headers. Added new file for MPI2 header version history. Signed-off-by: Kashyap Desai Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpi/mpi2_history.txt | 334 ++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 drivers/scsi/mpt2sas/mpi/mpi2_history.txt diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_history.txt b/drivers/scsi/mpt2sas/mpi/mpi2_history.txt new file mode 100644 index 000000000000..65fcaa31cb30 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpi/mpi2_history.txt @@ -0,0 +1,334 @@ + ============================== + Fusion-MPT MPI 2.0 Header File Change History + ============================== + + Copyright (c) 2000-2009 LSI Corporation. + + --------------------------------------- + Header Set Release Version: 02.00.12 + Header Set Release Date: 05-06-09 + --------------------------------------- + + Filename Current version Prior version + ---------- --------------- ------------- + mpi2.h 02.00.12 02.00.11 + mpi2_cnfg.h 02.00.11 02.00.10 + mpi2_init.h 02.00.07 02.00.06 + mpi2_ioc.h 02.00.11 02.00.10 + mpi2_raid.h 02.00.03 02.00.03 + mpi2_sas.h 02.00.02 02.00.02 + mpi2_targ.h 02.00.03 02.00.03 + mpi2_tool.h 02.00.03 02.00.02 + mpi2_type.h 02.00.00 02.00.00 + mpi2_ra.h 02.00.00 + mpi2_history.txt 02.00.11 02.00.12 + + + * Date Version Description + * -------- -------- ------------------------------------------------------ + +mpi2.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-04-07 02.00.01 Bumped MPI2_HEADER_VERSION_UNIT. + * 06-26-07 02.00.02 Bumped MPI2_HEADER_VERSION_UNIT. + * 08-31-07 02.00.03 Bumped MPI2_HEADER_VERSION_UNIT. + * Moved ReplyPostHostIndex register to offset 0x6C of the + * MPI2_SYSTEM_INTERFACE_REGS and modified the define for + * MPI2_REPLY_POST_HOST_INDEX_OFFSET. + * Added union of request descriptors. + * Added union of reply descriptors. + * 10-31-07 02.00.04 Bumped MPI2_HEADER_VERSION_UNIT. + * Added define for MPI2_VERSION_02_00. + * Fixed the size of the FunctionDependent5 field in the + * MPI2_DEFAULT_REPLY structure. + * 12-18-07 02.00.05 Bumped MPI2_HEADER_VERSION_UNIT. + * Removed the MPI-defined Fault Codes and extended the + * product specific codes up to 0xEFFF. + * Added a sixth key value for the WriteSequence register + * and changed the flush value to 0x0. + * Added message function codes for Diagnostic Buffer Post + * and Diagnsotic Release. + * New IOCStatus define: MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED + * Moved MPI2_VERSION_UNION from mpi2_ioc.h. + * 02-29-08 02.00.06 Bumped MPI2_HEADER_VERSION_UNIT. + * 03-03-08 02.00.07 Bumped MPI2_HEADER_VERSION_UNIT. + * 05-21-08 02.00.08 Bumped MPI2_HEADER_VERSION_UNIT. + * Added #defines for marking a reply descriptor as unused. + * 06-27-08 02.00.09 Bumped MPI2_HEADER_VERSION_UNIT. + * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT. + * Moved LUN field defines from mpi2_init.h. + * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT. + * 05-06-09 02.00.12 Bumped MPI2_HEADER_VERSION_UNIT. + * In all request and reply descriptors, replaced VF_ID + * field with MSIxIndex field. + * Removed DevHandle field from + * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those + * bytes reserved. + * Added RAID Accelerator functionality. + * -------------------------------------------------------------------------- + +mpi2_cnfg.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-04-07 02.00.01 Added defines for SAS IO Unit Page 2 PhyFlags. + * Added Manufacturing Page 11. + * Added MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE + * define. + * 06-26-07 02.00.02 Adding generic structure for product-specific + * Manufacturing pages: MPI2_CONFIG_PAGE_MANUFACTURING_PS. + * Rework of BIOS Page 2 configuration page. + * Fixed MPI2_BIOSPAGE2_BOOT_DEVICE to be a union of the + * forms. + * Added configuration pages IOC Page 8 and Driver + * Persistent Mapping Page 0. + * 08-31-07 02.00.03 Modified configuration pages dealing with Integrated + * RAID (Manufacturing Page 4, RAID Volume Pages 0 and 1, + * RAID Physical Disk Pages 0 and 1, RAID Configuration + * Page 0). + * Added new value for AccessStatus field of SAS Device + * Page 0 (_SATA_NEEDS_INITIALIZATION). + * 10-31-07 02.00.04 Added missing SEPDevHandle field to + * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. + * 12-18-07 02.00.05 Modified IO Unit Page 0 to use 32-bit version fields for + * NVDATA. + * Modified IOC Page 7 to use masks and added field for + * SASBroadcastPrimitiveMasks. + * Added MPI2_CONFIG_PAGE_BIOS_4. + * Added MPI2_CONFIG_PAGE_LOG_0. + * 02-29-08 02.00.06 Modified various names to make them 32-character unique. + * Added SAS Device IDs. + * Updated Integrated RAID configuration pages including + * Manufacturing Page 4, IOC Page 6, and RAID Configuration + * Page 0. + * 05-21-08 02.00.07 Added define MPI2_MANPAGE4_MIX_SSD_SAS_SATA. + * Added define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION. + * Fixed define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING. + * Added missing MaxNumRoutedSasAddresses field to + * MPI2_CONFIG_PAGE_EXPANDER_0. + * Added SAS Port Page 0. + * Modified structure layout for + * MPI2_CONFIG_PAGE_DRIVER_MAPPING_0. + * 06-27-08 02.00.08 Changed MPI2_CONFIG_PAGE_RD_PDISK_1 to use + * MPI2_RAID_PHYS_DISK1_PATH_MAX to size the array. + * 10-02-08 02.00.09 Changed MPI2_RAID_PGAD_CONFIGNUM_MASK from 0x0000FFFF + * to 0x000000FF. + * Added two new values for the Physical Disk Coercion Size + * bits in the Flags field of Manufacturing Page 4. + * Added product-specific Manufacturing pages 16 to 31. + * Modified Flags bits for controlling write cache on SATA + * drives in IO Unit Page 1. + * Added new bit to AdditionalControlFlags of SAS IO Unit + * Page 1 to control Invalid Topology Correction. + * Added SupportedPhysDisks field to RAID Volume Page 1 and + * added related defines. + * Added additional defines for RAID Volume Page 0 + * VolumeStatusFlags field. + * Modified meaning of RAID Volume Page 0 VolumeSettings + * define for auto-configure of hot-swap drives. + * Added PhysDiskAttributes field (and related defines) to + * RAID Physical Disk Page 0. + * Added MPI2_SAS_PHYINFO_PHY_VACANT define. + * Added three new DiscoveryStatus bits for SAS IO Unit + * Page 0 and SAS Expander Page 0. + * Removed multiplexing information from SAS IO Unit pages. + * Added BootDeviceWaitTime field to SAS IO Unit Page 4. + * Removed Zone Address Resolved bit from PhyInfo and from + * Expander Page 0 Flags field. + * Added two new AccessStatus values to SAS Device Page 0 + * for indicating routing problems. Added 3 reserved words + * to this page. + * 01-19-09 02.00.10 Fixed defines for GPIOVal field of IO Unit Page 3. + * Inserted missing reserved field into structure for IOC + * Page 6. + * Added more pending task bits to RAID Volume Page 0 + * VolumeStatusFlags defines. + * Added MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED define. + * Added a new DiscoveryStatus bit for SAS IO Unit Page 0 + * and SAS Expander Page 0 to flag a downstream initiator + * when in simplified routing mode. + * Removed SATA Init Failure defines for DiscoveryStatus + * fields of SAS IO Unit Page 0 and SAS Expander Page 0. + * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define. + * Added PortGroups, DmaGroup, and ControlGroup fields to + * SAS Device Page 0. + * 05-06-09 02.00.11 Added structures and defines for IO Unit Page 5 and IO + * Unit Page 6. + * Added expander reduced functionality data to SAS + * Expander Page 0. + * Added SAS PHY Page 2 and SAS PHY Page 3. + * -------------------------------------------------------------------------- + +mpi2_init.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 10-31-07 02.00.01 Fixed name for pMpi2SCSITaskManagementRequest_t. + * 12-18-07 02.00.02 Modified Task Management Target Reset Method defines. + * 02-29-08 02.00.03 Added Query Task Set and Query Unit Attention. + * 03-03-08 02.00.04 Fixed name of struct _MPI2_SCSI_TASK_MANAGE_REPLY. + * 05-21-08 02.00.05 Fixed typo in name of Mpi2SepRequest_t. + * 10-02-08 02.00.06 Removed Untagged and No Disconnect values from SCSI IO + * Control field Task Attribute flags. + * Moved LUN field defines to mpi2.h becasue they are + * common to many structures. + * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to + * Query Asynchronous Event. + * Defined two new bits in the SlotStatus field of the SCSI + * Enclosure Processor Request and Reply. + * -------------------------------------------------------------------------- + +mpi2_ioc.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-04-07 02.00.01 In IOCFacts Reply structure, renamed MaxDevices to + * MaxTargets. + * Added TotalImageSize field to FWDownload Request. + * Added reserved words to FWUpload Request. + * 06-26-07 02.00.02 Added IR Configuration Change List Event. + * 08-31-07 02.00.03 Removed SystemReplyQueueDepth field from the IOCInit + * request and replaced it with + * ReplyDescriptorPostQueueDepth and ReplyFreeQueueDepth. + * Replaced the MinReplyQueueDepth field of the IOCFacts + * reply with MaxReplyDescriptorPostQueueDepth. + * Added MPI2_RDPQ_DEPTH_MIN define to specify the minimum + * depth for the Reply Descriptor Post Queue. + * Added SASAddress field to Initiator Device Table + * Overflow Event data. + * 10-31-07 02.00.04 Added ReasonCode MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING + * for SAS Initiator Device Status Change Event data. + * Modified Reason Code defines for SAS Topology Change + * List Event data, including adding a bit for PHY Vacant + * status, and adding a mask for the Reason Code. + * Added define for + * MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING. + * Added define for MPI2_EXT_IMAGE_TYPE_MEGARAID. + * 12-18-07 02.00.05 Added Boot Status defines for the IOCExceptions field of + * the IOCFacts Reply. + * Removed MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. + * Moved MPI2_VERSION_UNION to mpi2.h. + * Changed MPI2_EVENT_NOTIFICATION_REQUEST to use masks + * instead of enables, and added SASBroadcastPrimitiveMasks + * field. + * Added Log Entry Added Event and related structure. + * 02-29-08 02.00.06 Added define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID. + * Removed define MPI2_IOCFACTS_PROTOCOL_SMP_TARGET. + * Added MaxVolumes and MaxPersistentEntries fields to + * IOCFacts reply. + * Added ProtocalFlags and IOCCapabilities fields to + * MPI2_FW_IMAGE_HEADER. + * Removed MPI2_PORTENABLE_FLAGS_ENABLE_SINGLE_PORT. + * 03-03-08 02.00.07 Fixed MPI2_FW_IMAGE_HEADER by changing Reserved26 to + * a U16 (from a U32). + * Removed extra 's' from EventMasks name. + * 06-27-08 02.00.08 Fixed an offset in a comment. + * 10-02-08 02.00.09 Removed SystemReplyFrameSize from MPI2_IOC_INIT_REQUEST. + * Removed CurReplyFrameSize from MPI2_IOC_FACTS_REPLY and + * renamed MinReplyFrameSize to ReplyFrameSize. + * Added MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX. + * Added two new RAIDOperation values for Integrated RAID + * Operations Status Event data. + * Added four new IR Configuration Change List Event data + * ReasonCode values. + * Added two new ReasonCode defines for SAS Device Status + * Change Event data. + * Added three new DiscoveryStatus bits for the SAS + * Discovery event data. + * Added Multiplexing Status Change bit to the PhyStatus + * field of the SAS Topology Change List event data. + * Removed define for MPI2_INIT_IMAGE_BOOTFLAGS_XMEMCOPY. + * BootFlags are now product-specific. + * Added defines for the indivdual signature bytes + * for MPI2_INIT_IMAGE_FOOTER. + * 01-19-09 02.00.10 Added MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY define. + * Added MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR + * define. + * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE + * define. + * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define. + * 05-06-09 02.00.11 Added MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR define. + * Added MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX define. + * Added two new reason codes for SAS Device Status Change + * Event. + * Added new event: SAS PHY Counter. + * -------------------------------------------------------------------------- + +mpi2_raid.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 08-31-07 02.00.01 Modifications to RAID Action request and reply, + * including the Actions and ActionData. + * 02-29-08 02.00.02 Added MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD. + * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that + * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT + * can be sized by the build environment. + * -------------------------------------------------------------------------- + +mpi2_sas.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-26-07 02.00.01 Added Clear All Persistent Operation to SAS IO Unit + * Control Request. + * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control + * Request. + * -------------------------------------------------------------------------- + +mpi2_targ.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 08-31-07 02.00.01 Added Command Buffer Data Location Address Space bits to + * BufferPostFlags field of CommandBufferPostBase Request. + * 02-29-08 02.00.02 Modified various names to make them 32-character unique. + * 10-02-08 02.00.03 Removed NextCmdBufferOffset from + * MPI2_TARGET_CMD_BUF_POST_BASE_REQUEST. + * Target Status Send Request only takes a single SGE for + * response data. + * -------------------------------------------------------------------------- + +mpi2_tool.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release + * structures and defines. + * 02-29-08 02.00.02 Modified various names to make them 32-character unique. + * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool. + * -------------------------------------------------------------------------- + +mpi2_type.h + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * -------------------------------------------------------------------------- + +mpi2_ra.h + * 05-06-09 02.00.00 Initial version. + * -------------------------------------------------------------------------- + +mpi2_history.txt Parts list history + +Filename 02.00.12 +---------- -------- +mpi2.h 02.00.12 +mpi2_cnfg.h 02.00.11 +mpi2_init.h 02.00.07 +mpi2_ioc.h 02.00.11 +mpi2_raid.h 02.00.03 +mpi2_sas.h 02.00.02 +mpi2_targ.h 02.00.03 +mpi2_tool.h 02.00.03 +mpi2_type.h 02.00.00 +mpi2_ra.h 02.00.00 + +Filename 02.00.11 02.00.10 02.00.09 02.00.08 02.00.07 02.00.06 +---------- -------- -------- -------- -------- -------- -------- +mpi2.h 02.00.11 02.00.10 02.00.09 02.00.08 02.00.07 02.00.06 +mpi2_cnfg.h 02.00.10 02.00.09 02.00.08 02.00.07 02.00.06 02.00.06 +mpi2_init.h 02.00.06 02.00.06 02.00.05 02.00.05 02.00.04 02.00.03 +mpi2_ioc.h 02.00.10 02.00.09 02.00.08 02.00.07 02.00.07 02.00.06 +mpi2_raid.h 02.00.03 02.00.03 02.00.03 02.00.03 02.00.02 02.00.02 +mpi2_sas.h 02.00.02 02.00.02 02.00.01 02.00.01 02.00.01 02.00.01 +mpi2_targ.h 02.00.03 02.00.03 02.00.02 02.00.02 02.00.02 02.00.02 +mpi2_tool.h 02.00.02 02.00.02 02.00.02 02.00.02 02.00.02 02.00.02 +mpi2_type.h 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 + +Filename 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 +---------- -------- -------- -------- -------- -------- -------- +mpi2.h 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 +mpi2_cnfg.h 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 +mpi2_init.h 02.00.02 02.00.01 02.00.00 02.00.00 02.00.00 02.00.00 +mpi2_ioc.h 02.00.05 02.00.04 02.00.03 02.00.02 02.00.01 02.00.00 +mpi2_raid.h 02.00.01 02.00.01 02.00.01 02.00.00 02.00.00 02.00.00 +mpi2_sas.h 02.00.01 02.00.01 02.00.01 02.00.01 02.00.00 02.00.00 +mpi2_targ.h 02.00.01 02.00.01 02.00.01 02.00.00 02.00.00 02.00.00 +mpi2_tool.h 02.00.01 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 +mpi2_type.h 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 02.00.00 + -- cgit v1.2.3 From 19d3ebe3d5c4d56c8309a64561d99e7920a35fbb Mon Sep 17 00:00:00 2001 From: "Kashyap, Desai" Date: Mon, 14 Sep 2009 11:01:36 +0530 Subject: [SCSI] mpt2sas: Copyright change to 2009. Update the copyright year to 2009 through out the code. Signed-off-by: Kashyap Desai Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/Kconfig | 2 +- drivers/scsi/mpt2sas/mpt2sas_base.c | 2 +- drivers/scsi/mpt2sas/mpt2sas_base.h | 2 +- drivers/scsi/mpt2sas/mpt2sas_config.c | 2 +- drivers/scsi/mpt2sas/mpt2sas_ctl.c | 2 +- drivers/scsi/mpt2sas/mpt2sas_ctl.h | 2 +- drivers/scsi/mpt2sas/mpt2sas_debug.h | 2 +- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 2 +- drivers/scsi/mpt2sas/mpt2sas_transport.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/mpt2sas/Kconfig b/drivers/scsi/mpt2sas/Kconfig index 4a86855c23b3..70c4c2467dd8 100644 --- a/drivers/scsi/mpt2sas/Kconfig +++ b/drivers/scsi/mpt2sas/Kconfig @@ -2,7 +2,7 @@ # Kernel configuration file for the MPT2SAS # # This code is based on drivers/scsi/mpt2sas/Kconfig -# Copyright (C) 2007-2008 LSI Corporation +# Copyright (C) 2007-2009 LSI Corporation # (mailto:DL-MPTFusionLinux@lsi.com) # This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 10de370c171e..675a049a7a5d 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -3,7 +3,7 @@ * for access to MPT (Message Passing Technology) firmware. * * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index 39e50e93a83c..3de37b702468 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -3,7 +3,7 @@ * for access to MPT (Message Passing Technology) firmware. * * This code is based on drivers/scsi/mpt2sas/mpt2_base.h - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c index 2e018c9f5982..d79a4ddf26be 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_config.c +++ b/drivers/scsi/mpt2sas/mpt2sas_config.c @@ -2,7 +2,7 @@ * This module provides common API for accessing firmware configuration pages * * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index af269268febd..37961504aaae 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -3,7 +3,7 @@ * controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt2sas/mpt2sas_ctl.h index 4da11435533f..211f296dd191 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.h +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.h @@ -3,7 +3,7 @@ * controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.h - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_debug.h b/drivers/scsi/mpt2sas/mpt2sas_debug.h index ad325096e842..5308a25cb307 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_debug.h +++ b/drivers/scsi/mpt2sas/mpt2sas_debug.h @@ -2,7 +2,7 @@ * Logging Support for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_debug.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 8277fa366898..10d99086ed46 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -2,7 +2,7 @@ * Scsi Host Layer for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_scsih.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c index 4b0e90ee892b..4b7201a1e867 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_transport.c +++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c @@ -2,7 +2,7 @@ * SAS Transport Layer for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_transport.c - * Copyright (C) 2007-2008 LSI Corporation + * Copyright (C) 2007-2009 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or -- cgit v1.2.3 From 595bb0bd62edb28a965993d90e0fa1285560ce53 Mon Sep 17 00:00:00 2001 From: "Kashyap, Desai" Date: Mon, 14 Sep 2009 11:02:48 +0530 Subject: [SCSI] mpt2sas: Added SCSIIO, Internal and high priority memory pools to support multiple TM 1) create a pool of high priority message frames in the region of memory between message frames and chains. The modifications are in _base_allocate_memory_pools. Also create a seperate pool of memory for internal commands located near the same region of memory. The pool of high priority message frames is restriced by the facts->HighPriorityCredit. 2) Create additional API for accessing request message frames. New function mpt2sas_base_get_smid_hpr is for highpriority request. New function mpt2sas_base_get_smid_scsiio for SCSI_IO, passing in the scsi command pointer. The mpt2sas_base_get_smid function is for requesting internal commands. 3) Added new function _base_get_cb_idx to obtain the callback index from one of the three pools of request message frames. 4) Removed wrapper functions _scsih_scsi_lookup_set and _scsih_scsi_lookup_getclear. These were removed because this handling was moved into mpt2sas_base_get_smid_scsiio and mpt2sas_base_free_smid. 5) The function mpt2sas_base_free_smid is modified so the request message frames are put back on one of the three pools of request message frames. Signed-off-by: Kashyap Desai Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_base.c | 302 ++++++++++++++++++++++++++++------- drivers/scsi/mpt2sas/mpt2sas_base.h | 49 +++++- drivers/scsi/mpt2sas/mpt2sas_ctl.c | 4 +- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 89 +++-------- 4 files changed, 307 insertions(+), 137 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 675a049a7a5d..4c583ff458db 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -63,7 +63,7 @@ static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS]; #define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */ -#define MPT2SAS_MAX_REQUEST_QUEUE 500 /* maximum controller queue depth */ +#define MPT2SAS_MAX_REQUEST_QUEUE 600 /* maximum controller queue depth */ static int max_queue_depth = -1; module_param(max_queue_depth, int, 0); @@ -649,6 +649,34 @@ _base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) mpt2sas_ctl_event_callback(ioc, msix_index, reply); } +/** + * _base_get_cb_idx - obtain the callback index + * @ioc: per adapter object + * @smid: system request message index + * + * Return callback index. + */ +static u8 +_base_get_cb_idx(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + int i; + u8 cb_idx = 0xFF; + + if (smid >= ioc->hi_priority_smid) { + if (smid < ioc->internal_smid) { + i = smid - ioc->hi_priority_smid; + cb_idx = ioc->hpr_lookup[i].cb_idx; + } else { + i = smid - ioc->internal_smid; + cb_idx = ioc->internal_lookup[i].cb_idx; + } + } else { + i = smid - 1; + cb_idx = ioc->scsi_lookup[i].cb_idx; + } + return cb_idx; +} + /** * _base_mask_interrupts - disable interrupts * @ioc: pointer to scsi command object @@ -747,9 +775,10 @@ _base_interrupt(int irq, void *bus_id) MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS) goto next; if (smid) - cb_idx = ioc->scsi_lookup[smid - 1].cb_idx; + cb_idx = _base_get_cb_idx(ioc, smid); if (smid && cb_idx != 0xFF) { - mpt_callbacks[cb_idx](ioc, smid, msix_index, reply); + mpt_callbacks[cb_idx](ioc, smid, msix_index, + reply); if (reply) _base_display_reply_info(ioc, smid, msix_index, reply); @@ -1192,19 +1221,6 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) return r; } -/** - * mpt2sas_base_get_msg_frame_dma - obtain request mf pointer phys addr - * @ioc: per adapter object - * @smid: system request message index(smid zero is invalid) - * - * Returns phys pointer to message frame. - */ -dma_addr_t -mpt2sas_base_get_msg_frame_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - return ioc->request_dma + (smid * ioc->request_sz); -} - /** * mpt2sas_base_get_msg_frame - obtain request mf pointer * @ioc: per adapter object @@ -1260,7 +1276,7 @@ mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr) } /** - * mpt2sas_base_get_smid - obtain a free smid + * mpt2sas_base_get_smid - obtain a free smid from internal queue * @ioc: per adapter object * @cb_idx: callback index * @@ -1273,6 +1289,39 @@ mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx) struct request_tracker *request; u16 smid; + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + if (list_empty(&ioc->internal_free_list)) { + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + printk(MPT2SAS_ERR_FMT "%s: smid not available\n", + ioc->name, __func__); + return 0; + } + + request = list_entry(ioc->internal_free_list.next, + struct request_tracker, tracker_list); + request->cb_idx = cb_idx; + smid = request->smid; + list_del(&request->tracker_list); + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return smid; +} + +/** + * mpt2sas_base_get_smid_scsiio - obtain a free smid from scsiio queue + * @ioc: per adapter object + * @cb_idx: callback index + * @scmd: pointer to scsi command object + * + * Returns smid (zero is invalid) + */ +u16 +mpt2sas_base_get_smid_scsiio(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx, + struct scsi_cmnd *scmd) +{ + unsigned long flags; + struct request_tracker *request; + u16 smid; + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); if (list_empty(&ioc->free_list)) { spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); @@ -1283,6 +1332,36 @@ mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx) request = list_entry(ioc->free_list.next, struct request_tracker, tracker_list); + request->scmd = scmd; + request->cb_idx = cb_idx; + smid = request->smid; + list_del(&request->tracker_list); + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return smid; +} + +/** + * mpt2sas_base_get_smid_hpr - obtain a free smid from hi-priority queue + * @ioc: per adapter object + * @cb_idx: callback index + * + * Returns smid (zero is invalid) + */ +u16 +mpt2sas_base_get_smid_hpr(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx) +{ + unsigned long flags; + struct request_tracker *request; + u16 smid; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + if (list_empty(&ioc->hpr_free_list)) { + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return 0; + } + + request = list_entry(ioc->hpr_free_list.next, + struct request_tracker, tracker_list); request->cb_idx = cb_idx; smid = request->smid; list_del(&request->tracker_list); @@ -1302,10 +1381,32 @@ void mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid) { unsigned long flags; + int i; spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - ioc->scsi_lookup[smid - 1].cb_idx = 0xFF; - list_add_tail(&ioc->scsi_lookup[smid - 1].tracker_list, + if (smid >= ioc->hi_priority_smid) { + if (smid < ioc->internal_smid) { + /* hi-priority */ + i = smid - ioc->hi_priority_smid; + ioc->hpr_lookup[i].cb_idx = 0xFF; + list_add_tail(&ioc->hpr_lookup[i].tracker_list, + &ioc->hpr_free_list); + } else { + /* internal queue */ + i = smid - ioc->internal_smid; + ioc->internal_lookup[i].cb_idx = 0xFF; + list_add_tail(&ioc->internal_lookup[i].tracker_list, + &ioc->internal_free_list); + } + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return; + } + + /* scsiio queue */ + i = smid - 1; + ioc->scsi_lookup[i].cb_idx = 0xFF; + ioc->scsi_lookup[i].scmd = NULL; + list_add_tail(&ioc->scsi_lookup[i].tracker_list, &ioc->free_list); spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); @@ -1713,6 +1814,8 @@ _base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc) } kfree(ioc->scsi_lookup); + kfree(ioc->hpr_lookup); + kfree(ioc->internal_lookup); } @@ -1732,7 +1835,6 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) u16 num_of_reply_frames; u16 chains_needed_per_io; u32 sz, total_sz; - u16 i; u32 retry_sz; u16 max_request_credit; @@ -1760,7 +1862,10 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) MPT2SAS_MAX_REQUEST_QUEUE) ? MPT2SAS_MAX_REQUEST_QUEUE : facts->RequestCredit; } - ioc->request_depth = max_request_credit; + + ioc->hba_queue_depth = max_request_credit; + ioc->hi_priority_depth = facts->HighPriorityCredit; + ioc->internal_depth = ioc->hi_priority_depth + 5; /* request frame size */ ioc->request_sz = facts->IOCRequestFrameSize * 4; @@ -1798,7 +1903,7 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) ioc->chains_needed_per_io = chains_needed_per_io; /* reply free queue sizing - taking into account for events */ - num_of_reply_frames = ioc->request_depth + 32; + num_of_reply_frames = ioc->hba_queue_depth + 32; /* number of replies frames can't be a multiple of 16 */ /* decrease number of reply frames by 1 */ @@ -1819,7 +1924,7 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) * frames */ - queue_size = ioc->request_depth + num_of_reply_frames + 1; + queue_size = ioc->hba_queue_depth + num_of_reply_frames + 1; /* round up to 16 byte boundary */ if (queue_size % 16) queue_size += 16 - (queue_size % 16); @@ -1833,60 +1938,85 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) if (queue_diff % 16) queue_diff += 16 - (queue_diff % 16); - /* adjust request_depth, reply_free_queue_depth, + /* adjust hba_queue_depth, reply_free_queue_depth, * and queue_size */ - ioc->request_depth -= queue_diff; + ioc->hba_queue_depth -= queue_diff; ioc->reply_free_queue_depth -= queue_diff; queue_size -= queue_diff; } ioc->reply_post_queue_depth = queue_size; - /* max scsi host queue depth */ - ioc->shost->can_queue = ioc->request_depth - INTERNAL_CMDS_COUNT; - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsi host queue: depth" - "(%d)\n", ioc->name, ioc->shost->can_queue)); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scatter gather: " "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), " "chains_per_io(%d)\n", ioc->name, ioc->max_sges_in_main_message, ioc->max_sges_in_chain_message, ioc->shost->sg_tablesize, ioc->chains_needed_per_io)); + ioc->scsiio_depth = ioc->hba_queue_depth - + ioc->hi_priority_depth - ioc->internal_depth; + + /* set the scsi host can_queue depth + * with some internal commands that could be outstanding + */ + ioc->shost->can_queue = ioc->scsiio_depth - (2); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsi host: " + "can_queue depth (%d)\n", ioc->name, ioc->shost->can_queue)); + /* contiguous pool for request and chains, 16 byte align, one extra " * "frame for smid=0 */ - ioc->chain_depth = ioc->chains_needed_per_io * ioc->request_depth; - sz = ((ioc->request_depth + 1 + ioc->chain_depth) * ioc->request_sz); + ioc->chain_depth = ioc->chains_needed_per_io * ioc->scsiio_depth; + sz = ((ioc->scsiio_depth + 1 + ioc->chain_depth) * ioc->request_sz); + + /* hi-priority queue */ + sz += (ioc->hi_priority_depth * ioc->request_sz); + + /* internal queue */ + sz += (ioc->internal_depth * ioc->request_sz); ioc->request_dma_sz = sz; ioc->request = pci_alloc_consistent(ioc->pdev, sz, &ioc->request_dma); if (!ioc->request) { printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent " - "failed: req_depth(%d), chains_per_io(%d), frame_sz(%d), " - "total(%d kB)\n", ioc->name, ioc->request_depth, + "failed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), " + "total(%d kB)\n", ioc->name, ioc->hba_queue_depth, ioc->chains_needed_per_io, ioc->request_sz, sz/1024); - if (ioc->request_depth < MPT2SAS_SAS_QUEUE_DEPTH) + if (ioc->scsiio_depth < MPT2SAS_SAS_QUEUE_DEPTH) goto out; retry_sz += 64; - ioc->request_depth = max_request_credit - retry_sz; + ioc->hba_queue_depth = max_request_credit - retry_sz; goto retry_allocation; } if (retry_sz) printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent " - "succeed: req_depth(%d), chains_per_io(%d), frame_sz(%d), " - "total(%d kb)\n", ioc->name, ioc->request_depth, + "succeed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), " + "total(%d kb)\n", ioc->name, ioc->hba_queue_depth, ioc->chains_needed_per_io, ioc->request_sz, sz/1024); - ioc->chain = ioc->request + ((ioc->request_depth + 1) * + + /* hi-priority queue */ + ioc->hi_priority = ioc->request + ((ioc->scsiio_depth + 1) * ioc->request_sz); - ioc->chain_dma = ioc->request_dma + ((ioc->request_depth + 1) * + ioc->hi_priority_dma = ioc->request_dma + ((ioc->scsiio_depth + 1) * + ioc->request_sz); + + /* internal queue */ + ioc->internal = ioc->hi_priority + (ioc->hi_priority_depth * ioc->request_sz); + ioc->internal_dma = ioc->hi_priority_dma + (ioc->hi_priority_depth * + ioc->request_sz); + + ioc->chain = ioc->internal + (ioc->internal_depth * + ioc->request_sz); + ioc->chain_dma = ioc->internal_dma + (ioc->internal_depth * + ioc->request_sz); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool(0x%p): " "depth(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, - ioc->request, ioc->request_depth, ioc->request_sz, - ((ioc->request_depth + 1) * ioc->request_sz)/1024)); + ioc->request, ioc->hba_queue_depth, ioc->request_sz, + (ioc->hba_queue_depth * ioc->request_sz)/1024)); dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "chain pool(0x%p): depth" "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->chain, ioc->chain_depth, ioc->request_sz, ((ioc->chain_depth * @@ -1895,7 +2025,7 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) ioc->name, (unsigned long long) ioc->request_dma)); total_sz += sz; - ioc->scsi_lookup = kcalloc(ioc->request_depth, + ioc->scsi_lookup = kcalloc(ioc->scsiio_depth, sizeof(struct request_tracker), GFP_KERNEL); if (!ioc->scsi_lookup) { printk(MPT2SAS_ERR_FMT "scsi_lookup: kcalloc failed\n", @@ -1903,12 +2033,38 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) goto out; } - /* initialize some bits */ - for (i = 0; i < ioc->request_depth; i++) - ioc->scsi_lookup[i].smid = i + 1; + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsiio(0x%p): " + "depth(%d)\n", ioc->name, ioc->request, + ioc->scsiio_depth)); + + /* initialize hi-priority queue smid's */ + ioc->hpr_lookup = kcalloc(ioc->hi_priority_depth, + sizeof(struct request_tracker), GFP_KERNEL); + if (!ioc->hpr_lookup) { + printk(MPT2SAS_ERR_FMT "hpr_lookup: kcalloc failed\n", + ioc->name); + goto out; + } + ioc->hi_priority_smid = ioc->scsiio_depth + 1; + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "hi_priority(0x%p): " + "depth(%d), start smid(%d)\n", ioc->name, ioc->hi_priority, + ioc->hi_priority_depth, ioc->hi_priority_smid)); + + /* initialize internal queue smid's */ + ioc->internal_lookup = kcalloc(ioc->internal_depth, + sizeof(struct request_tracker), GFP_KERNEL); + if (!ioc->internal_lookup) { + printk(MPT2SAS_ERR_FMT "internal_lookup: kcalloc failed\n", + ioc->name); + goto out; + } + ioc->internal_smid = ioc->hi_priority_smid + ioc->hi_priority_depth; + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "internal(0x%p): " + "depth(%d), start smid(%d)\n", ioc->name, ioc->internal, + ioc->internal_depth, ioc->internal_smid)); /* sense buffers, 4 byte align */ - sz = ioc->request_depth * SCSI_SENSE_BUFFERSIZE; + sz = ioc->scsiio_depth * SCSI_SENSE_BUFFERSIZE; ioc->sense_dma_pool = pci_pool_create("sense pool", ioc->pdev, sz, 4, 0); if (!ioc->sense_dma_pool) { @@ -1925,7 +2081,7 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) } dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense pool(0x%p): depth(%d), element_size(%d), pool_size" - "(%d kB)\n", ioc->name, ioc->sense, ioc->request_depth, + "(%d kB)\n", ioc->name, ioc->sense, ioc->scsiio_depth, SCSI_SENSE_BUFFERSIZE, sz/1024)); dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense_dma(0x%llx)\n", ioc->name, (unsigned long long)ioc->sense_dma)); @@ -3166,6 +3322,7 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) int r, i; unsigned long flags; u32 reply_address; + u16 smid; dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, __func__)); @@ -3173,11 +3330,34 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) /* initialize the scsi lookup free list */ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); INIT_LIST_HEAD(&ioc->free_list); - for (i = 0; i < ioc->request_depth; i++) { + smid = 1; + for (i = 0; i < ioc->scsiio_depth; i++, smid++) { ioc->scsi_lookup[i].cb_idx = 0xFF; + ioc->scsi_lookup[i].smid = smid; + ioc->scsi_lookup[i].scmd = NULL; list_add_tail(&ioc->scsi_lookup[i].tracker_list, &ioc->free_list); } + + /* hi-priority queue */ + INIT_LIST_HEAD(&ioc->hpr_free_list); + smid = ioc->hi_priority_smid; + for (i = 0; i < ioc->hi_priority_depth; i++, smid++) { + ioc->hpr_lookup[i].cb_idx = 0xFF; + ioc->hpr_lookup[i].smid = smid; + list_add_tail(&ioc->hpr_lookup[i].tracker_list, + &ioc->hpr_free_list); + } + + /* internal queue */ + INIT_LIST_HEAD(&ioc->internal_free_list); + smid = ioc->internal_smid; + for (i = 0; i < ioc->internal_depth; i++, smid++) { + ioc->internal_lookup[i].cb_idx = 0xFF; + ioc->internal_lookup[i].smid = smid; + list_add_tail(&ioc->internal_lookup[i].tracker_list, + &ioc->internal_free_list); + } spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); /* initialize Reply Free Queue */ @@ -3272,6 +3452,17 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) if (r) goto out_free_resources; + ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, + sizeof(Mpi2PortFactsReply_t), GFP_KERNEL); + if (!ioc->pfacts) + goto out_free_resources; + + for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { + r = _base_get_port_facts(ioc, i, CAN_SLEEP); + if (r) + goto out_free_resources; + } + r = _base_allocate_memory_pools(ioc, CAN_SLEEP); if (r) goto out_free_resources; @@ -3321,17 +3512,6 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS); _base_unmask_events(ioc, MPI2_EVENT_TASK_SET_FULL); _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED); - - ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, - sizeof(Mpi2PortFactsReply_t), GFP_KERNEL); - if (!ioc->pfacts) - goto out_free_resources; - - for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { - r = _base_get_port_facts(ioc, i, CAN_SLEEP); - if (r) - goto out_free_resources; - } r = _base_make_ioc_operational(ioc, CAN_SLEEP); if (r) goto out_free_resources; @@ -3460,7 +3640,7 @@ _wait_for_commands_to_complete(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) /* pending command count */ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - for (i = 0; i < ioc->request_depth; i++) + for (i = 0; i < ioc->scsiio_depth; i++) if (ioc->scsi_lookup[i].cb_idx != 0xFF) ioc->pending_io_count++; spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index 3de37b702468..91132626f4c5 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -264,6 +264,13 @@ struct _internal_cmd { * SAS Topology Structures */ +#define MPTSAS_STATE_TR_SEND 0x0001 +#define MPTSAS_STATE_TR_COMPLETE 0x0002 +#define MPTSAS_STATE_CNTRL_SEND 0x0004 +#define MPTSAS_STATE_CNTRL_COMPLETE 0x0008 + +#define MPT2SAS_REQ_SAS_CNTRL 0x0010 + /** * struct _sas_device - attached device information * @list: sas device list @@ -510,8 +517,9 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); * @config_page_sz: config page size * @config_page: reserve memory for config page payload * @config_page_dma: + * @hba_queue_depth: hba request queue depth * @sge_size: sg element size for either 32/64 bit - * @request_depth: hba request queue depth + * @scsiio_depth: SCSI_IO queue depth * @request_sz: per request frame size * @request: pool of request frames * @request_dma: @@ -528,6 +536,18 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); * @chains_needed_per_io: max chains per io * @chain_offset_value_for_main_message: location 1st sg in main * @chain_depth: total chains allocated + * @hi_priority_smid: + * @hi_priority: + * @hi_priority_dma: + * @hi_priority_depth: + * @hpr_lookup: + * @hpr_free_list: + * @internal_smid: + * @internal: + * @internal_dma: + * @internal_depth: + * @internal_lookup: + * @internal_free_list: * @sense: pool of sense * @sense_dma: * @sense_dma_pool: @@ -643,9 +663,10 @@ struct MPT2SAS_ADAPTER { void *config_page; dma_addr_t config_page_dma; - /* request */ + /* scsiio request */ + u16 hba_queue_depth; u16 sge_size; - u16 request_depth; + u16 scsiio_depth; u16 request_sz; u8 *request; dma_addr_t request_dma; @@ -665,6 +686,22 @@ struct MPT2SAS_ADAPTER { u16 chain_offset_value_for_main_message; u16 chain_depth; + /* hi-priority queue */ + u16 hi_priority_smid; + u8 *hi_priority; + dma_addr_t hi_priority_dma; + u16 hi_priority_depth; + struct request_tracker *hpr_lookup; + struct list_head hpr_free_list; + + /* internal queue */ + u16 internal_smid; + u8 *internal; + dma_addr_t internal_dma; + u16 internal_depth; + struct request_tracker *internal_lookup; + struct list_head internal_free_list; + /* sense */ u8 *sense; dma_addr_t sense_dma; @@ -720,9 +757,13 @@ int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, void *mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid); void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid); void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr); -dma_addr_t mpt2sas_base_get_msg_frame_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid); dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid); +/* hi-priority queue */ +u16 mpt2sas_base_get_smid_hpr(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx); +u16 mpt2sas_base_get_smid_scsiio(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx, + struct scsi_cmnd *scmd); + u16 mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx); void mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid); void mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index 37961504aaae..466e2f42367f 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -509,7 +509,7 @@ _ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg, handle = le16_to_cpu(tm_request->DevHandle); spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - for (i = ioc->request_depth; i && !found; i--) { + for (i = ioc->scsiio_depth; i && !found; i--) { scmd = ioc->scsi_lookup[i - 1].scmd; if (scmd == NULL || scmd->device == NULL || scmd->device->hostdata == NULL) @@ -616,7 +616,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", ioc->name, __func__); - smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); + smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->ctl_cb_idx, NULL); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 10d99086ed46..d4b003a618a1 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -762,66 +762,16 @@ _scsih_is_end_device(u32 device_info) } /** - * _scsih_scsi_lookup_get - returns scmd entry + * mptscsih_get_scsi_lookup - returns scmd entry * @ioc: per adapter object * @smid: system request message index - * Context: This function will acquire ioc->scsi_lookup_lock. * * Returns the smid stored scmd pointer. */ static struct scsi_cmnd * _scsih_scsi_lookup_get(struct MPT2SAS_ADAPTER *ioc, u16 smid) { - unsigned long flags; - struct scsi_cmnd *scmd; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - scmd = ioc->scsi_lookup[smid - 1].scmd; - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return scmd; -} - -/** - * mptscsih_getclear_scsi_lookup - returns scmd entry - * @ioc: per adapter object - * @smid: system request message index - * Context: This function will acquire ioc->scsi_lookup_lock. - * - * Returns the smid stored scmd pointer, as well as clearing the scmd pointer. - */ -static struct scsi_cmnd * -_scsih_scsi_lookup_getclear(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - unsigned long flags; - struct scsi_cmnd *scmd; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - scmd = ioc->scsi_lookup[smid - 1].scmd; - ioc->scsi_lookup[smid - 1].scmd = NULL; - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return scmd; -} - -/** - * _scsih_scsi_lookup_set - updates scmd entry in lookup - * @ioc: per adapter object - * @smid: system request message index - * @scmd: pointer to scsi command object - * Context: This function will acquire ioc->scsi_lookup_lock. - * - * This will save scmd pointer in the scsi_lookup array. - * - * Return nothing. - */ -static void -_scsih_scsi_lookup_set(struct MPT2SAS_ADAPTER *ioc, u16 smid, - struct scsi_cmnd *scmd) -{ - unsigned long flags; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - ioc->scsi_lookup[smid - 1].scmd = scmd; - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return ioc->scsi_lookup[smid - 1].scmd; } /** @@ -844,9 +794,9 @@ _scsih_scsi_lookup_find_by_scmd(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); smid = 0; - for (i = 0; i < ioc->request_depth; i++) { + for (i = 0; i < ioc->scsiio_depth; i++) { if (ioc->scsi_lookup[i].scmd == scmd) { - smid = i + 1; + smid = ioc->scsi_lookup[i].smid; goto out; } } @@ -875,7 +825,7 @@ _scsih_scsi_lookup_find_by_target(struct MPT2SAS_ADAPTER *ioc, int id, spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); found = 0; - for (i = 0 ; i < ioc->request_depth; i++) { + for (i = 0 ; i < ioc->scsiio_depth; i++) { if (ioc->scsi_lookup[i].scmd && (ioc->scsi_lookup[i].scmd->device->id == id && ioc->scsi_lookup[i].scmd->device->channel == channel)) { @@ -909,7 +859,7 @@ _scsih_scsi_lookup_find_by_lun(struct MPT2SAS_ADAPTER *ioc, int id, spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); found = 0; - for (i = 0 ; i < ioc->request_depth; i++) { + for (i = 0 ; i < ioc->scsiio_depth; i++) { if (ioc->scsi_lookup[i].scmd && (ioc->scsi_lookup[i].scmd->device->id == id && ioc->scsi_lookup[i].scmd->device->channel == channel && @@ -1119,7 +1069,7 @@ _scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) } /** - * _scsih_change_queue_depth - changing device queue tag type + * _scsih_change_queue_type - changing device queue tag type * @sdev: scsi device struct * @tag_type: requested tag type * @@ -1822,7 +1772,7 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, goto issue_host_reset; } - smid = mpt2sas_base_get_smid(ioc, ioc->tm_cb_idx); + smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); @@ -1830,7 +1780,8 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, } dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "sending tm: handle(0x%04x)," - " task_type(0x%02x), smid(%d)\n", ioc->name, handle, type, smid)); + " task_type(0x%02x), smid(%d)\n", ioc->name, handle, type, + smid_task)); ioc->tm_cmds.status = MPT2_CMD_PENDING; mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ioc->tm_cmds.smid = smid; @@ -2082,7 +2033,7 @@ _scsih_target_reset(struct scsi_cmnd *scmd) } /** - * _scsih_abort - eh threads main host reset routine + * _scsih_host_reset - eh threads main host reset routine * @sdev: scsi device struct * * Returns SUCCESS if command aborted else FAILED @@ -2440,8 +2391,8 @@ _scsih_flush_running_cmds(struct MPT2SAS_ADAPTER *ioc) u16 smid; u16 count = 0; - for (smid = 1; smid <= ioc->request_depth; smid++) { - scmd = _scsih_scsi_lookup_getclear(ioc, smid); + for (smid = 1; smid <= ioc->scsiio_depth; smid++) { + scmd = _scsih_scsi_lookup_get(ioc, smid); if (!scmd) continue; count++; @@ -2623,7 +2574,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON)) mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON; - smid = mpt2sas_base_get_smid(ioc, ioc->scsi_io_cb_idx); + smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); @@ -2665,7 +2616,6 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) } } - _scsih_scsi_lookup_set(ioc, smid, scmd); mpt2sas_base_put_smid_scsi_io(ioc, smid, sas_device_priv_data->sas_target->handle); return 0; @@ -2984,7 +2934,7 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) u32 response_code; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - scmd = _scsih_scsi_lookup_getclear(ioc, smid); + scmd = _scsih_scsi_lookup_get(ioc, smid); if (scmd == NULL) return; @@ -3406,9 +3356,8 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) } } - sas_address = le64_to_cpu(expander_pg0.SASAddress); - spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_address = le64_to_cpu(expander_pg0.SASAddress); sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, sas_address); spin_unlock_irqrestore(&ioc->sas_node_lock, flags); @@ -4081,7 +4030,7 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, termination_count = 0; query_count = 0; mpi_reply = ioc->tm_cmds.reply; - for (smid = 1; smid <= ioc->request_depth; smid++) { + for (smid = 1; smid <= ioc->scsiio_depth; smid++) { scmd = _scsih_scsi_lookup_get(ioc, smid); if (!scmd) continue; @@ -4145,8 +4094,8 @@ _scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc, (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? "start" : "stop"); if (event_data->DiscoveryStatus) - printk(MPT2SAS_DEBUG_FMT ", discovery_status(0x%08x)", - ioc->name, le32_to_cpu(event_data->DiscoveryStatus)); + printk("discovery_status(0x%08x)", + le32_to_cpu(event_data->DiscoveryStatus)); printk("\n"); } #endif -- cgit v1.2.3 From 77e63ed44305e89c0564f8292f9cd5764d4fddfb Mon Sep 17 00:00:00 2001 From: "Kashyap, Desai" Date: Mon, 14 Sep 2009 11:04:23 +0530 Subject: [SCSI] mpt2sas: Target Reset will be issued from Interrupt context. (1) Added three new functions to handle sending target resest and OP_REMOVE from interrupt time, they are _scsih_tm_tr_send, _scsih_tm_tr_complete, and _scsih_sas_control_complete. This code will create a link list of pending target resets if there is no more available request in the hipriority request queue. The list is stored in ioc->delayed_tr_list. (2) All callback handler return type is changed from void to u8. Now _base_interrupt will check for return type of callback handlers to take decision of message frame is already freed or not. In genral, Return 1 meaning mf should be freed from _base_interrupt 0 means the mf is freed from function. Signed-off-by: Kashyap Desai Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_base.c | 35 +++- drivers/scsi/mpt2sas/mpt2sas_base.h | 41 ++-- drivers/scsi/mpt2sas/mpt2sas_config.c | 10 +- drivers/scsi/mpt2sas/mpt2sas_ctl.c | 16 +- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 314 +++++++++++++++++++++++++++++-- drivers/scsi/mpt2sas/mpt2sas_transport.c | 10 +- 6 files changed, 372 insertions(+), 54 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 4c583ff458db..a9c71769b4ec 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -575,9 +575,10 @@ _base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void +u8 mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { @@ -585,10 +586,10 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK) - return; + return 1; if (ioc->base_cmds.status == MPT2_CMD_NOT_USED) - return; + return 1; ioc->base_cmds.status |= MPT2_CMD_COMPLETE; if (mpi_reply) { @@ -597,6 +598,7 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, } ioc->base_cmds.status &= ~MPT2_CMD_PENDING; complete(&ioc->base_cmds.done); + return 1; } /** @@ -605,9 +607,10 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -static void +static u8 _base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) { Mpi2EventNotificationReply_t *mpi_reply; @@ -616,9 +619,9 @@ _base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (!mpi_reply) - return; + return 1; if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION) - return; + return 1; #ifdef CONFIG_SCSI_MPT2SAS_LOGGING _base_display_event_data(ioc, mpi_reply); #endif @@ -647,6 +650,8 @@ _base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) /* ctl callback handler */ mpt2sas_ctl_event_callback(ioc, msix_index, reply); + + return 1; } /** @@ -745,6 +750,7 @@ _base_interrupt(int irq, void *bus_id) u8 msix_index; struct MPT2SAS_ADAPTER *ioc = bus_id; Mpi2ReplyDescriptorsUnion_t *rpf; + u8 rc; if (ioc->mask_interrupts) return IRQ_NONE; @@ -777,12 +783,13 @@ _base_interrupt(int irq, void *bus_id) if (smid) cb_idx = _base_get_cb_idx(ioc, smid); if (smid && cb_idx != 0xFF) { - mpt_callbacks[cb_idx](ioc, smid, msix_index, + rc = mpt_callbacks[cb_idx](ioc, smid, msix_index, reply); if (reply) _base_display_reply_info(ioc, smid, msix_index, reply); - mpt2sas_base_free_smid(ioc, smid); + if (rc) + mpt2sas_base_free_smid(ioc, smid); } if (!smid) _base_async_event(ioc, msix_index, reply); @@ -3323,10 +3330,18 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) unsigned long flags; u32 reply_address; u16 smid; + struct _tr_list *delayed_tr, *delayed_tr_next; dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, __func__)); + /* clean the delayed target reset list */ + list_for_each_entry_safe(delayed_tr, delayed_tr_next, + &ioc->delayed_tr_list, list) { + list_del(&delayed_tr->list); + kfree(delayed_tr); + } + /* initialize the scsi lookup free list */ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); INIT_LIST_HEAD(&ioc->free_list); diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index 91132626f4c5..7f94be90ad12 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -307,6 +307,7 @@ struct _sas_device { u16 slot; u8 hidden_raid_component; u8 responding; + u16 state; }; /** @@ -443,6 +444,17 @@ struct request_tracker { struct list_head tracker_list; }; +/** + * struct _tr_list - target reset list + * @handle: device handle + * @state: state machine + */ +struct _tr_list { + struct list_head list; + u16 handle; + u16 state; +}; + typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); /** @@ -617,6 +629,8 @@ struct MPT2SAS_ADAPTER { u8 ctl_cb_idx; u8 base_cb_idx; u8 config_cb_idx; + u8 tm_tr_cb_idx; + u8 tm_sas_control_cb_idx; struct _internal_cmd base_cmds; struct _internal_cmd transport_cmds; struct _internal_cmd tm_cmds; @@ -727,6 +741,8 @@ struct MPT2SAS_ADAPTER { struct dma_pool *reply_post_free_dma_pool; u32 reply_post_host_index; + struct list_head delayed_tr_list; + /* diag buffer support */ u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT]; u32 diag_buffer_sz[MPI2_DIAG_BUF_TYPE_COUNT]; @@ -738,8 +754,8 @@ struct MPT2SAS_ADAPTER { u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT]; }; -typedef void (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u8 msix_index, u32 reply); +typedef u8 (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply); /* base shared API */ @@ -757,7 +773,8 @@ int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, void *mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid); void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid); void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr); -dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid); +dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, + u16 smid); /* hi-priority queue */ u16 mpt2sas_base_get_smid_hpr(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx); @@ -776,7 +793,7 @@ void mpt2sas_base_initialize_callback_handler(void); u8 mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func); void mpt2sas_base_release_callback_handler(u8 cb_idx); -void mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, +u8 mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); void *mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr); @@ -791,6 +808,8 @@ int mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type); /* scsih shared API */ +u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, + u32 reply); void mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, u8 type, u16 smid_task, ulong timeout); void mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); @@ -802,12 +821,10 @@ struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAP struct _sas_device *mpt2sas_scsih_sas_device_find_by_sas_address( struct MPT2SAS_ADAPTER *ioc, u64 sas_address); -void mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, - u32 reply); void mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); /* config shared API */ -void mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, +u8 mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); int mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys); int mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc, @@ -861,17 +878,17 @@ extern struct device_attribute *mpt2sas_host_attrs[]; extern struct device_attribute *mpt2sas_dev_attrs[]; void mpt2sas_ctl_init(void); void mpt2sas_ctl_exit(void); -void mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, +u8 mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); void mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); -void mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, +u8 mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply); void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, Mpi2EventNotificationReply_t *mpi_reply); /* transport shared API */ -void mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u8 msix_index, u32 reply); +u8 mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply); struct _sas_port *mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, u16 parent_handle); void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, @@ -885,6 +902,8 @@ void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, u16 handle, extern struct sas_function_template mpt2sas_transport_functions; extern struct scsi_transport_template *mpt2sas_transport_template; extern int scsi_internal_device_block(struct scsi_device *sdev); +extern u8 mpt2sas_stm_zero_smid_handler(struct MPT2SAS_ADAPTER *ioc, + u8 msix_index, u32 reply); extern int scsi_internal_device_unblock(struct scsi_device *sdev); #endif /* MPT2SAS_BASE_H_INCLUDED */ diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c index d79a4ddf26be..594a389c6526 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_config.c +++ b/drivers/scsi/mpt2sas/mpt2sas_config.c @@ -233,18 +233,19 @@ _config_free_config_dma_memory(struct MPT2SAS_ADAPTER *ioc, * * The callback handler when using _config_request. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void +u8 mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; if (ioc->config_cmds.status == MPT2_CMD_NOT_USED) - return; + return 1; if (ioc->config_cmds.smid != smid) - return; + return 1; ioc->config_cmds.status |= MPT2_CMD_COMPLETE; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (mpi_reply) { @@ -258,6 +259,7 @@ mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, #endif ioc->config_cmds.smid = USHORT_MAX; complete(&ioc->config_cmds.done); + return 1; } /** diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index 466e2f42367f..33463ace0d48 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -225,18 +225,19 @@ _ctl_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, * * The callback handler when using ioc->ctl_cb_idx. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void +u8 mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; if (ioc->ctl_cmds.status == MPT2_CMD_NOT_USED) - return; + return 1; if (ioc->ctl_cmds.smid != smid) - return; + return 1; ioc->ctl_cmds.status |= MPT2_CMD_COMPLETE; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (mpi_reply) { @@ -248,6 +249,7 @@ mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, #endif ioc->ctl_cmds.status &= ~MPT2_CMD_PENDING; complete(&ioc->ctl_cmds.done); + return 1; } /** @@ -336,9 +338,10 @@ mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, * This function merely adds a new work task into ioc->firmware_event_thread. * The tasks are worked from _firmware_event_work in user context. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void +u8 mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) { @@ -346,6 +349,7 @@ mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); mpt2sas_ctl_add_to_event_log(ioc, mpi_reply); + return 1; } /** diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index d4b003a618a1..9ec50729d535 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -79,6 +79,9 @@ static u8 transport_cb_idx = -1; static u8 config_cb_idx = -1; static int mpt_ids; +static u8 tm_tr_cb_idx = -1 ; +static u8 tm_sas_control_cb_idx = -1; + /* command line options */ static u32 logging_level; MODULE_PARM_DESC(logging_level, " bits for enabling additional logging info " @@ -1641,17 +1644,18 @@ _scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code) * * The callback handler when using scsih_issue_tm. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -static void +static u8 _scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { MPI2DefaultReply_t *mpi_reply; if (ioc->tm_cmds.status == MPT2_CMD_NOT_USED) - return; + return 1; if (ioc->tm_cmds.smid != smid) - return; + return 1; ioc->tm_cmds.status |= MPT2_CMD_COMPLETE; mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (mpi_reply) { @@ -1660,6 +1664,7 @@ _scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) } ioc->tm_cmds.status &= ~MPT2_CMD_PENDING; complete(&ioc->tm_cmds.done); + return 1; } /** @@ -2311,6 +2316,231 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc, } } +/** + * _scsih_tm_tr_send - send task management request + * @ioc: per adapter object + * @handle: device handle + * Context: interrupt time. + * + * This code is to initiate the device removal handshake protocal + * with controller firmware. This function will issue target reset + * using high priority request queue. It will send a sas iounit + * controll request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion. + * + * This is designed to send muliple task management request at the same + * time to the fifo. If the fifo is full, we will append the request, + * and process it in a future completion. + */ +static void +_scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + Mpi2SCSITaskManagementRequest_t *mpi_request; + struct MPT2SAS_TARGET *sas_target_priv_data; + u16 smid; + struct _sas_device *sas_device; + unsigned long flags; + struct _tr_list *delayed_tr; + + if (ioc->shost_recovery) { + printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", + __func__, ioc->name); + return; + } + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (!sas_device) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n", + ioc->name, __func__); + return; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + /* skip is hidden raid component */ + if (sas_device->hidden_raid_component) + return; + + smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx); + if (!smid) { + delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); + if (!delayed_tr) + return; + INIT_LIST_HEAD(&delayed_tr->list); + delayed_tr->handle = handle; + delayed_tr->state = MPT2SAS_REQ_SAS_CNTRL; + list_add_tail(&delayed_tr->list, + &ioc->delayed_tr_list); + if (sas_device->starget) + dewtprintk(ioc, starget_printk(KERN_INFO, + sas_device->starget, "DELAYED:tr:handle(0x%04x), " + "(open)\n", sas_device->handle)); + return; + } + + if (sas_device->starget && sas_device->starget->hostdata) { + sas_target_priv_data = sas_device->starget->hostdata; + sas_target_priv_data->tm_busy = 1; + dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget, + "tr:handle(0x%04x), (open)\n", sas_device->handle)); + } + + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); + mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; + mpi_request->DevHandle = cpu_to_le16(handle); + mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; + sas_device->state |= MPTSAS_STATE_TR_SEND; + sas_device->state |= MPT2SAS_REQ_SAS_CNTRL; + mpt2sas_base_put_smid_hi_priority(ioc, smid); +} + + + +/** + * _scsih_sas_control_complete - completion routine + * @ioc: per adapter object + * @smid: system request message index + * @msix_index: MSIX table index supplied by the OS + * @reply: reply message frame(lower 32bit addr) + * Context: interrupt time. + * + * This is the sas iounit controll completion routine. + * This code is part of the code to initiate the device removal + * handshake protocal with controller firmware. + * + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. + */ +static u8 +_scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, + u8 msix_index, u32 reply) +{ + unsigned long flags; + u16 handle; + struct _sas_device *sas_device; + Mpi2SasIoUnitControlReply_t *mpi_reply = + mpt2sas_base_get_reply_virt_addr(ioc, reply); + + handle = le16_to_cpu(mpi_reply->DevHandle); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (!sas_device) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n", + ioc->name, __func__); + return 1; + } + sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE; + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (sas_device->starget) + dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget, + "sc_complete:handle(0x%04x), " + "ioc_status(0x%04x), loginfo(0x%08x)\n", + handle, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo))); + return 1; +} + +/** + * _scsih_tm_tr_complete - + * @ioc: per adapter object + * @smid: system request message index + * @msix_index: MSIX table index supplied by the OS + * @reply: reply message frame(lower 32bit addr) + * Context: interrupt time. + * + * This is the target reset completion routine. + * This code is part of the code to initiate the device removal + * handshake protocal with controller firmware. + * It will send a sas iounit controll request (MPI2_SAS_OP_REMOVE_DEVICE) + * + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. + */ +static u8 +_scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, + u32 reply) +{ + unsigned long flags; + u16 handle; + struct _sas_device *sas_device; + Mpi2SCSITaskManagementReply_t *mpi_reply = + mpt2sas_base_get_reply_virt_addr(ioc, reply); + Mpi2SasIoUnitControlRequest_t *mpi_request; + u16 smid_sas_ctrl; + struct MPT2SAS_TARGET *sas_target_priv_data; + struct _tr_list *delayed_tr; + u8 rc; + + handle = le16_to_cpu(mpi_reply->DevHandle); + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (!sas_device) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n", + ioc->name, __func__); + return 1; + } + sas_device->state |= MPTSAS_STATE_TR_COMPLETE; + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (sas_device->starget) + dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget, + "tr_complete:handle(0x%04x), (%s) ioc_status(0x%04x), " + "loginfo(0x%08x), completed(%d)\n", + sas_device->handle, (sas_device->state & + MPT2SAS_REQ_SAS_CNTRL) ? "open" : "active", + le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo), + le32_to_cpu(mpi_reply->TerminationCount))); + + if (sas_device->starget && sas_device->starget->hostdata) { + sas_target_priv_data = sas_device->starget->hostdata; + sas_target_priv_data->tm_busy = 0; + } + + if (!list_empty(&ioc->delayed_tr_list)) { + delayed_tr = list_entry(ioc->delayed_tr_list.next, + struct _tr_list, list); + mpt2sas_base_free_smid(ioc, smid); + if (delayed_tr->state & MPT2SAS_REQ_SAS_CNTRL) + _scsih_tm_tr_send(ioc, delayed_tr->handle); + list_del(&delayed_tr->list); + kfree(delayed_tr); + rc = 0; /* tells base_interrupt not to free mf */ + } else + rc = 1; + + + if (!(sas_device->state & MPT2SAS_REQ_SAS_CNTRL)) + return rc; + + if (ioc->shost_recovery) { + printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", + __func__, ioc->name); + return rc; + } + + smid_sas_ctrl = mpt2sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx); + if (!smid_sas_ctrl) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + return rc; + } + + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl); + memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); + mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; + mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; + mpi_request->DevHandle = mpi_reply->DevHandle; + sas_device->state |= MPTSAS_STATE_CNTRL_SEND; + mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl); + return rc; +} + /** * _scsih_check_topo_delete_events - sanity check on topo events * @ioc: per adapter object @@ -2333,6 +2563,21 @@ _scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc, u16 expander_handle; struct _sas_node *sas_expander; unsigned long flags; + int i, reason_code; + u16 handle; + + for (i = 0 ; i < event_data->NumEntries; i++) { + if (event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) + continue; + handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); + if (!handle) + continue; + reason_code = event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_RC_MASK; + if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING) + _scsih_tm_tr_send(ioc, handle); + } expander_handle = le16_to_cpu(event_data->ExpanderDevHandle); if (expander_handle < ioc->sas_hba.num_phys) { @@ -2915,11 +3160,12 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) * @msix_index: MSIX table index supplied by the OS * @reply: reply message frame(lower 32bit addr) * - * Callback handler when using scsih_qcmd. + * Callback handler when using _scsih_qcmd. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -static void +static u8 _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { Mpi2SCSIIORequest_t *mpi_request; @@ -2936,7 +3182,7 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); scmd = _scsih_scsi_lookup_get(ioc, smid); if (scmd == NULL) - return; + return 1; mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); @@ -3092,6 +3338,7 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) out: scsi_dma_unmap(scmd); scmd->scsi_done(scmd); + return 1; } /** @@ -3623,6 +3870,12 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) if (ioc->remove_host) goto out; + if ((sas_device->state & MPTSAS_STATE_TR_COMPLETE)) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " + "target_reset handle(0x%04x)\n", ioc->name, handle)); + goto skip_tr; + } + /* Target Reset to flush out all the outstanding IO */ device_handle = (sas_device->hidden_raid_component) ? sas_device->volume_handle : handle; @@ -3639,6 +3892,13 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) if (ioc->shost_recovery) goto out; } + skip_tr: + + if ((sas_device->state & MPTSAS_STATE_CNTRL_COMPLETE)) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " + "sas_cntrl handle(0x%04x)\n", ioc->name, handle)); + goto out; + } /* SAS_IO_UNIT_CNTR - send REMOVE_DEVICE */ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: handle" @@ -4829,6 +5089,10 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, if (sas_device->sas_address == sas_address && sas_device->slot == slot && sas_device->starget) { sas_device->responding = 1; + sas_device->state = 0; + starget = sas_device->starget; + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->tm_busy = 0; starget_printk(KERN_INFO, sas_device->starget, "handle(0x%04x), sas_addr(0x%016llx), enclosure " "logical id(0x%016llx), slot(%d)\n", handle, @@ -4841,8 +5105,6 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", sas_device->handle); sas_device->handle = handle; - starget = sas_device->starget; - sas_target_priv_data = starget->hostdata; sas_target_priv_data->handle = handle; goto out; } @@ -5235,9 +5497,10 @@ _firmware_event_work(struct work_struct *work) * This function merely adds a new work task into ioc->firmware_event_thread. * The tasks are worked from _firmware_event_work in user context. * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void +u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) { @@ -5250,11 +5513,11 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, spin_lock_irqsave(&ioc->fw_event_lock, flags); if (ioc->fw_events_off || ioc->remove_host) { spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - return; + return 1; } spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); event = le16_to_cpu(mpi_reply->Event); switch (event) { @@ -5268,7 +5531,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, if (baen_data->Primitive != MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT || ioc->broadcast_aen_busy) - return; + return 1; ioc->broadcast_aen_busy = 1; break; } @@ -5290,14 +5553,14 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, break; default: /* ignore the rest */ - return; + return 1; } fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); if (!fw_event) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); - return; + return 1; } fw_event->event_data = kzalloc(mpi_reply->EventDataLength*4, GFP_ATOMIC); @@ -5305,7 +5568,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); kfree(fw_event); - return; + return 1; } memcpy(fw_event->event_data, mpi_reply->EventData, @@ -5315,6 +5578,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, fw_event->VP_ID = mpi_reply->VP_ID; fw_event->event = event; _scsih_fw_event_add(ioc, fw_event); + return 1; } /* shost template */ @@ -5574,7 +5838,7 @@ _scsih_probe_raid(struct MPT2SAS_ADAPTER *ioc) } /** - * _scsih_probe_sas - reporting raid volumes to sas transport + * _scsih_probe_sas - reporting sas devices to sas transport * @ioc: per adapter object * * Called during initial loading of the driver. @@ -5671,6 +5935,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->base_cb_idx = base_cb_idx; ioc->transport_cb_idx = transport_cb_idx; ioc->config_cb_idx = config_cb_idx; + ioc->tm_tr_cb_idx = tm_tr_cb_idx; + ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx; ioc->logging_level = logging_level; /* misc semaphores and spin locks */ spin_lock_init(&ioc->ioc_reset_in_progress_lock); @@ -5686,6 +5952,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&ioc->fw_event_list); INIT_LIST_HEAD(&ioc->raid_device_list); INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list); + INIT_LIST_HEAD(&ioc->delayed_tr_list); /* init shost parameters */ shost->max_cmd_len = 16; @@ -5702,6 +5969,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE3_PROTECTION); + scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC); /* event thread */ snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name), @@ -5851,6 +6119,11 @@ _scsih_init(void) /* ctl module callback handler */ ctl_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_ctl_done); + tm_tr_cb_idx = mpt2sas_base_register_callback_handler( + _scsih_tm_tr_complete); + tm_sas_control_cb_idx = mpt2sas_base_register_callback_handler( + _scsih_sas_control_complete); + mpt2sas_ctl_init(); error = pci_register_driver(&scsih_driver); @@ -5881,6 +6154,9 @@ _scsih_exit(void) mpt2sas_base_release_callback_handler(config_cb_idx); mpt2sas_base_release_callback_handler(ctl_cb_idx); + mpt2sas_base_release_callback_handler(tm_tr_cb_idx); + mpt2sas_base_release_callback_handler(tm_sas_control_cb_idx); + mpt2sas_ctl_exit(); } diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c index 4b7201a1e867..8bc57e315ce8 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_transport.c +++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c @@ -218,9 +218,10 @@ _transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle, * Callback handler when sending internal generated transport cmds. * The callback index passed is `ioc->transport_cb_idx` * - * Return nothing. + * Return 1 meaning mf should be freed from _base_interrupt + * 0 means the mf is freed from this function. */ -void +u8 mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { @@ -228,9 +229,9 @@ mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); if (ioc->transport_cmds.status == MPT2_CMD_NOT_USED) - return; + return 1; if (ioc->transport_cmds.smid != smid) - return; + return 1; ioc->transport_cmds.status |= MPT2_CMD_COMPLETE; if (mpi_reply) { memcpy(ioc->transport_cmds.reply, mpi_reply, @@ -239,6 +240,7 @@ mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, } ioc->transport_cmds.status &= ~MPT2_CMD_PENDING; complete(&ioc->transport_cmds.done); + return 1; } /* report manufacture request structure */ -- cgit v1.2.3 From bcfb6e6ea46d84bfb541069545e5b0d7f6cc3233 Mon Sep 17 00:00:00 2001 From: "Kashyap, Desai" Date: Mon, 14 Sep 2009 11:05:24 +0530 Subject: [SCSI] mpt2sas: Call init_completion on a per request basis. Now driver call init_completion on a per request basis. At some point the wait_for_completion_timeout is not waiting for the timeout, instead returning immediately, thus going into diag reset. This fix will address all request using the wait_for_completion_timeout API. The previous implimentation was only calling init_completion at driver load time. Signed-off-by: Kashyap Desai Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_base.c | 7 ++++--- drivers/scsi/mpt2sas/mpt2sas_ctl.c | 4 ++++ drivers/scsi/mpt2sas/mpt2sas_transport.c | 2 ++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index a9c71769b4ec..f679bff9fc33 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -2614,6 +2614,7 @@ mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc, mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) ioc->ioc_link_reset_in_progress = 1; mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->base_cmds.done); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || @@ -2715,6 +2716,7 @@ mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, ioc->base_cmds.smid = smid; memcpy(request, mpi_request, sizeof(Mpi2SepReply_t)); mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->base_cmds.done); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, msecs_to_jiffies(10000)); if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { @@ -2991,6 +2993,7 @@ _base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) mpi_request->VP_ID = 0; mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->base_cmds.done); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 300*HZ); if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { @@ -3090,6 +3093,7 @@ _base_event_notification(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) mpi_request->EventMasks[i] = le32_to_cpu(ioc->event_masks[i]); mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->base_cmds.done); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", @@ -3486,7 +3490,6 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) /* base internal command bits */ mutex_init(&ioc->base_cmds.mutex); - init_completion(&ioc->base_cmds.done); ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ioc->base_cmds.status = MPT2_CMD_NOT_USED; @@ -3494,7 +3497,6 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ioc->transport_cmds.status = MPT2_CMD_NOT_USED; mutex_init(&ioc->transport_cmds.mutex); - init_completion(&ioc->transport_cmds.done); /* task management internal command bits */ ioc->tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); @@ -3510,7 +3512,6 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; mutex_init(&ioc->ctl_cmds.mutex); - init_completion(&ioc->ctl_cmds.done); for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) ioc->event_masks[i] = -1; diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index 33463ace0d48..57d724633906 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -812,6 +812,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, timeout = MPT2_IOCTL_DEFAULT_TIMEOUT; else timeout = karg.timeout; + init_completion(&ioc->ctl_cmds.done); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ); if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { @@ -1388,6 +1389,7 @@ _ctl_diag_register(void __user *arg, enum block_state state) cpu_to_le32(ioc->product_specific[buffer_type][i]); mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->ctl_cmds.done); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); @@ -1654,6 +1656,7 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset) mpi_request->VP_ID = 0; mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->ctl_cmds.done); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); @@ -1915,6 +1918,7 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) mpi_request->VP_ID = 0; mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->ctl_cmds.done); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c index 8bc57e315ce8..eb98188c7f3f 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_transport.c +++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c @@ -401,6 +401,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, "send to sas_addr(0x%016llx)\n", ioc->name, (unsigned long long)sas_address)); mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->transport_cmds.done); timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); @@ -1154,6 +1155,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, "sending smp request\n", ioc->name, __func__)); mpt2sas_base_put_smid_default(ioc, smid); + init_completion(&ioc->transport_cmds.done); timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, 10*HZ); -- cgit v1.2.3 From 29786e19cda2117e12303df67546839591d2afa7 Mon Sep 17 00:00:00 2001 From: "Kashyap, Desai" Date: Mon, 14 Sep 2009 11:06:21 +0530 Subject: [SCSI] mpt2sas: Timeout occurred within the HANDSHAKE logic while waiting on firmware to ACK. Following a diag_reset, a request to send an ioc_init is timing out. The timeout occurred within the HANDSHAKE logic while waiting on firmware to acknowledge that the driver had wrote to the doorbell register. This was root caused to a logic timeout in the firmware code. The proposed solution is for the driver to call the udelay instead of msleep API in function where its looping reading the interrupt status. In addition to this change, there were two additional cases where we deleted the clearing interrupt status outside handshake context. Signed-off-by: Kashyap Desai Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_base.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index f679bff9fc33..670241efa4b5 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -715,7 +715,6 @@ _base_unmask_interrupts(struct MPT2SAS_ADAPTER *ioc) { u32 him_register; - writel(0, &ioc->chip->HostInterruptStatus); him_register = readl(&ioc->chip->HostInterruptMask); him_register &= ~MPI2_HIM_RIM; writel(him_register, &ioc->chip->HostInterruptMask); @@ -2463,7 +2462,7 @@ _base_handshake_req_reply_wait(struct MPT2SAS_ADAPTER *ioc, int request_bytes, ((request_bytes/4)<chip->Doorbell); - if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { + if ((_base_wait_for_doorbell_int(ioc, 5, NO_SLEEP))) { printk(MPT2SAS_ERR_FMT "doorbell handshake " "int failed (line=%d)\n", ioc->name, __LINE__); return -EFAULT; @@ -3169,7 +3168,6 @@ _base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "clear interrupts\n", ioc->name)); - writel(0, &ioc->chip->HostInterruptStatus); count = 0; do { -- cgit v1.2.3 From 308609c63c6d6144c1210c9583c68bc532acb8d2 Mon Sep 17 00:00:00 2001 From: "Kashyap, Desai" Date: Mon, 14 Sep 2009 11:07:23 +0530 Subject: [SCSI] mpt2sas: Support dev remove when phy status is MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT Add support to process device removal events when the phy status is set to MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT. Signed-off-by: Kashyap Desai Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 9ec50729d535..86ab32d7ab15 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -4067,15 +4067,16 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, } if (ioc->shost_recovery) return; - if (event_data->PHY[i].PhyStatus & - MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) + phy_number = event_data->StartPhyNum + i; + reason_code = event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_RC_MASK; + if ((event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) && (reason_code != + MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING)) continue; handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); if (!handle) continue; - phy_number = event_data->StartPhyNum + i; - reason_code = event_data->PHY[i].PhyStatus & - MPI2_EVENT_SAS_TOPO_RC_MASK; link_rate_ = event_data->PHY[i].LinkRate >> 4; switch (reason_code) { case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: -- cgit v1.2.3 From dac3bbd643cd54e2383e88b4f0f86db68c921701 Mon Sep 17 00:00:00 2001 From: "Kashyap, Desai" Date: Mon, 14 Sep 2009 11:08:13 +0530 Subject: [SCSI] mpt2sas: Bump version 02.100.03.00 Bump version to 02.100.03.00 Signed-off-by: Kashyap Desai Signed-off-by: James Bottomley --- drivers/scsi/mpt2sas/mpt2sas_base.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index 7f94be90ad12..0cf6bc236e4d 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -69,10 +69,10 @@ #define MPT2SAS_DRIVER_NAME "mpt2sas" #define MPT2SAS_AUTHOR "LSI Corporation " #define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" -#define MPT2SAS_DRIVER_VERSION "01.100.06.00" -#define MPT2SAS_MAJOR_VERSION 01 +#define MPT2SAS_DRIVER_VERSION "02.100.03.00" +#define MPT2SAS_MAJOR_VERSION 02 #define MPT2SAS_MINOR_VERSION 100 -#define MPT2SAS_BUILD_VERSION 06 +#define MPT2SAS_BUILD_VERSION 03 #define MPT2SAS_RELEASE_VERSION 00 /* -- cgit v1.2.3 From b1a58985853574346f1518531bdd82069501b317 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Thu, 24 Sep 2009 10:23:21 +0200 Subject: [SCSI] zfcp: correctly initialize unchained requests The common initialization of ct/gs and els requests missed the initialization of unchained requests. Fix this by moving the common parts to a place that is called for all ct/gs and els requests. Reviewed-by: Felix Beck Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_fsf.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index f09c863dc6bd..38a7e4a6b639 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1058,11 +1058,25 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->queue_req, SBAL_FLAGS0_TYPE_WRITE_READ, sg_resp, max_sbals); + req->qtcb->bottom.support.resp_buf_length = bytes; if (bytes <= 0) return -EIO; + return 0; +} + +static int zfcp_fsf_setup_ct_els(struct zfcp_fsf_req *req, + struct scatterlist *sg_req, + struct scatterlist *sg_resp, + int max_sbals) +{ + int ret; + + ret = zfcp_fsf_setup_ct_els_sbals(req, sg_req, sg_resp, max_sbals); + if (ret) + return ret; + /* common settings for ct/gs and els requests */ - req->qtcb->bottom.support.resp_buf_length = bytes; req->qtcb->bottom.support.service_class = FSF_CLASS_3; req->qtcb->bottom.support.timeout = 2 * R_A_TOV; zfcp_fsf_start_timer(req, 2 * R_A_TOV + 10); @@ -1094,8 +1108,8 @@ int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - ret = zfcp_fsf_setup_ct_els_sbals(req, ct->req, ct->resp, - FSF_MAX_SBALS_PER_REQ); + ret = zfcp_fsf_setup_ct_els(req, ct->req, ct->resp, + FSF_MAX_SBALS_PER_REQ); if (ret) goto failed_send; @@ -1192,7 +1206,7 @@ int zfcp_fsf_send_els(struct zfcp_send_els *els) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - ret = zfcp_fsf_setup_ct_els_sbals(req, els->req, els->resp, 2); + ret = zfcp_fsf_setup_ct_els(req, els->req, els->resp, 2); if (ret) goto failed_send; -- cgit v1.2.3 From c5afd81e5d7ebacc9dd23954e169ac79a34fe399 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Thu, 24 Sep 2009 10:23:22 +0200 Subject: [SCSI] zfcp: Fix initial device and cfdc for delayed adapter allocation With the change for delaying the allocation of zfcp_adapter, the initial device parameter function has to first call ccw_device_set_online which allocates the zfcp_adapter structure. Change this and adapt the cfdc part accordingly. Reviewed-by: Felix Beck Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 31 ++++++++++++++++++++----------- drivers/s390/scsi/zfcp_ccw.c | 19 +------------------ drivers/s390/scsi/zfcp_cfdc.c | 17 ++++++++++++++++- drivers/s390/scsi/zfcp_ext.h | 2 +- 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 1be6bf7e8ce6..351d2e711ec5 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -80,28 +80,35 @@ int zfcp_reqlist_isempty(struct zfcp_adapter *adapter) static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) { + struct ccw_device *ccwdev; struct zfcp_adapter *adapter; struct zfcp_port *port; struct zfcp_unit *unit; - mutex_lock(&zfcp_data.config_mutex); - read_lock_irq(&zfcp_data.config_lock); - adapter = zfcp_get_adapter_by_busid(busid); - if (adapter) - zfcp_adapter_get(adapter); - read_unlock_irq(&zfcp_data.config_lock); + ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); + if (!ccwdev) + return; + + if (ccw_device_set_online(ccwdev)) + goto out_ccwdev; + mutex_lock(&zfcp_data.config_mutex); + adapter = dev_get_drvdata(&ccwdev->dev); if (!adapter) - goto out_adapter; - port = zfcp_port_enqueue(adapter, wwpn, 0, 0); - if (IS_ERR(port)) + goto out_unlock; + zfcp_adapter_get(adapter); + + port = zfcp_get_port_by_wwpn(adapter, wwpn); + if (!port) goto out_port; + + zfcp_port_get(port); unit = zfcp_unit_enqueue(port, lun); if (IS_ERR(unit)) goto out_unit; mutex_unlock(&zfcp_data.config_mutex); - ccw_device_set_online(adapter->ccw_device); + zfcp_erp_unit_reopen(unit, 0, "auidc_1", NULL); zfcp_erp_wait(adapter); flush_work(&unit->scsi_work); @@ -111,8 +118,10 @@ out_unit: zfcp_port_put(port); out_port: zfcp_adapter_put(adapter); -out_adapter: +out_unlock: mutex_unlock(&zfcp_data.config_mutex); +out_ccwdev: + put_device(&ccwdev->dev); return; } diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index 0c90f8e71605..ef5282dcdbb4 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -259,7 +259,7 @@ static void zfcp_ccw_shutdown(struct ccw_device *cdev) mutex_unlock(&zfcp_data.config_mutex); } -static struct ccw_driver zfcp_ccw_driver = { +struct ccw_driver zfcp_ccw_driver = { .owner = THIS_MODULE, .name = "zfcp", .ids = zfcp_ccw_device_id, @@ -284,20 +284,3 @@ int __init zfcp_ccw_register(void) { return ccw_driver_register(&zfcp_ccw_driver); } - -/** - * zfcp_get_adapter_by_busid - find zfcp_adapter struct - * @busid: bus id string of zfcp adapter to find - */ -struct zfcp_adapter *zfcp_get_adapter_by_busid(char *busid) -{ - struct ccw_device *ccw_device; - struct zfcp_adapter *adapter = NULL; - - ccw_device = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); - if (ccw_device) { - adapter = dev_get_drvdata(&ccw_device->dev); - put_device(&ccw_device->dev); - } - return adapter; -} diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c index 8305c874e86f..ef681dfed0cc 100644 --- a/drivers/s390/scsi/zfcp_cfdc.c +++ b/drivers/s390/scsi/zfcp_cfdc.c @@ -86,8 +86,23 @@ static int zfcp_cfdc_copy_to_user(void __user *user_buffer, static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno) { char busid[9]; + struct ccw_device *ccwdev; + struct zfcp_adapter *adapter = NULL; + snprintf(busid, sizeof(busid), "0.0.%04x", devno); - return zfcp_get_adapter_by_busid(busid); + ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); + if (!ccwdev) + goto out; + + adapter = dev_get_drvdata(&ccwdev->dev); + if (!adapter) + goto out_put; + + zfcp_adapter_get(adapter); +out_put: + put_device(&ccwdev->dev); +out: + return adapter; } static int zfcp_cfdc_set_fsf(struct zfcp_fsf_cfdc *fsf_cfdc, int command) diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 36935bc0818f..629edec70405 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -28,7 +28,7 @@ extern int zfcp_sg_setup_table(struct scatterlist *, int); /* zfcp_ccw.c */ extern int zfcp_ccw_register(void); extern int zfcp_ccw_priv_sch(struct zfcp_adapter *); -extern struct zfcp_adapter *zfcp_get_adapter_by_busid(char *); +extern struct ccw_driver zfcp_ccw_driver; /* zfcp_cfdc.c */ extern struct miscdevice zfcp_cfdc_misc; -- cgit v1.2.3 From 1f99bd4cbfa50be144aee24905befc1209d90d47 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Thu, 24 Sep 2009 10:23:23 +0200 Subject: [SCSI] zfcp: Fix oops during shutdown of offline device With the change that the zfcp_adapter struct is only allocated when the device is set online, the shutdown handler has to check for a non-existing zfcp_adapter struct. On the other hand, this check is not necessary in the offline callback, since an online device has the zfcp_adapter allocated and we go through the offline callback before removing the ccw device. Reviewed-by: Felix Beck Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_ccw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index ef5282dcdbb4..95644b71836e 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -192,13 +192,9 @@ static int zfcp_ccw_set_offline(struct ccw_device *ccw_device) mutex_lock(&zfcp_data.config_mutex); adapter = dev_get_drvdata(&ccw_device->dev); - if (!adapter) - goto out; - zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1", NULL); zfcp_erp_wait(adapter); mutex_unlock(&zfcp_data.config_mutex); -out: return 0; } @@ -253,9 +249,13 @@ static void zfcp_ccw_shutdown(struct ccw_device *cdev) mutex_lock(&zfcp_data.config_mutex); adapter = dev_get_drvdata(&cdev->dev); + if (!adapter) + goto out; + zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1", NULL); zfcp_erp_wait(adapter); zfcp_erp_thread_kill(adapter); +out: mutex_unlock(&zfcp_data.config_mutex); } -- cgit v1.2.3 From f45a54214a0770582af06bb1e807493d1b7268ab Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Thu, 24 Sep 2009 10:23:24 +0200 Subject: [SCSI] zfcp: Fix lockdep warning when offlining device with offline chpid ======================================================= [ INFO: possible circular locking dependency detected ] 2.6.31-39.x.20090917-s390xdefault #1 ------------------------------------------------------- kslowcrw/83 is trying to acquire lock: (&adapter->scan_work){+.+.+.}, at: [<0000000000169c5c>] __cancel_work_timer+0x64/0x3d4 but task is already holding lock: (&zfcp_data.config_mutex){+.+.+.}, at: [<00000000004671ea>] zfcp_ccw_remove+0x66/0x384 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (&zfcp_data.config_mutex){+.+.+.}: [<0000000000189962>] __lock_acquire+0xe26/0x1834 [<000000000018a4b6>] lock_acquire+0x146/0x178 [<000000000058cb5a>] mutex_lock_nested+0x82/0x3ec [<0000000000477170>] zfcp_fc_scan_ports+0x3ec/0x728 [<0000000000168e34>] worker_thread+0x278/0x3a8 [<000000000016ff08>] kthread+0x9c/0xa4 [<0000000000109ebe>] kernel_thread_starter+0x6/0xc [<0000000000109eb8>] kernel_thread_starter+0x0/0xc -> #0 (&adapter->scan_work){+.+.+.}: [<0000000000189e60>] __lock_acquire+0x1324/0x1834 [<000000000018a4b6>] lock_acquire+0x146/0x178 [<0000000000169c9a>] __cancel_work_timer+0xa2/0x3d4 [<0000000000465cb2>] zfcp_adapter_dequeue+0x32/0x14c [<00000000004673e4>] zfcp_ccw_remove+0x260/0x384 [<00000000004250f6>] ccw_device_remove+0x42/0x1ac [<00000000003cb6be>] __device_release_driver+0x9a/0x10c [<00000000003cb856>] device_release_driver+0x3a/0x4c [<00000000003ca94c>] bus_remove_device+0xcc/0x114 [<00000000003c8506>] device_del+0x162/0x21c [<0000000000425ff2>] ccw_device_unregister+0x5e/0x7c [<000000000042607e>] io_subchannel_remove+0x6e/0x9c [<000000000041ff9a>] css_remove+0x3e/0x7c [<00000000003cb6be>] __device_release_driver+0x9a/0x10c [<00000000003cb856>] device_release_driver+0x3a/0x4c [<00000000003ca94c>] bus_remove_device+0xcc/0x114 [<00000000003c8506>] device_del+0x162/0x21c [<00000000003c85e8>] device_unregister+0x28/0x38 [<0000000000420152>] css_sch_device_unregister+0x46/0x58 [<00000000004276a6>] io_subchannel_sch_event+0x28e/0x794 [<0000000000420442>] css_evaluate_known_subchannel+0x46/0xd0 [<0000000000420ebc>] slow_eval_known_fn+0x88/0xa0 [<00000000003caffa>] bus_for_each_dev+0x7e/0xd0 [<000000000042188c>] for_each_subchannel_staged+0x6c/0xd4 [<0000000000421a00>] css_slow_path_func+0x54/0xd8 [<0000000000168e34>] worker_thread+0x278/0x3a8 [<000000000016ff08>] kthread+0x9c/0xa4 [<0000000000109ebe>] kernel_thread_starter+0x6/0xc [<0000000000109eb8>] kernel_thread_starter+0x0/0xc cancel_work_sync is called while holding the config_mutex. But the work that is being cancelled or flushed also uses the config_mutex. Fix the resulting deadlock possibility by calling cancel_work_sync earlier without holding the mutex. The best place to do is is after offlining the device. No new port scan work will be scheduled for the offline device, so this is a safe place to call cancel_work_sync. Reviewed-by: Felix Beck Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 1 - drivers/s390/scsi/zfcp_ccw.c | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 351d2e711ec5..5dcac24a7a1b 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -602,7 +602,6 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter) int retval = 0; unsigned long flags; - cancel_work_sync(&adapter->scan_work); cancel_work_sync(&adapter->stat_work); zfcp_fc_wka_ports_force_offline(adapter->gs); zfcp_adapter_scsi_unregister(adapter); diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index 95644b71836e..9fe32f7ec8d2 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -102,7 +102,11 @@ static void zfcp_ccw_remove(struct ccw_device *ccw_device) adapter = dev_get_drvdata(&ccw_device->dev); if (!adapter) goto out; + mutex_unlock(&zfcp_data.config_mutex); + + cancel_work_sync(&adapter->scan_work); + mutex_lock(&zfcp_data.config_mutex); write_lock_irq(&zfcp_data.config_lock); list_for_each_entry_safe(port, p, &adapter->port_list_head, list) { list_for_each_entry_safe(unit, u, &port->unit_list_head, list) { -- cgit v1.2.3 From d74cf7c3e9c4a6a659e0442aafb550b162d15e72 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Thu, 24 Sep 2009 10:23:25 +0200 Subject: [SCSI] zfcp: Fix hang when offlining device with offline chpid Running chchp --vary 0 and chccwdev -d on a FCP device with scsi devices attached can lead to this thread hanging: ================================================================ STACK TRACE FOR TASK: 0x2fbfcc00 (kslowcrw) STACK: 0 schedule+1136 [0x45f99c] 1 schedule_timeout+534 [0x46054e] 2 wait_for_common+374 [0x45f442] 3 blk_execute_rq+160 [0x217a2c] 4 scsi_execute+278 [0x26daf2] 5 scsi_execute_req+150 [0x26dc86] 6 sd_sync_cache+138 [0x28460a] 7 sd_shutdown+130 [0x28486a] 8 sd_remove+104 [0x284c84] 9 __device_release_driver+152 [0x257430] 10 device_release_driver+56 [0x2575c8] 11 bus_remove_device+214 [0x25672a] 12 device_del+352 [0x25456c] 13 __scsi_remove_device+108 [0x272630] 14 scsi_remove_device+66 [0x2726ba] 15 zfcp_ccw_remove+824 [0x335558] 16 ccw_device_remove+62 [0x2b3f2a] 17 __device_release_driver+152 [0x257430] 18 device_release_driver+56 [0x2575c8] 19 bus_remove_device+214 [0x25672a] 20 device_del+352 [0x25456c] 21 ccw_device_unregister+92 [0x2b48c4] 22 io_subchannel_remove+108 [0x2b4950] 23 css_remove+62 [0x2af7ee] 24 __device_release_driver+152 [0x257430] 25 device_release_driver+56 [0x2575c8] 26 bus_remove_device+214 [0x25672a] 27 device_del+352 [0x25456c] 28 device_unregister+38 [0x25464a] 29 css_sch_device_unregister+68 [0x2af97c] 30 ccw_device_call_sch_unregister+78 [0x2b581e] 31 worker_thread+604 [0x69eb0] 32 kthread+154 [0x6ff42] 33 kernel_thread_starter+6 [0x1c952] ================================================================ The problem is that the chchp --vary 0 leads to zfcp first calling fc_remote_port_delete which blocks all scsi devices on the remote port. Calling scsi_remove_device later lets the sd driver issue a SYNCHRONIZE_CACHE command. This command stays on the "stopped" request requeue because the SCSI device is blocked. Fix this by first removing the scsi and fc hosts which removes all scsi devices and do not use scsi_remove_device. Reviewed-by: Felix Beck Signed-off-by: Christof Schmitt Signed-off-by: James Bottomley --- drivers/s390/scsi/zfcp_aux.c | 1 - drivers/s390/scsi/zfcp_ccw.c | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 5dcac24a7a1b..0f79f3af4f54 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -604,7 +604,6 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter) cancel_work_sync(&adapter->stat_work); zfcp_fc_wka_ports_force_offline(adapter->gs); - zfcp_adapter_scsi_unregister(adapter); sysfs_remove_group(&adapter->ccw_device->dev.kobj, &zfcp_sysfs_adapter_attrs); dev_set_drvdata(&adapter->ccw_device->dev, NULL); diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index 9fe32f7ec8d2..e08339428ecf 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -107,6 +107,10 @@ static void zfcp_ccw_remove(struct ccw_device *ccw_device) cancel_work_sync(&adapter->scan_work); mutex_lock(&zfcp_data.config_mutex); + + /* this also removes the scsi devices, so call it first */ + zfcp_adapter_scsi_unregister(adapter); + write_lock_irq(&zfcp_data.config_lock); list_for_each_entry_safe(port, p, &adapter->port_list_head, list) { list_for_each_entry_safe(unit, u, &port->unit_list_head, list) { @@ -121,11 +125,8 @@ static void zfcp_ccw_remove(struct ccw_device *ccw_device) write_unlock_irq(&zfcp_data.config_lock); list_for_each_entry_safe(port, p, &port_remove_lh, list) { - list_for_each_entry_safe(unit, u, &unit_remove_lh, list) { - if (unit->device) - scsi_remove_device(unit->device); + list_for_each_entry_safe(unit, u, &unit_remove_lh, list) zfcp_unit_dequeue(unit); - } zfcp_port_dequeue(port); } wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0); -- cgit v1.2.3 From 6733b39a1301b0b020bbcbf3295852e93e624cb1 Mon Sep 17 00:00:00 2001 From: Jayamohan Kallickal Date: Sat, 5 Sep 2009 07:36:35 +0530 Subject: [SCSI] be2iscsi: add 10Gbps iSCSI - BladeEngine 2 driver [v2: fixed up virt_to_bus() issue spotted by sfr] Signed-off-by: Mike Christie Signed-off-by: Jayamohan Kallickal Signed-off-by: James Bottomley --- MAINTAINERS | 8 + drivers/scsi/Kconfig | 1 + drivers/scsi/Makefile | 1 + drivers/scsi/be2iscsi/Kconfig | 8 + drivers/scsi/be2iscsi/Makefile | 8 + drivers/scsi/be2iscsi/be.h | 183 ++ drivers/scsi/be2iscsi/be_cmds.c | 523 ++++++ drivers/scsi/be2iscsi/be_cmds.h | 877 ++++++++++ drivers/scsi/be2iscsi/be_iscsi.c | 646 ++++++++ drivers/scsi/be2iscsi/be_iscsi.h | 75 + drivers/scsi/be2iscsi/be_main.c | 3393 ++++++++++++++++++++++++++++++++++++++ drivers/scsi/be2iscsi/be_main.h | 833 ++++++++++ drivers/scsi/be2iscsi/be_mgmt.c | 321 ++++ drivers/scsi/be2iscsi/be_mgmt.h | 249 +++ 14 files changed, 7126 insertions(+) create mode 100644 drivers/scsi/be2iscsi/Kconfig create mode 100644 drivers/scsi/be2iscsi/Makefile create mode 100644 drivers/scsi/be2iscsi/be.h create mode 100644 drivers/scsi/be2iscsi/be_cmds.c create mode 100644 drivers/scsi/be2iscsi/be_cmds.h create mode 100644 drivers/scsi/be2iscsi/be_iscsi.c create mode 100644 drivers/scsi/be2iscsi/be_iscsi.h create mode 100644 drivers/scsi/be2iscsi/be_main.c create mode 100644 drivers/scsi/be2iscsi/be_main.h create mode 100644 drivers/scsi/be2iscsi/be_mgmt.c create mode 100644 drivers/scsi/be2iscsi/be_mgmt.h diff --git a/MAINTAINERS b/MAINTAINERS index c233f6fe0fff..db6c47b3ec5a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4625,6 +4625,14 @@ F: drivers/ata/ F: include/linux/ata.h F: include/linux/libata.h +SERVER ENGINES 10Gbps iSCSI - BladeEngine 2 DRIVER +P: Jayamohan Kallickal +M: jayamohank@serverengines.com +L: linux-scsi@vger.kernel.org +W: http://www.serverengines.com +S: Supported +F: drivers/scsi/be2iscsi/ + SERVER ENGINES 10Gbps NIC - BladeEngine 2 DRIVER M: Sathya Perla M: Subbu Seetharaman diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index a8ea01b07868..e11cca4c784c 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -366,6 +366,7 @@ config ISCSI_TCP source "drivers/scsi/cxgb3i/Kconfig" source "drivers/scsi/bnx2i/Kconfig" +source "drivers/scsi/be2iscsi/Kconfig" config SGIWD93_SCSI tristate "SGI WD93C93 SCSI Driver" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 316f0a1a7986..3ad61db5e3fa 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -131,6 +131,7 @@ obj-$(CONFIG_SCSI_MVSAS) += mvsas/ obj-$(CONFIG_PS3_ROM) += ps3rom.o obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgb3i/ obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/ +obj-$(CONFIG_BE2ISCSI) += libiscsi.o be2iscsi/ obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o obj-$(CONFIG_ARM) += arm/ diff --git a/drivers/scsi/be2iscsi/Kconfig b/drivers/scsi/be2iscsi/Kconfig new file mode 100644 index 000000000000..2952fcd008ea --- /dev/null +++ b/drivers/scsi/be2iscsi/Kconfig @@ -0,0 +1,8 @@ +config BE2ISCSI + tristate "ServerEngines' 10Gbps iSCSI - BladeEngine 2" + depends on PCI && SCSI + select SCSI_ISCSI_ATTRS + + help + This driver implements the iSCSI functionality for ServerEngines' + 10Gbps Storage adapter - BladeEngine 2. diff --git a/drivers/scsi/be2iscsi/Makefile b/drivers/scsi/be2iscsi/Makefile new file mode 100644 index 000000000000..c11f443e3f83 --- /dev/null +++ b/drivers/scsi/be2iscsi/Makefile @@ -0,0 +1,8 @@ +# +# Makefile to build the iSCSI driver for ServerEngine's BladeEngine. +# +# + +obj-$(CONFIG_BE2ISCSI) += be2iscsi.o + +be2iscsi-y := be_iscsi.o be_main.o be_mgmt.o be_cmds.o diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h new file mode 100644 index 000000000000..b36020dcf012 --- /dev/null +++ b/drivers/scsi/be2iscsi/be.h @@ -0,0 +1,183 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + */ + +#ifndef BEISCSI_H +#define BEISCSI_H + +#include +#include + +#define FW_VER_LEN 32 + +struct be_dma_mem { + void *va; + dma_addr_t dma; + u32 size; +}; + +struct be_queue_info { + struct be_dma_mem dma_mem; + u16 len; + u16 entry_size; /* Size of an element in the queue */ + u16 id; + u16 tail, head; + bool created; + atomic_t used; /* Number of valid elements in the queue */ +}; + +static inline u32 MODULO(u16 val, u16 limit) +{ + WARN_ON(limit & (limit - 1)); + return val & (limit - 1); +} + +static inline void index_inc(u16 *index, u16 limit) +{ + *index = MODULO((*index + 1), limit); +} + +static inline void *queue_head_node(struct be_queue_info *q) +{ + return q->dma_mem.va + q->head * q->entry_size; +} + +static inline void *queue_tail_node(struct be_queue_info *q) +{ + return q->dma_mem.va + q->tail * q->entry_size; +} + +static inline void queue_head_inc(struct be_queue_info *q) +{ + index_inc(&q->head, q->len); +} + +static inline void queue_tail_inc(struct be_queue_info *q) +{ + index_inc(&q->tail, q->len); +} + +/*ISCSI */ + +struct be_eq_obj { + struct be_queue_info q; + char desc[32]; + + /* Adaptive interrupt coalescing (AIC) info */ + bool enable_aic; + u16 min_eqd; /* in usecs */ + u16 max_eqd; /* in usecs */ + u16 cur_eqd; /* in usecs */ +}; + +struct be_mcc_obj { + struct be_queue_info *q; + struct be_queue_info *cq; +}; + +struct be_ctrl_info { + u8 __iomem *csr; + u8 __iomem *db; /* Door Bell */ + u8 __iomem *pcicfg; /* PCI config space */ + struct pci_dev *pdev; + + /* Mbox used for cmd request/response */ + spinlock_t mbox_lock; /* For serializing mbox cmds to BE card */ + struct be_dma_mem mbox_mem; + /* Mbox mem is adjusted to align to 16 bytes. The allocated addr + * is stored for freeing purpose */ + struct be_dma_mem mbox_mem_alloced; + + /* MCC Rings */ + struct be_mcc_obj mcc_obj; + spinlock_t mcc_lock; /* For serializing mcc cmds to BE card */ + spinlock_t mcc_cq_lock; + + /* MCC Async callback */ + void (*async_cb) (void *adapter, bool link_up); + void *adapter_ctxt; +}; + +#include "be_cmds.h" + +#define PAGE_SHIFT_4K 12 +#define PAGE_SIZE_4K (1 << PAGE_SHIFT_4K) + +/* Returns number of pages spanned by the data starting at the given addr */ +#define PAGES_4K_SPANNED(_address, size) \ + ((u32)((((size_t)(_address) & (PAGE_SIZE_4K - 1)) + \ + (size) + (PAGE_SIZE_4K - 1)) >> PAGE_SHIFT_4K)) + +/* Byte offset into the page corresponding to given address */ +#define OFFSET_IN_PAGE(addr) \ + ((size_t)(addr) & (PAGE_SIZE_4K-1)) + +/* Returns bit offset within a DWORD of a bitfield */ +#define AMAP_BIT_OFFSET(_struct, field) \ + (((size_t)&(((_struct *)0)->field))%32) + +/* Returns the bit mask of the field that is NOT shifted into location. */ +static inline u32 amap_mask(u32 bitsize) +{ + return (bitsize == 32 ? 0xFFFFFFFF : (1 << bitsize) - 1); +} + +static inline void amap_set(void *ptr, u32 dw_offset, u32 mask, + u32 offset, u32 value) +{ + u32 *dw = (u32 *) ptr + dw_offset; + *dw &= ~(mask << offset); + *dw |= (mask & value) << offset; +} + +#define AMAP_SET_BITS(_struct, field, ptr, val) \ + amap_set(ptr, \ + offsetof(_struct, field)/32, \ + amap_mask(sizeof(((_struct *)0)->field)), \ + AMAP_BIT_OFFSET(_struct, field), \ + val) + +static inline u32 amap_get(void *ptr, u32 dw_offset, u32 mask, u32 offset) +{ + u32 *dw = ptr; + return mask & (*(dw + dw_offset) >> offset); +} + +#define AMAP_GET_BITS(_struct, field, ptr) \ + amap_get(ptr, \ + offsetof(_struct, field)/32, \ + amap_mask(sizeof(((_struct *)0)->field)), \ + AMAP_BIT_OFFSET(_struct, field)) + +#define be_dws_cpu_to_le(wrb, len) swap_dws(wrb, len) +#define be_dws_le_to_cpu(wrb, len) swap_dws(wrb, len) +static inline void swap_dws(void *wrb, int len) +{ +#ifdef __BIG_ENDIAN + u32 *dw = wrb; + WARN_ON(len % 4); + do { + *dw = cpu_to_le32(*dw); + dw++; + len -= 4; + } while (len); +#endif /* __BIG_ENDIAN */ +} + +extern void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm, + u16 num_popped); + +#endif /* BEISCSI_H */ diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c new file mode 100644 index 000000000000..08007b6e42df --- /dev/null +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -0,0 +1,523 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + */ + +#include "be.h" +#include "be_mgmt.h" +#include "be_main.h" + +static inline bool be_mcc_compl_is_new(struct be_mcc_compl *compl) +{ + if (compl->flags != 0) { + compl->flags = le32_to_cpu(compl->flags); + WARN_ON((compl->flags & CQE_FLAGS_VALID_MASK) == 0); + return true; + } else + return false; +} + +static inline void be_mcc_compl_use(struct be_mcc_compl *compl) +{ + compl->flags = 0; +} + +static int be_mcc_compl_process(struct be_ctrl_info *ctrl, + struct be_mcc_compl *compl) +{ + u16 compl_status, extd_status; + + be_dws_le_to_cpu(compl, 4); + + compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) & + CQE_STATUS_COMPL_MASK; + if (compl_status != MCC_STATUS_SUCCESS) { + extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) & + CQE_STATUS_EXTD_MASK; + dev_err(&ctrl->pdev->dev, + "error in cmd completion: status(compl/extd)=%d/%d\n", + compl_status, extd_status); + return -1; + } + return 0; +} + +static inline bool is_link_state_evt(u32 trailer) +{ + return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) & + ASYNC_TRAILER_EVENT_CODE_MASK) == ASYNC_EVENT_CODE_LINK_STATE); +} + +void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm, + u16 num_popped) +{ + u32 val = 0; + val |= qid & DB_CQ_RING_ID_MASK; + if (arm) + val |= 1 << DB_CQ_REARM_SHIFT; + val |= num_popped << DB_CQ_NUM_POPPED_SHIFT; + iowrite32(val, ctrl->db + DB_CQ_OFFSET); +} + +static int be_mbox_db_ready_wait(struct be_ctrl_info *ctrl) +{ +#define long_delay 2000 + void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET; + int cnt = 0, wait = 5; /* in usecs */ + u32 ready; + + do { + ready = ioread32(db) & MPU_MAILBOX_DB_RDY_MASK; + if (ready) + break; + + if (cnt > 6000000) { + dev_err(&ctrl->pdev->dev, "mbox_db poll timed out\n"); + return -1; + } + + if (cnt > 50) { + wait = long_delay; + mdelay(long_delay / 1000); + } else + udelay(wait); + cnt += wait; + } while (true); + return 0; +} + +int be_mbox_notify(struct be_ctrl_info *ctrl) +{ + int status; + u32 val = 0; + void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET; + struct be_dma_mem *mbox_mem = &ctrl->mbox_mem; + struct be_mcc_mailbox *mbox = mbox_mem->va; + struct be_mcc_compl *compl = &mbox->compl; + + val &= ~MPU_MAILBOX_DB_RDY_MASK; + val |= MPU_MAILBOX_DB_HI_MASK; + val |= (upper_32_bits(mbox_mem->dma) >> 2) << 2; + iowrite32(val, db); + + status = be_mbox_db_ready_wait(ctrl); + if (status != 0) { + SE_DEBUG(DBG_LVL_1, " be_mbox_db_ready_wait failed 1\n"); + return status; + } + val = 0; + val &= ~MPU_MAILBOX_DB_RDY_MASK; + val &= ~MPU_MAILBOX_DB_HI_MASK; + val |= (u32) (mbox_mem->dma >> 4) << 2; + iowrite32(val, db); + + status = be_mbox_db_ready_wait(ctrl); + if (status != 0) { + SE_DEBUG(DBG_LVL_1, " be_mbox_db_ready_wait failed 2\n"); + return status; + } + if (be_mcc_compl_is_new(compl)) { + status = be_mcc_compl_process(ctrl, &mbox->compl); + be_mcc_compl_use(compl); + if (status) { + SE_DEBUG(DBG_LVL_1, "After be_mcc_compl_process \n"); + return status; + } + } else { + dev_err(&ctrl->pdev->dev, "invalid mailbox completion\n"); + return -1; + } + return 0; +} + +void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len, + bool embedded, u8 sge_cnt) +{ + if (embedded) + wrb->embedded |= MCC_WRB_EMBEDDED_MASK; + else + wrb->embedded |= (sge_cnt & MCC_WRB_SGE_CNT_MASK) << + MCC_WRB_SGE_CNT_SHIFT; + wrb->payload_length = payload_len; + be_dws_cpu_to_le(wrb, 8); +} + +void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr, + u8 subsystem, u8 opcode, int cmd_len) +{ + req_hdr->opcode = opcode; + req_hdr->subsystem = subsystem; + req_hdr->request_length = cpu_to_le32(cmd_len - sizeof(*req_hdr)); +} + +static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages, + struct be_dma_mem *mem) +{ + int i, buf_pages; + u64 dma = (u64) mem->dma; + + buf_pages = min(PAGES_4K_SPANNED(mem->va, mem->size), max_pages); + for (i = 0; i < buf_pages; i++) { + pages[i].lo = cpu_to_le32(dma & 0xFFFFFFFF); + pages[i].hi = cpu_to_le32(upper_32_bits(dma)); + dma += PAGE_SIZE_4K; + } +} + +static u32 eq_delay_to_mult(u32 usec_delay) +{ +#define MAX_INTR_RATE 651042 + const u32 round = 10; + u32 multiplier; + + if (usec_delay == 0) + multiplier = 0; + else { + u32 interrupt_rate = 1000000 / usec_delay; + if (interrupt_rate == 0) + multiplier = 1023; + else { + multiplier = (MAX_INTR_RATE - interrupt_rate) * round; + multiplier /= interrupt_rate; + multiplier = (multiplier + round / 2) / round; + multiplier = min(multiplier, (u32) 1023); + } + } + return multiplier; +} + +struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem) +{ + return &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb; +} + +int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *eq, int eq_delay) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_cmd_req_eq_create *req = embedded_payload(wrb); + struct be_cmd_resp_eq_create *resp = embedded_payload(wrb); + struct be_dma_mem *q_mem = &eq->dma_mem; + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_EQ_CREATE, sizeof(*req)); + + req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size)); + + AMAP_SET_BITS(struct amap_eq_context, func, req->context, + PCI_FUNC(ctrl->pdev->devfn)); + AMAP_SET_BITS(struct amap_eq_context, valid, req->context, 1); + AMAP_SET_BITS(struct amap_eq_context, size, req->context, 0); + AMAP_SET_BITS(struct amap_eq_context, count, req->context, + __ilog2_u32(eq->len / 256)); + AMAP_SET_BITS(struct amap_eq_context, delaymult, req->context, + eq_delay_to_mult(eq_delay)); + be_dws_cpu_to_le(req->context, sizeof(req->context)); + + be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); + + status = be_mbox_notify(ctrl); + if (!status) { + eq->id = le16_to_cpu(resp->eq_id); + eq->created = true; + } + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int be_cmd_fw_initialize(struct be_ctrl_info *ctrl) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + int status; + u8 *endian_check; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + endian_check = (u8 *) wrb; + *endian_check++ = 0xFF; + *endian_check++ = 0x12; + *endian_check++ = 0x34; + *endian_check++ = 0xFF; + *endian_check++ = 0xFF; + *endian_check++ = 0x56; + *endian_check++ = 0x78; + *endian_check++ = 0xFF; + be_dws_cpu_to_le(wrb, sizeof(*wrb)); + + status = be_mbox_notify(ctrl); + if (status) + SE_DEBUG(DBG_LVL_1, "be_cmd_fw_initialize Failed \n"); + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *cq, struct be_queue_info *eq, + bool sol_evts, bool no_delay, int coalesce_wm) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_cmd_req_cq_create *req = embedded_payload(wrb); + struct be_cmd_resp_cq_create *resp = embedded_payload(wrb); + struct be_dma_mem *q_mem = &cq->dma_mem; + void *ctxt = &req->context; + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_CQ_CREATE, sizeof(*req)); + + if (!q_mem->va) + SE_DEBUG(DBG_LVL_1, "uninitialized q_mem->va\n"); + + req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size)); + + AMAP_SET_BITS(struct amap_cq_context, coalescwm, ctxt, coalesce_wm); + AMAP_SET_BITS(struct amap_cq_context, nodelay, ctxt, no_delay); + AMAP_SET_BITS(struct amap_cq_context, count, ctxt, + __ilog2_u32(cq->len / 256)); + AMAP_SET_BITS(struct amap_cq_context, valid, ctxt, 1); + AMAP_SET_BITS(struct amap_cq_context, solevent, ctxt, sol_evts); + AMAP_SET_BITS(struct amap_cq_context, eventable, ctxt, 1); + AMAP_SET_BITS(struct amap_cq_context, eqid, ctxt, eq->id); + AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 1); + AMAP_SET_BITS(struct amap_cq_context, func, ctxt, + PCI_FUNC(ctrl->pdev->devfn)); + be_dws_cpu_to_le(ctxt, sizeof(req->context)); + + be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); + + status = be_mbox_notify(ctrl); + if (!status) { + cq->id = le16_to_cpu(resp->cq_id); + cq->created = true; + } else + SE_DEBUG(DBG_LVL_1, "In be_cmd_cq_create, status=ox%08x \n", + status); + spin_unlock(&ctrl->mbox_lock); + + return status; +} + +static u32 be_encoded_q_len(int q_len) +{ + u32 len_encoded = fls(q_len); /* log2(len) + 1 */ + if (len_encoded == 16) + len_encoded = 0; + return len_encoded; +} +int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, + int queue_type) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_cmd_req_q_destroy *req = embedded_payload(wrb); + u8 subsys = 0, opcode = 0; + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + switch (queue_type) { + case QTYPE_EQ: + subsys = CMD_SUBSYSTEM_COMMON; + opcode = OPCODE_COMMON_EQ_DESTROY; + break; + case QTYPE_CQ: + subsys = CMD_SUBSYSTEM_COMMON; + opcode = OPCODE_COMMON_CQ_DESTROY; + break; + case QTYPE_WRBQ: + subsys = CMD_SUBSYSTEM_ISCSI; + opcode = OPCODE_COMMON_ISCSI_WRBQ_DESTROY; + break; + case QTYPE_DPDUQ: + subsys = CMD_SUBSYSTEM_ISCSI; + opcode = OPCODE_COMMON_ISCSI_DEFQ_DESTROY; + break; + case QTYPE_SGL: + subsys = CMD_SUBSYSTEM_ISCSI; + opcode = OPCODE_COMMON_ISCSI_CFG_REMOVE_SGL_PAGES; + break; + default: + spin_unlock(&ctrl->mbox_lock); + BUG(); + return -1; + } + be_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req)); + if (queue_type != QTYPE_SGL) + req->id = cpu_to_le16(q->id); + + status = be_mbox_notify(ctrl); + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_cmd_req_get_mac_addr *req = embedded_payload(wrb); + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG, + sizeof(*req)); + + status = be_mbox_notify(ctrl); + if (!status) { + struct be_cmd_resp_get_mac_addr *resp = embedded_payload(wrb); + + memcpy(mac_addr, resp->mac_address, ETH_ALEN); + } + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl, + struct be_queue_info *cq, + struct be_queue_info *dq, int length, + int entry_size) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_defq_create_req *req = embedded_payload(wrb); + struct be_dma_mem *q_mem = &dq->dma_mem; + void *ctxt = &req->context; + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_DEFQ_CREATE, sizeof(*req)); + + req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size); + AMAP_SET_BITS(struct amap_be_default_pdu_context, rx_pdid, ctxt, 0); + AMAP_SET_BITS(struct amap_be_default_pdu_context, rx_pdid_valid, ctxt, + 1); + AMAP_SET_BITS(struct amap_be_default_pdu_context, pci_func_id, ctxt, + PCI_FUNC(ctrl->pdev->devfn)); + AMAP_SET_BITS(struct amap_be_default_pdu_context, ring_size, ctxt, + be_encoded_q_len(length / sizeof(struct phys_addr))); + AMAP_SET_BITS(struct amap_be_default_pdu_context, default_buffer_size, + ctxt, entry_size); + AMAP_SET_BITS(struct amap_be_default_pdu_context, cq_id_recv, ctxt, + cq->id); + + be_dws_cpu_to_le(ctxt, sizeof(req->context)); + + be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); + + status = be_mbox_notify(ctrl); + if (!status) { + struct be_defq_create_resp *resp = embedded_payload(wrb); + + dq->id = le16_to_cpu(resp->id); + dq->created = true; + } + spin_unlock(&ctrl->mbox_lock); + + return status; +} + +int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, + struct be_queue_info *wrbq) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_wrbq_create_req *req = embedded_payload(wrb); + struct be_wrbq_create_resp *resp = embedded_payload(wrb); + int status; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_WRBQ_CREATE, sizeof(*req)); + req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size); + be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); + + status = be_mbox_notify(ctrl); + if (!status) + wrbq->id = le16_to_cpu(resp->cid); + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl, + struct be_dma_mem *q_mem, + u32 page_offset, u32 num_pages) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_post_sgl_pages_req *req = embedded_payload(wrb); + int status; + unsigned int curr_pages; + u32 internal_page_offset = 0; + u32 temp_num_pages = num_pages; + + if (num_pages == 0xff) + num_pages = 1; + + spin_lock(&ctrl->mbox_lock); + do { + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_CFG_POST_SGL_PAGES, + sizeof(*req)); + curr_pages = BE_NUMBER_OF_FIELD(struct be_post_sgl_pages_req, + pages); + req->num_pages = min(num_pages, curr_pages); + req->page_offset = page_offset; + be_cmd_page_addrs_prepare(req->pages, req->num_pages, q_mem); + q_mem->dma = q_mem->dma + (req->num_pages * PAGE_SIZE); + internal_page_offset += req->num_pages; + page_offset += req->num_pages; + num_pages -= req->num_pages; + + if (temp_num_pages == 0xff) + req->num_pages = temp_num_pages; + + status = be_mbox_notify(ctrl); + if (status) { + SE_DEBUG(DBG_LVL_1, + "FW CMD to map iscsi frags failed.\n"); + goto error; + } + } while (num_pages > 0); +error: + spin_unlock(&ctrl->mbox_lock); + if (status != 0) + beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL); + return status; +} diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h new file mode 100644 index 000000000000..c20d686cbb43 --- /dev/null +++ b/drivers/scsi/be2iscsi/be_cmds.h @@ -0,0 +1,877 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + */ + +#ifndef BEISCSI_CMDS_H +#define BEISCSI_CMDS_H + +/** + * The driver sends configuration and managements command requests to the + * firmware in the BE. These requests are communicated to the processor + * using Work Request Blocks (WRBs) submitted to the MCC-WRB ring or via one + * WRB inside a MAILBOX. + * The commands are serviced by the ARM processor in the BladeEngine's MPU. + */ +struct be_sge { + u32 pa_lo; + u32 pa_hi; + u32 len; +}; + +#define MCC_WRB_SGE_CNT_SHIFT 3 /* bits 3 - 7 of dword 0 */ +#define MCC_WRB_SGE_CNT_MASK 0x1F /* bits 3 - 7 of dword 0 */ +struct be_mcc_wrb { + u32 embedded; /* dword 0 */ + u32 payload_length; /* dword 1 */ + u32 tag0; /* dword 2 */ + u32 tag1; /* dword 3 */ + u32 rsvd; /* dword 4 */ + union { + u8 embedded_payload[236]; /* used by embedded cmds */ + struct be_sge sgl[19]; /* used by non-embedded cmds */ + } payload; +}; + +#define CQE_FLAGS_VALID_MASK (1 << 31) +#define CQE_FLAGS_ASYNC_MASK (1 << 30) + +/* Completion Status */ +#define MCC_STATUS_SUCCESS 0x0 + +#define CQE_STATUS_COMPL_MASK 0xFFFF +#define CQE_STATUS_COMPL_SHIFT 0 /* bits 0 - 15 */ +#define CQE_STATUS_EXTD_MASK 0xFFFF +#define CQE_STATUS_EXTD_SHIFT 0 /* bits 0 - 15 */ + +struct be_mcc_compl { + u32 status; /* dword 0 */ + u32 tag0; /* dword 1 */ + u32 tag1; /* dword 2 */ + u32 flags; /* dword 3 */ +}; + +/********* Mailbox door bell *************/ +/** + * Used for driver communication with the FW. + * The software must write this register twice to post any command. First, + * it writes the register with hi=1 and the upper bits of the physical address + * for the MAILBOX structure. Software must poll the ready bit until this + * is acknowledged. Then, sotware writes the register with hi=0 with the lower + * bits in the address. It must poll the ready bit until the command is + * complete. Upon completion, the MAILBOX will contain a valid completion + * queue entry. + */ +#define MPU_MAILBOX_DB_OFFSET 0x160 +#define MPU_MAILBOX_DB_RDY_MASK 0x1 /* bit 0 */ +#define MPU_MAILBOX_DB_HI_MASK 0x2 /* bit 1 */ + +/********** MPU semphore ******************/ +#define MPU_EP_SEMAPHORE_OFFSET 0xac +#define EP_SEMAPHORE_POST_STAGE_MASK 0x0000FFFF +#define EP_SEMAPHORE_POST_ERR_MASK 0x1 +#define EP_SEMAPHORE_POST_ERR_SHIFT 31 + +/********** MCC door bell ************/ +#define DB_MCCQ_OFFSET 0x140 +#define DB_MCCQ_RING_ID_MASK 0x7FF /* bits 0 - 10 */ +/* Number of entries posted */ +#define DB_MCCQ_NUM_POSTED_SHIFT 16 /* bits 16 - 29 */ + +/* MPU semphore POST stage values */ +#define POST_STAGE_ARMFW_RDY 0xc000 /* FW is done with POST */ + +/** + * When the async bit of mcc_compl is set, the last 4 bytes of + * mcc_compl is interpreted as follows: + */ +#define ASYNC_TRAILER_EVENT_CODE_SHIFT 8 /* bits 8 - 15 */ +#define ASYNC_TRAILER_EVENT_CODE_MASK 0xFF +#define ASYNC_EVENT_CODE_LINK_STATE 0x1 +struct be_async_event_trailer { + u32 code; +}; + +enum { + ASYNC_EVENT_LINK_DOWN = 0x0, + ASYNC_EVENT_LINK_UP = 0x1 +}; + +/** + * When the event code of an async trailer is link-state, the mcc_compl + * must be interpreted as follows + */ +struct be_async_event_link_state { + u8 physical_port; + u8 port_link_status; + u8 port_duplex; + u8 port_speed; + u8 port_fault; + u8 rsvd0[7]; + struct be_async_event_trailer trailer; +} __packed; + +struct be_mcc_mailbox { + struct be_mcc_wrb wrb; + struct be_mcc_compl compl; +}; + +/* Type of subsystems supported by FW */ +#define CMD_SUBSYSTEM_COMMON 0x1 +#define CMD_SUBSYSTEM_ISCSI 0x2 +#define CMD_SUBSYSTEM_ETH 0x3 +#define CMD_SUBSYSTEM_ISCSI_INI 0x6 +#define CMD_COMMON_TCP_UPLOAD 0x1 + +/** + * List of common opcodes subsystem CMD_SUBSYSTEM_COMMON + * These opcodes are unique for each subsystem defined above + */ +#define OPCODE_COMMON_CQ_CREATE 12 +#define OPCODE_COMMON_EQ_CREATE 13 +#define OPCODE_COMMON_MCC_CREATE 21 +#define OPCODE_COMMON_GET_CNTL_ATTRIBUTES 32 +#define OPCODE_COMMON_GET_FW_VERSION 35 +#define OPCODE_COMMON_MODIFY_EQ_DELAY 41 +#define OPCODE_COMMON_FIRMWARE_CONFIG 42 +#define OPCODE_COMMON_MCC_DESTROY 53 +#define OPCODE_COMMON_CQ_DESTROY 54 +#define OPCODE_COMMON_EQ_DESTROY 55 +#define OPCODE_COMMON_QUERY_FIRMWARE_CONFIG 58 +#define OPCODE_COMMON_FUNCTION_RESET 61 + +/** + * LIST of opcodes that are common between Initiator and Target + * used by CMD_SUBSYSTEM_ISCSI + * These opcodes are unique for each subsystem defined above + */ +#define OPCODE_COMMON_ISCSI_CFG_POST_SGL_PAGES 2 +#define OPCODE_COMMON_ISCSI_CFG_REMOVE_SGL_PAGES 3 +#define OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG 7 +#define OPCODE_COMMON_ISCSI_SET_FRAGNUM_BITS_FOR_SGL_CRA 61 +#define OPCODE_COMMON_ISCSI_DEFQ_CREATE 64 +#define OPCODE_COMMON_ISCSI_DEFQ_DESTROY 65 +#define OPCODE_COMMON_ISCSI_WRBQ_CREATE 66 +#define OPCODE_COMMON_ISCSI_WRBQ_DESTROY 67 + +struct be_cmd_req_hdr { + u8 opcode; /* dword 0 */ + u8 subsystem; /* dword 0 */ + u8 port_number; /* dword 0 */ + u8 domain; /* dword 0 */ + u32 timeout; /* dword 1 */ + u32 request_length; /* dword 2 */ + u32 rsvd; /* dword 3 */ +}; + +struct be_cmd_resp_hdr { + u32 info; /* dword 0 */ + u32 status; /* dword 1 */ + u32 response_length; /* dword 2 */ + u32 actual_resp_len; /* dword 3 */ +}; + +struct phys_addr { + u32 lo; + u32 hi; +}; + +/************************** + * BE Command definitions * + **************************/ + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte - used to calculate offset/shift/mask of each field + */ +struct amap_eq_context { + u8 cidx[13]; /* dword 0 */ + u8 rsvd0[3]; /* dword 0 */ + u8 epidx[13]; /* dword 0 */ + u8 valid; /* dword 0 */ + u8 rsvd1; /* dword 0 */ + u8 size; /* dword 0 */ + u8 pidx[13]; /* dword 1 */ + u8 rsvd2[3]; /* dword 1 */ + u8 pd[10]; /* dword 1 */ + u8 count[3]; /* dword 1 */ + u8 solevent; /* dword 1 */ + u8 stalled; /* dword 1 */ + u8 armed; /* dword 1 */ + u8 rsvd3[4]; /* dword 2 */ + u8 func[8]; /* dword 2 */ + u8 rsvd4; /* dword 2 */ + u8 delaymult[10]; /* dword 2 */ + u8 rsvd5[2]; /* dword 2 */ + u8 phase[2]; /* dword 2 */ + u8 nodelay; /* dword 2 */ + u8 rsvd6[4]; /* dword 2 */ + u8 rsvd7[32]; /* dword 3 */ +} __packed; + +struct be_cmd_req_eq_create { + struct be_cmd_req_hdr hdr; /* dw[4] */ + u16 num_pages; /* sword */ + u16 rsvd0; /* sword */ + u8 context[sizeof(struct amap_eq_context) / 8]; /* dw[4] */ + struct phys_addr pages[8]; +} __packed; + +struct be_cmd_resp_eq_create { + struct be_cmd_resp_hdr resp_hdr; + u16 eq_id; /* sword */ + u16 rsvd0; /* sword */ +} __packed; + +struct mac_addr { + u16 size_of_struct; + u8 addr[ETH_ALEN]; +} __packed; + +struct be_cmd_req_mac_query { + struct be_cmd_req_hdr hdr; + u8 type; + u8 permanent; + u16 if_id; +} __packed; + +struct be_cmd_resp_mac_query { + struct be_cmd_resp_hdr hdr; + struct mac_addr mac; +}; + +/******************** Create CQ ***************************/ +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte - used to calculate offset/shift/mask of each field + */ +struct amap_cq_context { + u8 cidx[11]; /* dword 0 */ + u8 rsvd0; /* dword 0 */ + u8 coalescwm[2]; /* dword 0 */ + u8 nodelay; /* dword 0 */ + u8 epidx[11]; /* dword 0 */ + u8 rsvd1; /* dword 0 */ + u8 count[2]; /* dword 0 */ + u8 valid; /* dword 0 */ + u8 solevent; /* dword 0 */ + u8 eventable; /* dword 0 */ + u8 pidx[11]; /* dword 1 */ + u8 rsvd2; /* dword 1 */ + u8 pd[10]; /* dword 1 */ + u8 eqid[8]; /* dword 1 */ + u8 stalled; /* dword 1 */ + u8 armed; /* dword 1 */ + u8 rsvd3[4]; /* dword 2 */ + u8 func[8]; /* dword 2 */ + u8 rsvd4[20]; /* dword 2 */ + u8 rsvd5[32]; /* dword 3 */ +} __packed; + +struct be_cmd_req_cq_create { + struct be_cmd_req_hdr hdr; + u16 num_pages; + u16 rsvd0; + u8 context[sizeof(struct amap_cq_context) / 8]; + struct phys_addr pages[4]; +} __packed; + +struct be_cmd_resp_cq_create { + struct be_cmd_resp_hdr hdr; + u16 cq_id; + u16 rsvd0; +} __packed; + +/******************** Create MCCQ ***************************/ +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte - used to calculate offset/shift/mask of each field + */ +struct amap_mcc_context { + u8 con_index[14]; + u8 rsvd0[2]; + u8 ring_size[4]; + u8 fetch_wrb; + u8 fetch_r2t; + u8 cq_id[10]; + u8 prod_index[14]; + u8 fid[8]; + u8 pdid[9]; + u8 valid; + u8 rsvd1[32]; + u8 rsvd2[32]; +} __packed; + +struct be_cmd_req_mcc_create { + struct be_cmd_req_hdr hdr; + u16 num_pages; + u16 rsvd0; + u8 context[sizeof(struct amap_mcc_context) / 8]; + struct phys_addr pages[8]; +} __packed; + +struct be_cmd_resp_mcc_create { + struct be_cmd_resp_hdr hdr; + u16 id; + u16 rsvd0; +} __packed; + +/******************** Q Destroy ***************************/ +/* Type of Queue to be destroyed */ +enum { + QTYPE_EQ = 1, + QTYPE_CQ, + QTYPE_MCCQ, + QTYPE_WRBQ, + QTYPE_DPDUQ, + QTYPE_SGL +}; + +struct be_cmd_req_q_destroy { + struct be_cmd_req_hdr hdr; + u16 id; + u16 bypass_flush; /* valid only for rx q destroy */ +} __packed; + +struct macaddr { + u8 byte[ETH_ALEN]; +}; + +struct be_cmd_req_mcast_mac_config { + struct be_cmd_req_hdr hdr; + u16 num_mac; + u8 promiscuous; + u8 interface_id; + struct macaddr mac[32]; +} __packed; + +static inline void *embedded_payload(struct be_mcc_wrb *wrb) +{ + return wrb->payload.embedded_payload; +} + +static inline struct be_sge *nonembedded_sgl(struct be_mcc_wrb *wrb) +{ + return &wrb->payload.sgl[0]; +} + +/******************** Modify EQ Delay *******************/ +struct be_cmd_req_modify_eq_delay { + struct be_cmd_req_hdr hdr; + u32 num_eq; + struct { + u32 eq_id; + u32 phase; + u32 delay_multiplier; + } delay[8]; +} __packed; + +/******************** Get MAC ADDR *******************/ + +#define ETH_ALEN 6 + + +struct be_cmd_req_get_mac_addr { + struct be_cmd_req_hdr hdr; + u32 nic_port_count; + u32 speed; + u32 max_speed; + u32 link_state; + u32 max_frame_size; + u16 size_of_structure; + u8 mac_address[ETH_ALEN]; + u32 rsvd[23]; +}; + +struct be_cmd_resp_get_mac_addr { + struct be_cmd_resp_hdr hdr; + u32 nic_port_count; + u32 speed; + u32 max_speed; + u32 link_state; + u32 max_frame_size; + u16 size_of_structure; + u8 mac_address[6]; + u32 rsvd[23]; +}; + +int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *eq, int eq_delay); + +int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *cq, struct be_queue_info *eq, + bool sol_evts, bool no_delay, + int num_cqe_dma_coalesce); + +int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, + int type); +int be_poll_mcc(struct be_ctrl_info *ctrl); +unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl); +int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr); + +/*ISCSI Functuions */ +int be_cmd_fw_initialize(struct be_ctrl_info *ctrl); + +struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem); + +int be_mbox_notify(struct be_ctrl_info *ctrl); + +int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl, + struct be_queue_info *cq, + struct be_queue_info *dq, int length, + int entry_size); + +int be_cmd_iscsi_post_sgl_pages(struct be_ctrl_info *ctrl, + struct be_dma_mem *q_mem, u32 page_offset, + u32 num_pages); + +int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem, + struct be_queue_info *wrbq); + +struct be_default_pdu_context { + u32 dw[4]; +} __packed; + +struct amap_be_default_pdu_context { + u8 dbuf_cindex[13]; /* dword 0 */ + u8 rsvd0[3]; /* dword 0 */ + u8 ring_size[4]; /* dword 0 */ + u8 ring_state[4]; /* dword 0 */ + u8 rsvd1[8]; /* dword 0 */ + u8 dbuf_pindex[13]; /* dword 1 */ + u8 rsvd2; /* dword 1 */ + u8 pci_func_id[8]; /* dword 1 */ + u8 rx_pdid[9]; /* dword 1 */ + u8 rx_pdid_valid; /* dword 1 */ + u8 default_buffer_size[16]; /* dword 2 */ + u8 cq_id_recv[10]; /* dword 2 */ + u8 rx_pdid_not_valid; /* dword 2 */ + u8 rsvd3[5]; /* dword 2 */ + u8 rsvd4[32]; /* dword 3 */ +} __packed; + +struct be_defq_create_req { + struct be_cmd_req_hdr hdr; + u16 num_pages; + u8 ulp_num; + u8 rsvd0; + struct be_default_pdu_context context; + struct phys_addr pages[8]; +} __packed; + +struct be_defq_create_resp { + struct be_cmd_req_hdr hdr; + u16 id; + u16 rsvd0; +} __packed; + +struct be_post_sgl_pages_req { + struct be_cmd_req_hdr hdr; + u16 num_pages; + u16 page_offset; + u32 rsvd0; + struct phys_addr pages[26]; + u32 rsvd1; +} __packed; + +struct be_wrbq_create_req { + struct be_cmd_req_hdr hdr; + u16 num_pages; + u8 ulp_num; + u8 rsvd0; + struct phys_addr pages[8]; +} __packed; + +struct be_wrbq_create_resp { + struct be_cmd_resp_hdr resp_hdr; + u16 cid; + u16 rsvd0; +} __packed; + +#define SOL_CID_MASK 0x0000FFC0 +#define SOL_CODE_MASK 0x0000003F +#define SOL_WRB_INDEX_MASK 0x00FF0000 +#define SOL_CMD_WND_MASK 0xFF000000 +#define SOL_RES_CNT_MASK 0x7FFFFFFF +#define SOL_EXP_CMD_SN_MASK 0xFFFFFFFF +#define SOL_HW_STS_MASK 0x000000FF +#define SOL_STS_MASK 0x0000FF00 +#define SOL_RESP_MASK 0x00FF0000 +#define SOL_FLAGS_MASK 0x7F000000 +#define SOL_S_MASK 0x80000000 + +struct sol_cqe { + u32 dw[4]; +}; + +struct amap_sol_cqe { + u8 hw_sts[8]; /* dword 0 */ + u8 i_sts[8]; /* dword 0 */ + u8 i_resp[8]; /* dword 0 */ + u8 i_flags[7]; /* dword 0 */ + u8 s; /* dword 0 */ + u8 i_exp_cmd_sn[32]; /* dword 1 */ + u8 code[6]; /* dword 2 */ + u8 cid[10]; /* dword 2 */ + u8 wrb_index[8]; /* dword 2 */ + u8 i_cmd_wnd[8]; /* dword 2 */ + u8 i_res_cnt[31]; /* dword 3 */ + u8 valid; /* dword 3 */ +} __packed; + + +/** + * Post WRB Queue Doorbell Register used by the host Storage + * stack to notify the + * controller of a posted Work Request Block + */ +#define DB_WRB_POST_CID_MASK 0x3FF /* bits 0 - 9 */ +#define DB_DEF_PDU_WRB_INDEX_MASK 0xFF /* bits 0 - 9 */ + +#define DB_DEF_PDU_WRB_INDEX_SHIFT 16 +#define DB_DEF_PDU_NUM_POSTED_SHIFT 24 + +struct fragnum_bits_for_sgl_cra_in { + struct be_cmd_req_hdr hdr; + u32 num_bits; +} __packed; + +struct iscsi_cleanup_req { + struct be_cmd_req_hdr hdr; + u16 chute; + u8 hdr_ring_id; + u8 data_ring_id; + +} __packed; + +struct eq_delay { + u32 eq_id; + u32 phase; + u32 delay_multiplier; +} __packed; + +struct be_eq_delay_params_in { + struct be_cmd_req_hdr hdr; + u32 num_eq; + struct eq_delay delay[8]; +} __packed; + +struct ip_address_format { + u16 size_of_structure; + u8 reserved; + u8 ip_type; + u8 ip_address[16]; + u32 rsvd0; +} __packed; + +struct tcp_connect_and_offload_in { + struct be_cmd_req_hdr hdr; + struct ip_address_format ip_address; + u16 tcp_port; + u16 cid; + u16 cq_id; + u16 defq_id; + struct phys_addr dataout_template_pa; + u16 hdr_ring_id; + u16 data_ring_id; + u8 do_offload; + u8 rsvd0[3]; +} __packed; + +struct tcp_connect_and_offload_out { + struct be_cmd_resp_hdr hdr; + u32 connection_handle; + u16 cid; + u16 rsvd0; + +} __packed; + +struct be_mcc_wrb_context { + struct MCC_WRB *wrb; + int *users_final_status; +} __packed; + +#define DB_DEF_PDU_RING_ID_MASK 0x3FF /* bits 0 - 9 */ +#define DB_DEF_PDU_CQPROC_MASK 0x3FFF /* bits 0 - 9 */ +#define DB_DEF_PDU_REARM_SHIFT 14 +#define DB_DEF_PDU_EVENT_SHIFT 15 +#define DB_DEF_PDU_CQPROC_SHIFT 16 + +struct dmsg_cqe { + u32 dw[4]; +} __packed; + +struct tcp_upload_params_in { + struct be_cmd_req_hdr hdr; + u16 id; + u16 upload_type; + u32 reset_seq; +} __packed; + +struct tcp_upload_params_out { + u32 dw[32]; +} __packed; + +union tcp_upload_params { + struct tcp_upload_params_in request; + struct tcp_upload_params_out response; +} __packed; + +struct be_ulp_fw_cfg { + u32 ulp_mode; + u32 etx_base; + u32 etx_count; + u32 sq_base; + u32 sq_count; + u32 rq_base; + u32 rq_count; + u32 dq_base; + u32 dq_count; + u32 lro_base; + u32 lro_count; + u32 icd_base; + u32 icd_count; +}; + +struct be_fw_cfg { + struct be_cmd_req_hdr hdr; + u32 be_config_number; + u32 asic_revision; + u32 phys_port; + u32 function_mode; + struct be_ulp_fw_cfg ulp[2]; + u32 function_caps; +} __packed; + +#define CMD_ISCSI_COMMAND_INVALIDATE 1 +#define ISCSI_OPCODE_SCSI_DATA_OUT 5 +#define OPCODE_COMMON_ISCSI_TCP_CONNECT_AND_OFFLOAD 70 +#define OPCODE_ISCSI_INI_DRIVER_OFFLOAD_SESSION 41 +#define OPCODE_COMMON_MODIFY_EQ_DELAY 41 +#define OPCODE_COMMON_ISCSI_CLEANUP 59 +#define OPCODE_COMMON_TCP_UPLOAD 56 +#define OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS 1 +/* --- CMD_ISCSI_INVALIDATE_CONNECTION_TYPE --- */ +#define CMD_ISCSI_CONNECTION_INVALIDATE 1 +#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 2 +#define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42 + +#define INI_WR_CMD 1 /* Initiator write command */ +#define INI_TMF_CMD 2 /* Initiator TMF command */ +#define INI_NOPOUT_CMD 3 /* Initiator; Send a NOP-OUT */ +#define INI_RD_CMD 5 /* Initiator requesting to send + * a read command + */ +#define TGT_CTX_UPDT_CMD 7 /* Target context update */ +#define TGT_STS_CMD 8 /* Target R2T and other BHS + * where only the status number + * need to be updated + */ +#define TGT_DATAIN_CMD 9 /* Target Data-Ins in response + * to read command + */ +#define TGT_SOS_PDU 10 /* Target:standalone status + * response + */ +#define TGT_DM_CMD 11 /* Indicates that the bhs + * preparedby + * driver should not be touched + */ +/* --- CMD_CHUTE_TYPE --- */ +#define CMD_CONNECTION_CHUTE_0 1 +#define CMD_CONNECTION_CHUTE_1 2 +#define CMD_CONNECTION_CHUTE_2 3 + +#define EQ_MAJOR_CODE_COMPLETION 0 + +#define CMD_ISCSI_SESSION_DEL_CFG_FROM_FLASH 0 +#define CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH 1 + +/* --- CONNECTION_UPLOAD_PARAMS --- */ +/* These parameters are used to define the type of upload desired. */ +#define CONNECTION_UPLOAD_GRACEFUL 1 /* Graceful upload */ +#define CONNECTION_UPLOAD_ABORT_RESET 2 /* Abortive upload with + * reset + */ +#define CONNECTION_UPLOAD_ABORT 3 /* Abortive upload without + * reset + */ +#define CONNECTION_UPLOAD_ABORT_WITH_SEQ 4 /* Abortive upload with reset, + * sequence number by driver */ + +/* Returns byte size of given field with a structure. */ + +/* Returns the number of items in the field array. */ +#define BE_NUMBER_OF_FIELD(_type_, _field_) \ + (FIELD_SIZEOF(_type_, _field_)/sizeof((((_type_ *)0)->_field_[0])))\ + +/** + * Different types of iSCSI completions to host driver for both initiator + * and taget mode + * of operation. + */ +#define SOL_CMD_COMPLETE 1 /* Solicited command completed + * normally + */ +#define SOL_CMD_KILLED_DATA_DIGEST_ERR 2 /* Solicited command got + * invalidated internally due + * to Data Digest error + */ +#define CXN_KILLED_PDU_SIZE_EXCEEDS_DSL 3 /* Connection got invalidated + * internally + * due to a recieved PDU + * size > DSL + */ +#define CXN_KILLED_BURST_LEN_MISMATCH 4 /* Connection got invalidated + * internally due ti received + * PDU sequence size > + * FBL/MBL. + */ +#define CXN_KILLED_AHS_RCVD 5 /* Connection got invalidated + * internally due to a recieved + * PDU Hdr that has + * AHS */ +#define CXN_KILLED_HDR_DIGEST_ERR 6 /* Connection got invalidated + * internally due to Hdr Digest + * error + */ +#define CXN_KILLED_UNKNOWN_HDR 7 /* Connection got invalidated + * internally + * due to a bad opcode in the + * pdu hdr + */ +#define CXN_KILLED_STALE_ITT_TTT_RCVD 8 /* Connection got invalidated + * internally due to a recieved + * ITT/TTT that does not belong + * to this Connection + */ +#define CXN_KILLED_INVALID_ITT_TTT_RCVD 9 /* Connection got invalidated + * internally due to recieved + * ITT/TTT value > Max + * Supported ITTs/TTTs + */ +#define CXN_KILLED_RST_RCVD 10 /* Connection got invalidated + * internally due to an + * incoming TCP RST + */ +#define CXN_KILLED_TIMED_OUT 11 /* Connection got invalidated + * internally due to timeout on + * tcp segment 12 retransmit + * attempts failed + */ +#define CXN_KILLED_RST_SENT 12 /* Connection got invalidated + * internally due to TCP RST + * sent by the Tx side + */ +#define CXN_KILLED_FIN_RCVD 13 /* Connection got invalidated + * internally due to an + * incoming TCP FIN. + */ +#define CXN_KILLED_BAD_UNSOL_PDU_RCVD 14 /* Connection got invalidated + * internally due to bad + * unsolicited PDU Unsolicited + * PDUs are PDUs with + * ITT=0xffffffff + */ +#define CXN_KILLED_BAD_WRB_INDEX_ERROR 15 /* Connection got invalidated + * internally due to bad WRB + * index. + */ +#define CXN_KILLED_OVER_RUN_RESIDUAL 16 /* Command got invalidated + * internally due to recived + * command has residual + * over run bytes. + */ +#define CXN_KILLED_UNDER_RUN_RESIDUAL 17 /* Command got invalidated + * internally due to recived + * command has residual under + * run bytes. + */ +#define CMD_KILLED_INVALID_STATSN_RCVD 18 /* Command got invalidated + * internally due to a recieved + * PDU has an invalid StatusSN + */ +#define CMD_KILLED_INVALID_R2T_RCVD 19 /* Command got invalidated + * internally due to a recieved + * an R2T with some invalid + * fields in it + */ +#define CMD_CXN_KILLED_LUN_INVALID 20 /* Command got invalidated + * internally due to received + * PDU has an invalid LUN. + */ +#define CMD_CXN_KILLED_ICD_INVALID 21 /* Command got invalidated + * internally due to the + * corresponding ICD not in a + * valid state + */ +#define CMD_CXN_KILLED_ITT_INVALID 22 /* Command got invalidated due + * to received PDU has an + * invalid ITT. + */ +#define CMD_CXN_KILLED_SEQ_OUTOFORDER 23 /* Command got invalidated due + * to received sequence buffer + * offset is out of order. + */ +#define CMD_CXN_KILLED_INVALID_DATASN_RCVD 24 /* Command got invalidated + * internally due to a + * recieved PDU has an invalid + * DataSN + */ +#define CXN_INVALIDATE_NOTIFY 25 /* Connection invalidation + * completion notify. + */ +#define CXN_INVALIDATE_INDEX_NOTIFY 26 /* Connection invalidation + * completion + * with data PDU index. + */ +#define CMD_INVALIDATED_NOTIFY 27 /* Command invalidation + * completionnotifify. + */ +#define UNSOL_HDR_NOTIFY 28 /* Unsolicited header notify.*/ +#define UNSOL_DATA_NOTIFY 29 /* Unsolicited data notify.*/ +#define UNSOL_DATA_DIGEST_ERROR_NOTIFY 30 /* Unsolicited data digest + * error notify. + */ +#define DRIVERMSG_NOTIFY 31 /* TCP acknowledge based + * notification. + */ +#define CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN 32 /* Connection got invalidated + * internally due to command + * and data are not on same + * connection. + */ +#define SOL_CMD_KILLED_DIF_ERR 33 /* Solicited command got + * invalidated internally due + * to DIF error + */ +#define CXN_KILLED_SYN_RCVD 34 /* Connection got invalidated + * internally due to incoming + * TCP SYN + */ +#define CXN_KILLED_IMM_DATA_RCVD 35 /* Connection got invalidated + * internally due to an + * incoming Unsolicited PDU + * that has immediate data on + * the cxn + */ + +void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len, + bool embedded, u8 sge_cnt); + +void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr, + u8 subsystem, u8 opcode, int cmd_len); + +#endif /* !BEISCSI_CMDS_H */ diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c new file mode 100644 index 000000000000..b23526cb39d7 --- /dev/null +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -0,0 +1,646 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "be_iscsi.h" + +extern struct iscsi_transport beiscsi_iscsi_transport; + +/** + * beiscsi_session_create - creates a new iscsi session + * @cmds_max: max commands supported + * @qdepth: max queue depth supported + * @initial_cmdsn: initial iscsi CMDSN + */ +struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, + u16 cmds_max, + u16 qdepth, + u32 initial_cmdsn) +{ + struct Scsi_Host *shost; + struct beiscsi_endpoint *beiscsi_ep; + struct iscsi_cls_session *cls_session; + struct iscsi_session *sess; + struct beiscsi_hba *phba; + struct iscsi_task *task; + struct beiscsi_io_task *io_task; + unsigned int max_size, num_cmd; + dma_addr_t bus_add; + u64 pa_addr; + void *vaddr; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_session_create\n"); + + if (!ep) { + SE_DEBUG(DBG_LVL_1, "beiscsi_session_create: invalid ep \n"); + return NULL; + } + beiscsi_ep = ep->dd_data; + phba = beiscsi_ep->phba; + shost = phba->shost; + if (cmds_max > beiscsi_ep->phba->params.wrbs_per_cxn) { + shost_printk(KERN_ERR, shost, "Cannot handle %d cmds." + "Max cmds per session supported is %d. Using %d. " + "\n", cmds_max, + beiscsi_ep->phba->params.wrbs_per_cxn, + beiscsi_ep->phba->params.wrbs_per_cxn); + cmds_max = beiscsi_ep->phba->params.wrbs_per_cxn; + } + + cls_session = iscsi_session_setup(&beiscsi_iscsi_transport, + shost, cmds_max, + sizeof(struct beiscsi_io_task), + initial_cmdsn, ISCSI_MAX_TARGET); + if (!cls_session) + return NULL; + sess = cls_session->dd_data; + max_size = ALIGN(sizeof(struct be_cmd_bhs), 64) * sess->cmds_max; + vaddr = pci_alloc_consistent(phba->pcidev, max_size, &bus_add); + pa_addr = (__u64) bus_add; + + for (num_cmd = 0; num_cmd < sess->cmds_max; num_cmd++) { + task = sess->cmds[num_cmd]; + io_task = task->dd_data; + io_task->cmd_bhs = vaddr; + io_task->bhs_pa.u.a64.address = pa_addr; + io_task->alloc_size = max_size; + vaddr += ALIGN(sizeof(struct be_cmd_bhs), 64); + pa_addr += ALIGN(sizeof(struct be_cmd_bhs), 64); + } + return cls_session; +} + +/** + * beiscsi_session_destroy - destroys iscsi session + * @cls_session: pointer to iscsi cls session + * + * Destroys iSCSI session instance and releases + * resources allocated for it. + */ +void beiscsi_session_destroy(struct iscsi_cls_session *cls_session) +{ + struct iscsi_task *task; + struct beiscsi_io_task *io_task; + struct iscsi_session *sess = cls_session->dd_data; + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); + struct beiscsi_hba *phba = iscsi_host_priv(shost); + + task = sess->cmds[0]; + io_task = task->dd_data; + pci_free_consistent(phba->pcidev, + io_task->alloc_size, + io_task->cmd_bhs, + io_task->bhs_pa.u.a64.address); + iscsi_session_teardown(cls_session); +} + +/** + * beiscsi_conn_create - create an instance of iscsi connection + * @cls_session: ptr to iscsi_cls_session + * @cid: iscsi cid + */ +struct iscsi_cls_conn * +beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid) +{ + struct beiscsi_hba *phba; + struct Scsi_Host *shost; + struct iscsi_cls_conn *cls_conn; + struct beiscsi_conn *beiscsi_conn; + struct iscsi_conn *conn; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_create ,cid" + "from iscsi layer=%d\n", cid); + shost = iscsi_session_to_shost(cls_session); + phba = iscsi_host_priv(shost); + + cls_conn = iscsi_conn_setup(cls_session, sizeof(*beiscsi_conn), cid); + if (!cls_conn) + return NULL; + + conn = cls_conn->dd_data; + beiscsi_conn = conn->dd_data; + beiscsi_conn->ep = NULL; + beiscsi_conn->phba = phba; + beiscsi_conn->conn = conn; + return cls_conn; +} + +/** + * beiscsi_bindconn_cid - Bind the beiscsi_conn with phba connection table + * @beiscsi_conn: The pointer to beiscsi_conn structure + * @phba: The phba instance + * @cid: The cid to free + */ +static int beiscsi_bindconn_cid(struct beiscsi_hba *phba, + struct beiscsi_conn *beiscsi_conn, + unsigned int cid) +{ + if (phba->conn_table[cid]) { + SE_DEBUG(DBG_LVL_1, + "Connection table already occupied. Detected clash\n"); + return -EINVAL; + } else { + SE_DEBUG(DBG_LVL_8, "phba->conn_table[%d]=%p(beiscsi_conn) \n", + cid, beiscsi_conn); + phba->conn_table[cid] = beiscsi_conn; + } + return 0; +} + +/** + * beiscsi_conn_bind - Binds iscsi session/connection with TCP connection + * @cls_session: pointer to iscsi cls session + * @cls_conn: pointer to iscsi cls conn + * @transport_fd: EP handle(64 bit) + * + * This function binds the TCP Conn with iSCSI Connection and Session. + */ +int beiscsi_conn_bind(struct iscsi_cls_session *cls_session, + struct iscsi_cls_conn *cls_conn, + u64 transport_fd, int is_leading) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct Scsi_Host *shost = + (struct Scsi_Host *)iscsi_session_to_shost(cls_session); + struct beiscsi_hba *phba = (struct beiscsi_hba *)iscsi_host_priv(shost); + struct beiscsi_endpoint *beiscsi_ep; + struct iscsi_endpoint *ep; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_bind\n"); + ep = iscsi_lookup_endpoint(transport_fd); + if (!ep) + return -EINVAL; + + beiscsi_ep = ep->dd_data; + + if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) + return -EINVAL; + + if (beiscsi_ep->phba != phba) { + SE_DEBUG(DBG_LVL_8, + "beiscsi_ep->hba=%p not equal to phba=%p \n", + beiscsi_ep->phba, phba); + return -EEXIST; + } + + beiscsi_conn->beiscsi_conn_cid = beiscsi_ep->ep_cid; + beiscsi_conn->ep = beiscsi_ep; + beiscsi_ep->conn = beiscsi_conn; + SE_DEBUG(DBG_LVL_8, "beiscsi_conn=%p conn=%p ep_cid=%d \n", + beiscsi_conn, conn, beiscsi_ep->ep_cid); + return beiscsi_bindconn_cid(phba, beiscsi_conn, beiscsi_ep->ep_cid); +} + +/** + * beiscsi_conn_get_param - get the iscsi parameter + * @cls_conn: pointer to iscsi cls conn + * @param: parameter type identifier + * @buf: buffer pointer + * + * returns iscsi parameter + */ +int beiscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf) +{ + struct beiscsi_endpoint *beiscsi_ep; + struct iscsi_conn *conn = cls_conn->dd_data; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + int len = 0; + + beiscsi_ep = beiscsi_conn->ep; + if (!beiscsi_ep) { + SE_DEBUG(DBG_LVL_1, + "In beiscsi_conn_get_param , no beiscsi_ep\n"); + return -1; + } + + switch (param) { + case ISCSI_PARAM_CONN_PORT: + len = sprintf(buf, "%hu\n", beiscsi_ep->dst_tcpport); + break; + case ISCSI_PARAM_CONN_ADDRESS: + if (beiscsi_ep->ip_type == BE2_IPV4) + len = sprintf(buf, "%pI4\n", &beiscsi_ep->dst_addr); + else + len = sprintf(buf, "%pI6\n", &beiscsi_ep->dst6_addr); + break; + default: + return iscsi_conn_get_param(cls_conn, param, buf); + } + return len; +} + +int beiscsi_set_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf, int buflen) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct iscsi_session *session = conn->session; + int ret; + + ret = iscsi_set_param(cls_conn, param, buf, buflen); + if (ret) + return ret; + /* + * If userspace tried to set the value to higher than we can + * support override here. + */ + switch (param) { + case ISCSI_PARAM_FIRST_BURST: + if (session->first_burst > 8192) + session->first_burst = 8192; + break; + case ISCSI_PARAM_MAX_RECV_DLENGTH: + if (conn->max_recv_dlength > 65536) + conn->max_recv_dlength = 65536; + break; + case ISCSI_PARAM_MAX_BURST: + if (session->first_burst > 262144) + session->first_burst = 262144; + break; + default: + return 0; + } + + return 0; +} + +/** + * beiscsi_get_host_param - get the iscsi parameter + * @shost: pointer to scsi_host structure + * @param: parameter type identifier + * @buf: buffer pointer + * + * returns host parameter + */ +int beiscsi_get_host_param(struct Scsi_Host *shost, + enum iscsi_host_param param, char *buf) +{ + struct beiscsi_hba *phba = (struct beiscsi_hba *)iscsi_host_priv(shost); + int len = 0; + + switch (param) { + case ISCSI_HOST_PARAM_HWADDRESS: + be_cmd_get_mac_addr(&phba->ctrl, phba->mac_address); + len = sysfs_format_mac(buf, phba->mac_address, ETH_ALEN); + break; + default: + return iscsi_host_get_param(shost, param, buf); + } + return len; +} + +/** + * beiscsi_conn_get_stats - get the iscsi stats + * @cls_conn: pointer to iscsi cls conn + * @stats: pointer to iscsi_stats structure + * + * returns iscsi stats + */ +void beiscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, + struct iscsi_stats *stats) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_get_stats\n"); + stats->txdata_octets = conn->txdata_octets; + stats->rxdata_octets = conn->rxdata_octets; + stats->dataout_pdus = conn->dataout_pdus_cnt; + stats->scsirsp_pdus = conn->scsirsp_pdus_cnt; + stats->scsicmd_pdus = conn->scsicmd_pdus_cnt; + stats->datain_pdus = conn->datain_pdus_cnt; + stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt; + stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt; + stats->r2t_pdus = conn->r2t_pdus_cnt; + stats->digest_err = 0; + stats->timeout_err = 0; + stats->custom_length = 0; + strcpy(stats->custom[0].desc, "eh_abort_cnt"); + stats->custom[0].value = conn->eh_abort_cnt; +} + +/** + * beiscsi_set_params_for_offld - get the parameters for offload + * @beiscsi_conn: pointer to beiscsi_conn + * @params: pointer to offload_params structure + */ +static void beiscsi_set_params_for_offld(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_offload_params *params) +{ + struct iscsi_conn *conn = beiscsi_conn->conn; + struct iscsi_session *session = conn->session; + + AMAP_SET_BITS(struct amap_beiscsi_offload_params, max_burst_length, + params, session->max_burst); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, + max_send_data_segment_length, params, + conn->max_xmit_dlength); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, first_burst_length, + params, session->first_burst); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, erl, params, + session->erl); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, dde, params, + conn->datadgst_en); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, hde, params, + conn->hdrdgst_en); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, ir2t, params, + session->initial_r2t_en); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, imd, params, + session->imm_data_en); + AMAP_SET_BITS(struct amap_beiscsi_offload_params, exp_statsn, params, + (conn->exp_statsn - 1)); +} + +/** + * beiscsi_conn_start - offload of session to chip + * @cls_conn: pointer to beiscsi_conn + */ +int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_endpoint *beiscsi_ep; + struct beiscsi_offload_params params; + struct iscsi_session *session = conn->session; + struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session); + struct beiscsi_hba *phba = iscsi_host_priv(shost); + + memset(¶ms, 0, sizeof(struct beiscsi_offload_params)); + beiscsi_ep = beiscsi_conn->ep; + if (!beiscsi_ep) + SE_DEBUG(DBG_LVL_1, "In beiscsi_conn_start , no beiscsi_ep\n"); + + free_mgmt_sgl_handle(phba, beiscsi_conn->plogin_sgl_handle); + beiscsi_conn->login_in_progress = 0; + beiscsi_set_params_for_offld(beiscsi_conn, ¶ms); + beiscsi_offload_connection(beiscsi_conn, ¶ms); + iscsi_conn_start(cls_conn); + return 0; +} + +/** + * beiscsi_get_cid - Allocate a cid + * @phba: The phba instance + */ +static int beiscsi_get_cid(struct beiscsi_hba *phba) +{ + unsigned short cid = 0xFFFF; + + if (!phba->avlbl_cids) + return cid; + + cid = phba->cid_array[phba->cid_alloc++]; + if (phba->cid_alloc == phba->params.cxns_per_ctrl) + phba->cid_alloc = 0; + phba->avlbl_cids--; + return cid; +} + +/** + * beiscsi_open_conn - Ask FW to open a TCP connection + * @ep: endpoint to be used + * @src_addr: The source IP address + * @dst_addr: The Destination IP address + * + * Asks the FW to open a TCP connection + */ +static int beiscsi_open_conn(struct iscsi_endpoint *ep, + struct sockaddr *src_addr, + struct sockaddr *dst_addr, int non_blocking) +{ + struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; + struct beiscsi_hba *phba = beiscsi_ep->phba; + int ret = -1; + + beiscsi_ep->ep_cid = beiscsi_get_cid(phba); + if (beiscsi_ep->ep_cid == 0xFFFF) { + SE_DEBUG(DBG_LVL_1, "No free cid available\n"); + return ret; + } + SE_DEBUG(DBG_LVL_8, "In beiscsi_open_conn, ep_cid=%d ", + beiscsi_ep->ep_cid); + phba->ep_array[beiscsi_ep->ep_cid] = ep; + if (beiscsi_ep->ep_cid > + (phba->fw_config.iscsi_cid_start + phba->params.cxns_per_ctrl)) { + SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n"); + return ret; + } + + beiscsi_ep->cid_vld = 0; + return mgmt_open_connection(phba, dst_addr, beiscsi_ep); +} + +/** + * beiscsi_put_cid - Free the cid + * @phba: The phba for which the cid is being freed + * @cid: The cid to free + */ +static void beiscsi_put_cid(struct beiscsi_hba *phba, unsigned short cid) +{ + phba->avlbl_cids++; + phba->cid_array[phba->cid_free++] = cid; + if (phba->cid_free == phba->params.cxns_per_ctrl) + phba->cid_free = 0; +} + +/** + * beiscsi_free_ep - free endpoint + * @ep: pointer to iscsi endpoint structure + */ +static void beiscsi_free_ep(struct iscsi_endpoint *ep) +{ + struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; + struct beiscsi_hba *phba = beiscsi_ep->phba; + + beiscsi_put_cid(phba, beiscsi_ep->ep_cid); + beiscsi_ep->phba = NULL; + iscsi_destroy_endpoint(ep); +} + +/** + * beiscsi_ep_connect - Ask chip to create TCP Conn + * @scsi_host: Pointer to scsi_host structure + * @dst_addr: The IP address of Target + * @non_blocking: blocking or non-blocking call + * + * This routines first asks chip to create a connection and then allocates an EP + */ +struct iscsi_endpoint * +beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, + int non_blocking) +{ + struct beiscsi_hba *phba; + struct beiscsi_endpoint *beiscsi_ep; + struct iscsi_endpoint *ep; + int ret; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_connect \n"); + if (shost) + phba = iscsi_host_priv(shost); + else { + ret = -ENXIO; + SE_DEBUG(DBG_LVL_1, "shost is NULL \n"); + return ERR_PTR(ret); + } + ep = iscsi_create_endpoint(sizeof(struct beiscsi_endpoint)); + if (!ep) { + ret = -ENOMEM; + return ERR_PTR(ret); + } + + beiscsi_ep = ep->dd_data; + beiscsi_ep->phba = phba; + + if (beiscsi_open_conn(ep, NULL, dst_addr, non_blocking)) { + SE_DEBUG(DBG_LVL_1, "Failed in allocate iscsi cid\n"); + ret = -ENOMEM; + goto free_ep; + } + + return ep; + +free_ep: + beiscsi_free_ep(ep); + return ERR_PTR(ret); +} + +/** + * beiscsi_ep_poll - Poll to see if connection is established + * @ep: endpoint to be used + * @timeout_ms: timeout specified in millisecs + * + * Poll to see if TCP connection established + */ +int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms) +{ + struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_poll\n"); + if (beiscsi_ep->cid_vld == 1) + return 1; + else + return 0; +} + +/** + * beiscsi_close_conn - Upload the connection + * @ep: The iscsi endpoint + * @flag: The type of connection closure + */ +static int beiscsi_close_conn(struct iscsi_endpoint *ep, int flag) +{ + int ret = 0; + struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; + struct beiscsi_hba *phba = beiscsi_ep->phba; + + if (MGMT_STATUS_SUCCESS != + mgmt_upload_connection(phba, beiscsi_ep->ep_cid, + CONNECTION_UPLOAD_GRACEFUL)) { + SE_DEBUG(DBG_LVL_8, "upload failed for cid 0x%x", + beiscsi_ep->ep_cid); + ret = -1; + } + + return ret; +} + +/** + * beiscsi_ep_disconnect - Tears down the TCP connection + * @ep: endpoint to be used + * + * Tears down the TCP connection + */ +void beiscsi_ep_disconnect(struct iscsi_endpoint *ep) +{ + struct beiscsi_conn *beiscsi_conn; + struct beiscsi_endpoint *beiscsi_ep; + struct beiscsi_hba *phba; + int flag = 0; + + beiscsi_ep = ep->dd_data; + phba = beiscsi_ep->phba; + SE_DEBUG(DBG_LVL_8, "In beiscsi_ep_disconnect\n"); + + if (beiscsi_ep->conn) { + beiscsi_conn = beiscsi_ep->conn; + iscsi_suspend_queue(beiscsi_conn->conn); + beiscsi_close_conn(ep, flag); + } + + beiscsi_free_ep(ep); +} + +/** + * beiscsi_unbind_conn_to_cid - Unbind the beiscsi_conn from phba conn table + * @phba: The phba instance + * @cid: The cid to free + */ +static int beiscsi_unbind_conn_to_cid(struct beiscsi_hba *phba, + unsigned int cid) +{ + if (phba->conn_table[cid]) + phba->conn_table[cid] = NULL; + else { + SE_DEBUG(DBG_LVL_8, "Connection table Not occupied. \n"); + return -EINVAL; + } + return 0; +} + +/** + * beiscsi_conn_stop - Invalidate and stop the connection + * @cls_conn: pointer to get iscsi_conn + * @flag: The type of connection closure + */ +void beiscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_endpoint *beiscsi_ep; + struct iscsi_session *session = conn->session; + struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session); + struct beiscsi_hba *phba = iscsi_host_priv(shost); + unsigned int status; + unsigned short savecfg_flag = CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH; + + SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_stop\n"); + beiscsi_ep = beiscsi_conn->ep; + if (!beiscsi_ep) { + SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_stop , no beiscsi_ep\n"); + return; + } + status = mgmt_invalidate_connection(phba, beiscsi_ep, + beiscsi_ep->ep_cid, 1, + savecfg_flag); + if (status != MGMT_STATUS_SUCCESS) { + SE_DEBUG(DBG_LVL_1, + "mgmt_invalidate_connection Failed for cid=%d \n", + beiscsi_ep->ep_cid); + } + beiscsi_unbind_conn_to_cid(phba, beiscsi_ep->ep_cid); + iscsi_conn_stop(cls_conn, flag); +} diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h new file mode 100644 index 000000000000..f92ffc5349fb --- /dev/null +++ b/drivers/scsi/be2iscsi/be_iscsi.h @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ + +#ifndef _BE_ISCSI_ +#define _BE_ISCSI_ + +#include "be_main.h" +#include "be_mgmt.h" + +#define BE2_IPV4 0x1 +#define BE2_IPV6 0x10 + +void beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_offload_params *params); + +void beiscsi_offload_iscsi(struct beiscsi_hba *phba, struct iscsi_conn *conn, + struct beiscsi_conn *beiscsi_conn, + unsigned int fw_handle); + +struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, + uint16_t cmds_max, + uint16_t qdepth, + uint32_t initial_cmdsn); + +void beiscsi_session_destroy(struct iscsi_cls_session *cls_session); + +struct iscsi_cls_conn *beiscsi_conn_create(struct iscsi_cls_session + *cls_session, uint32_t cid); + +int beiscsi_conn_bind(struct iscsi_cls_session *cls_session, + struct iscsi_cls_conn *cls_conn, + uint64_t transport_fd, int is_leading); + +int beiscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf); + +int beiscsi_get_host_param(struct Scsi_Host *shost, + enum iscsi_host_param param, char *buf); + +int beiscsi_set_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf, int buflen); + +int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn); + +void beiscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag); + +struct iscsi_endpoint *beiscsi_ep_connect(struct Scsi_Host *shost, + struct sockaddr *dst_addr, + int non_blocking); + +int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms); + +void beiscsi_ep_disconnect(struct iscsi_endpoint *ep); + +void beiscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, + struct iscsi_stats *stats); + +#endif diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c new file mode 100644 index 000000000000..d520fe875859 --- /dev/null +++ b/drivers/scsi/be2iscsi/be_main.c @@ -0,0 +1,3393 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "be_main.h" +#include "be_iscsi.h" +#include "be_mgmt.h" + +static unsigned int be_iopoll_budget = 10; +static unsigned int be_max_phys_size = 64; +static unsigned int enable_msix; + +MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table); +MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR); +MODULE_AUTHOR("ServerEngines Corporation"); +MODULE_LICENSE("GPL"); +module_param(be_iopoll_budget, int, 0); +module_param(enable_msix, int, 0); +module_param(be_max_phys_size, uint, S_IRUGO); +MODULE_PARM_DESC(be_max_phys_size, "Maximum Size (In Kilobytes) of physically" + "contiguous memory that can be allocated." + "Range is 16 - 128"); + +static int beiscsi_slave_configure(struct scsi_device *sdev) +{ + blk_queue_max_segment_size(sdev->request_queue, 65536); + return 0; +} + +static struct scsi_host_template beiscsi_sht = { + .module = THIS_MODULE, + .name = "ServerEngines 10Gbe open-iscsi Initiator Driver", + .proc_name = DRV_NAME, + .queuecommand = iscsi_queuecommand, + .eh_abort_handler = iscsi_eh_abort, + .change_queue_depth = iscsi_change_queue_depth, + .slave_configure = beiscsi_slave_configure, + .target_alloc = iscsi_target_alloc, + .eh_device_reset_handler = iscsi_eh_device_reset, + .eh_target_reset_handler = iscsi_eh_target_reset, + .sg_tablesize = BEISCSI_SGLIST_ELEMENTS, + .can_queue = BE2_IO_DEPTH, + .this_id = -1, + .max_sectors = BEISCSI_MAX_SECTORS, + .cmd_per_lun = BEISCSI_CMD_PER_LUN, + .use_clustering = ENABLE_CLUSTERING, +}; +static struct scsi_transport_template *beiscsi_scsi_transport; + +/*------------------- PCI Driver operations and data ----------------- */ +static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = { + { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) }, + { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) }, + { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table); + +static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev) +{ + struct beiscsi_hba *phba; + struct Scsi_Host *shost; + + shost = iscsi_host_alloc(&beiscsi_sht, sizeof(*phba), 0); + if (!shost) { + dev_err(&pcidev->dev, "beiscsi_hba_alloc -" + "iscsi_host_alloc failed \n"); + return NULL; + } + shost->dma_boundary = pcidev->dma_mask; + shost->max_id = BE2_MAX_SESSIONS; + shost->max_channel = 0; + shost->max_cmd_len = BEISCSI_MAX_CMD_LEN; + shost->max_lun = BEISCSI_NUM_MAX_LUN; + shost->transportt = beiscsi_scsi_transport; + + phba = iscsi_host_priv(shost); + memset(phba, 0, sizeof(*phba)); + phba->shost = shost; + phba->pcidev = pci_dev_get(pcidev); + + if (iscsi_host_add(shost, &phba->pcidev->dev)) + goto free_devices; + return phba; + +free_devices: + pci_dev_put(phba->pcidev); + iscsi_host_free(phba->shost); + return NULL; +} + +static void beiscsi_unmap_pci_function(struct beiscsi_hba *phba) +{ + if (phba->csr_va) { + iounmap(phba->csr_va); + phba->csr_va = NULL; + } + if (phba->db_va) { + iounmap(phba->db_va); + phba->db_va = NULL; + } + if (phba->pci_va) { + iounmap(phba->pci_va); + phba->pci_va = NULL; + } +} + +static int beiscsi_map_pci_bars(struct beiscsi_hba *phba, + struct pci_dev *pcidev) +{ + u8 __iomem *addr; + + addr = ioremap_nocache(pci_resource_start(pcidev, 2), + pci_resource_len(pcidev, 2)); + if (addr == NULL) + return -ENOMEM; + phba->ctrl.csr = addr; + phba->csr_va = addr; + phba->csr_pa.u.a64.address = pci_resource_start(pcidev, 2); + + addr = ioremap_nocache(pci_resource_start(pcidev, 4), 128 * 1024); + if (addr == NULL) + goto pci_map_err; + phba->ctrl.db = addr; + phba->db_va = addr; + phba->db_pa.u.a64.address = pci_resource_start(pcidev, 4); + + addr = ioremap_nocache(pci_resource_start(pcidev, 1), + pci_resource_len(pcidev, 1)); + if (addr == NULL) + goto pci_map_err; + phba->ctrl.pcicfg = addr; + phba->pci_va = addr; + phba->pci_pa.u.a64.address = pci_resource_start(pcidev, 1); + return 0; + +pci_map_err: + beiscsi_unmap_pci_function(phba); + return -ENOMEM; +} + +static int beiscsi_enable_pci(struct pci_dev *pcidev) +{ + int ret; + + ret = pci_enable_device(pcidev); + if (ret) { + dev_err(&pcidev->dev, "beiscsi_enable_pci - enable device " + "failed. Returning -ENODEV\n"); + return ret; + } + + if (pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64))) { + ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pcidev->dev, "Could not set PCI DMA Mask\n"); + pci_disable_device(pcidev); + return ret; + } + } + return 0; +} + +static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_dma_mem *mbox_mem_alloc = &ctrl->mbox_mem_alloced; + struct be_dma_mem *mbox_mem_align = &ctrl->mbox_mem; + int status = 0; + + ctrl->pdev = pdev; + status = beiscsi_map_pci_bars(phba, pdev); + if (status) + return status; + + mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16; + mbox_mem_alloc->va = pci_alloc_consistent(pdev, + mbox_mem_alloc->size, + &mbox_mem_alloc->dma); + if (!mbox_mem_alloc->va) { + beiscsi_unmap_pci_function(phba); + status = -ENOMEM; + return status; + } + + mbox_mem_align->size = sizeof(struct be_mcc_mailbox); + mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16); + mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16); + memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox)); + spin_lock_init(&ctrl->mbox_lock); + return status; +} + +static void beiscsi_get_params(struct beiscsi_hba *phba) +{ + phba->params.ios_per_ctrl = BE2_IO_DEPTH; + phba->params.cxns_per_ctrl = BE2_MAX_SESSIONS; + phba->params.asyncpdus_per_ctrl = BE2_ASYNCPDUS; + phba->params.icds_per_ctrl = BE2_MAX_ICDS / 2; + phba->params.num_sge_per_io = BE2_SGE; + phba->params.defpdu_hdr_sz = BE2_DEFPDU_HDR_SZ; + phba->params.defpdu_data_sz = BE2_DEFPDU_DATA_SZ; + phba->params.eq_timer = 64; + phba->params.num_eq_entries = + (((BE2_CMDS_PER_CXN * 2 + BE2_LOGOUTS + BE2_TMFS + BE2_ASYNCPDUS) / + 512) + 1) * 512; + phba->params.num_eq_entries = (phba->params.num_eq_entries < 1024) + ? 1024 : phba->params.num_eq_entries; + SE_DEBUG(DBG_LVL_8, "phba->params.num_eq_entries=%d \n", + phba->params.num_eq_entries); + phba->params.num_cq_entries = + (((BE2_CMDS_PER_CXN * 2 + BE2_LOGOUTS + BE2_TMFS + BE2_ASYNCPDUS) / + 512) + 1) * 512; + SE_DEBUG(DBG_LVL_8, + "phba->params.num_cq_entries=%d BE2_CMDS_PER_CXN=%d" + "BE2_LOGOUTS=%d BE2_TMFS=%d BE2_ASYNCPDUS=%d \n", + phba->params.num_cq_entries, BE2_CMDS_PER_CXN, + BE2_LOGOUTS, BE2_TMFS, BE2_ASYNCPDUS); + phba->params.wrbs_per_cxn = 256; +} + +static void hwi_ring_eq_db(struct beiscsi_hba *phba, + unsigned int id, unsigned int clr_interrupt, + unsigned int num_processed, + unsigned char rearm, unsigned char event) +{ + u32 val = 0; + val |= id & DB_EQ_RING_ID_MASK; + if (rearm) + val |= 1 << DB_EQ_REARM_SHIFT; + if (clr_interrupt) + val |= 1 << DB_EQ_CLR_SHIFT; + if (event) + val |= 1 << DB_EQ_EVNT_SHIFT; + val |= num_processed << DB_EQ_NUM_POPPED_SHIFT; + iowrite32(val, phba->db_va + DB_EQ_OFFSET); +} + +/** + * be_isr - The isr routine of the driver. + * @irq: Not used + * @dev_id: Pointer to host adapter structure + */ +static irqreturn_t be_isr(int irq, void *dev_id) +{ + struct beiscsi_hba *phba; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_eq_entry *eqe = NULL; + struct be_queue_info *eq; + struct be_queue_info *cq; + unsigned long flags, index; + unsigned int num_eq_processed; + struct be_ctrl_info *ctrl; + int isr; + + phba = dev_id; + if (!enable_msix) { + ctrl = &phba->ctrl;; + isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET + + (PCI_FUNC(ctrl->pdev->devfn) * CEV_ISR_SIZE)); + if (!isr) + return IRQ_NONE; + } + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + eq = &phwi_context->be_eq.q; + cq = &phwi_context->be_cq; + index = 0; + eqe = queue_tail_node(eq); + if (!eqe) + SE_DEBUG(DBG_LVL_1, "eqe is NULL\n"); + + num_eq_processed = 0; + if (blk_iopoll_enabled) { + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + if (!blk_iopoll_sched_prep(&phba->iopoll)) + blk_iopoll_sched(&phba->iopoll); + + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + num_eq_processed++; + SE_DEBUG(DBG_LVL_8, "Valid EQE\n"); + } + if (num_eq_processed) { + hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 0, 1); + return IRQ_HANDLED; + } else + return IRQ_NONE; + } else { + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + + if (((eqe->dw[offsetof(struct amap_eq_entry, + resource_id) / 32] & + EQE_RESID_MASK) >> 16) != cq->id) { + spin_lock_irqsave(&phba->isr_lock, flags); + phba->todo_mcc_cq = 1; + spin_unlock_irqrestore(&phba->isr_lock, flags); + } else { + spin_lock_irqsave(&phba->isr_lock, flags); + phba->todo_cq = 1; + spin_unlock_irqrestore(&phba->isr_lock, flags); + } + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + num_eq_processed++; + } + if (phba->todo_cq || phba->todo_mcc_cq) + queue_work(phba->wq, &phba->work_cqs); + + if (num_eq_processed) { + hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 1, 1); + return IRQ_HANDLED; + } else + return IRQ_NONE; + } +} + +static int beiscsi_init_irqs(struct beiscsi_hba *phba) +{ + struct pci_dev *pcidev = phba->pcidev; + int ret; + + ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED, "beiscsi", phba); + if (ret) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-" + "Failed to register irq\\n"); + return ret; + } + return 0; +} + +static void hwi_ring_cq_db(struct beiscsi_hba *phba, + unsigned int id, unsigned int num_processed, + unsigned char rearm, unsigned char event) +{ + u32 val = 0; + val |= id & DB_CQ_RING_ID_MASK; + if (rearm) + val |= 1 << DB_CQ_REARM_SHIFT; + val |= num_processed << DB_CQ_NUM_POPPED_SHIFT; + iowrite32(val, phba->db_va + DB_CQ_OFFSET); +} + +/* + * async pdus include + * a. unsolicited NOP-In (target initiated NOP-In) + * b. Async Messages + * c. Reject PDU + * d. Login response + * These headers arrive unprocessed by the EP firmware and iSCSI layer + * process them + */ +static unsigned int +beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, + unsigned short cid, + struct pdu_base *ppdu, + unsigned long pdu_len, + void *pbuffer, unsigned long buf_len) +{ + struct iscsi_conn *conn = beiscsi_conn->conn; + struct iscsi_session *session = conn->session; + + switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] & + PDUBASE_OPCODE_MASK) { + case ISCSI_OP_NOOP_IN: + pbuffer = NULL; + buf_len = 0; + break; + case ISCSI_OP_ASYNC_EVENT: + break; + case ISCSI_OP_REJECT: + WARN_ON(!pbuffer); + WARN_ON(!(buf_len == 48)); + SE_DEBUG(DBG_LVL_1, "In ISCSI_OP_REJECT\n"); + break; + case ISCSI_OP_LOGIN_RSP: + break; + default: + shost_printk(KERN_WARNING, phba->shost, + "Unrecognized opcode 0x%x in async msg \n", + (ppdu-> + dw[offsetof(struct amap_pdu_base, opcode) / 32] + & PDUBASE_OPCODE_MASK)); + return 1; + } + + spin_lock_bh(&session->lock); + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)ppdu, pbuffer, buf_len); + spin_unlock_bh(&session->lock); + return 0; +} + +static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba) +{ + struct sgl_handle *psgl_handle; + + if (phba->io_sgl_hndl_avbl) { + SE_DEBUG(DBG_LVL_8, + "In alloc_io_sgl_handle,io_sgl_alloc_index=%d \n", + phba->io_sgl_alloc_index); + psgl_handle = phba->io_sgl_hndl_base[phba-> + io_sgl_alloc_index]; + phba->io_sgl_hndl_base[phba->io_sgl_alloc_index] = NULL; + phba->io_sgl_hndl_avbl--; + if (phba->io_sgl_alloc_index == (phba->params.ios_per_ctrl - 1)) + phba->io_sgl_alloc_index = 0; + else + phba->io_sgl_alloc_index++; + } else + psgl_handle = NULL; + return psgl_handle; +} + +static void +free_io_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle) +{ + SE_DEBUG(DBG_LVL_8, "In free_,io_sgl_free_index=%d \n", + phba->io_sgl_free_index); + if (phba->io_sgl_hndl_base[phba->io_sgl_free_index]) { + /* + * this can happen if clean_task is called on a task that + * failed in xmit_task or alloc_pdu. + */ + SE_DEBUG(DBG_LVL_8, + "Double Free in IO SGL io_sgl_free_index=%d," + "value there=%p \n", phba->io_sgl_free_index, + phba->io_sgl_hndl_base[phba->io_sgl_free_index]); + return; + } + phba->io_sgl_hndl_base[phba->io_sgl_free_index] = psgl_handle; + phba->io_sgl_hndl_avbl++; + if (phba->io_sgl_free_index == (phba->params.ios_per_ctrl - 1)) + phba->io_sgl_free_index = 0; + else + phba->io_sgl_free_index++; +} + +/** + * alloc_wrb_handle - To allocate a wrb handle + * @phba: The hba pointer + * @cid: The cid to use for allocation + * @index: index allocation and wrb index + * + * This happens under session_lock until submission to chip + */ +struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid, + int index) +{ + struct hwi_wrb_context *pwrb_context; + struct hwi_controller *phwi_ctrlr; + struct wrb_handle *pwrb_handle; + + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = &phwi_ctrlr->wrb_context[cid]; + pwrb_handle = pwrb_context->pwrb_handle_base[index]; + pwrb_handle->wrb_index = index; + pwrb_handle->nxt_wrb_index = index; + return pwrb_handle; +} + +/** + * free_wrb_handle - To free the wrb handle back to pool + * @phba: The hba pointer + * @pwrb_context: The context to free from + * @pwrb_handle: The wrb_handle to free + * + * This happens under session_lock until submission to chip + */ +static void +free_wrb_handle(struct beiscsi_hba *phba, struct hwi_wrb_context *pwrb_context, + struct wrb_handle *pwrb_handle) +{ + SE_DEBUG(DBG_LVL_8, + "FREE WRB: pwrb_handle=%p free_index=%d=0x%x" + "wrb_handles_available=%d \n", + pwrb_handle, pwrb_context->free_index, + pwrb_context->free_index, pwrb_context->wrb_handles_available); +} + +static struct sgl_handle *alloc_mgmt_sgl_handle(struct beiscsi_hba *phba) +{ + struct sgl_handle *psgl_handle; + + if (phba->eh_sgl_hndl_avbl) { + psgl_handle = phba->eh_sgl_hndl_base[phba->eh_sgl_alloc_index]; + phba->eh_sgl_hndl_base[phba->eh_sgl_alloc_index] = NULL; + SE_DEBUG(DBG_LVL_8, "mgmt_sgl_alloc_index=%d=0x%x \n", + phba->eh_sgl_alloc_index, phba->eh_sgl_alloc_index); + phba->eh_sgl_hndl_avbl--; + if (phba->eh_sgl_alloc_index == + (phba->params.icds_per_ctrl - phba->params.ios_per_ctrl - + 1)) + phba->eh_sgl_alloc_index = 0; + else + phba->eh_sgl_alloc_index++; + } else + psgl_handle = NULL; + return psgl_handle; +} + +void +free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle) +{ + + if (phba->eh_sgl_hndl_base[phba->eh_sgl_free_index]) { + /* + * this can happen if clean_task is called on a task that + * failed in xmit_task or alloc_pdu. + */ + SE_DEBUG(DBG_LVL_8, + "Double Free in eh SGL ,eh_sgl_free_index=%d \n", + phba->eh_sgl_free_index); + return; + } + phba->eh_sgl_hndl_base[phba->eh_sgl_free_index] = psgl_handle; + phba->eh_sgl_hndl_avbl++; + if (phba->eh_sgl_free_index == + (phba->params.icds_per_ctrl - phba->params.ios_per_ctrl - 1)) + phba->eh_sgl_free_index = 0; + else + phba->eh_sgl_free_index++; +} + +static void +be_complete_io(struct beiscsi_conn *beiscsi_conn, + struct iscsi_task *task, struct sol_cqe *psol) +{ + struct beiscsi_io_task *io_task = task->dd_data; + struct be_status_bhs *sts_bhs = + (struct be_status_bhs *)io_task->cmd_bhs; + struct iscsi_conn *conn = beiscsi_conn->conn; + unsigned int sense_len; + unsigned char *sense; + u32 resid = 0, exp_cmdsn, max_cmdsn; + u8 rsp, status, flags; + + exp_cmdsn = be32_to_cpu(psol-> + dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32] + & SOL_EXP_CMD_SN_MASK); + max_cmdsn = be32_to_cpu((psol-> + dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32] + & SOL_EXP_CMD_SN_MASK) + + ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) + / 32] & SOL_CMD_WND_MASK) >> 24) - 1); + rsp = ((psol->dw[offsetof(struct amap_sol_cqe, i_resp) / 32] + & SOL_RESP_MASK) >> 16); + status = ((psol->dw[offsetof(struct amap_sol_cqe, i_sts) / 32] + & SOL_STS_MASK) >> 8); + flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32] + & SOL_FLAGS_MASK) >> 24) | 0x80; + + task->sc->result = (DID_OK << 16) | status; + if (rsp != ISCSI_STATUS_CMD_COMPLETED) { + task->sc->result = DID_ERROR << 16; + goto unmap; + } + + /* bidi not initially supported */ + if (flags & (ISCSI_FLAG_CMD_UNDERFLOW | ISCSI_FLAG_CMD_OVERFLOW)) { + resid = (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) / + 32] & SOL_RES_CNT_MASK); + + if (!status && (flags & ISCSI_FLAG_CMD_OVERFLOW)) + task->sc->result = DID_ERROR << 16; + + if (flags & ISCSI_FLAG_CMD_UNDERFLOW) { + scsi_set_resid(task->sc, resid); + if (!status && (scsi_bufflen(task->sc) - resid < + task->sc->underflow)) + task->sc->result = DID_ERROR << 16; + } + } + + if (status == SAM_STAT_CHECK_CONDITION) { + sense = sts_bhs->sense_info + sizeof(unsigned short); + sense_len = + cpu_to_be16((unsigned short)(sts_bhs->sense_info[0])); + memcpy(task->sc->sense_buffer, sense, + min_t(u16, sense_len, SCSI_SENSE_BUFFERSIZE)); + } + if (io_task->cmd_bhs->iscsi_hdr.flags & ISCSI_FLAG_CMD_READ) { + if (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32] + & SOL_RES_CNT_MASK) + conn->rxdata_octets += (psol-> + dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32] + & SOL_RES_CNT_MASK); + } +unmap: + scsi_dma_unmap(io_task->scsi_cmnd); + iscsi_complete_scsi_task(task, exp_cmdsn, max_cmdsn); +} + +static void +be_complete_logout(struct beiscsi_conn *beiscsi_conn, + struct iscsi_task *task, struct sol_cqe *psol) +{ + struct iscsi_logout_rsp *hdr; + struct iscsi_conn *conn = beiscsi_conn->conn; + + hdr = (struct iscsi_logout_rsp *)task->hdr; + hdr->t2wait = 5; + hdr->t2retain = 0; + hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32] + & SOL_FLAGS_MASK) >> 24) | 0x80; + hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) / + 32] & SOL_RESP_MASK); + hdr->exp_cmdsn = cpu_to_be32(psol-> + dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32] + & SOL_EXP_CMD_SN_MASK); + hdr->max_cmdsn = be32_to_cpu((psol-> + dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32] + & SOL_EXP_CMD_SN_MASK) + + ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) + / 32] & SOL_CMD_WND_MASK) >> 24) - 1); + hdr->hlength = 0; + + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); +} + +static void +be_complete_tmf(struct beiscsi_conn *beiscsi_conn, + struct iscsi_task *task, struct sol_cqe *psol) +{ + struct iscsi_tm_rsp *hdr; + struct iscsi_conn *conn = beiscsi_conn->conn; + + hdr = (struct iscsi_tm_rsp *)task->hdr; + hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32] + & SOL_FLAGS_MASK) >> 24) | 0x80; + hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) / + 32] & SOL_RESP_MASK); + hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe, + i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK); + hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe, + i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) + + ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) + / 32] & SOL_CMD_WND_MASK) >> 24) - 1); + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); +} + +static void +hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, struct sol_cqe *psol) +{ + struct hwi_wrb_context *pwrb_context; + struct wrb_handle *pwrb_handle; + struct hwi_controller *phwi_ctrlr; + struct iscsi_conn *conn = beiscsi_conn->conn; + struct iscsi_session *session = conn->session; + + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = &phwi_ctrlr->wrb_context[((psol-> + dw[offsetof(struct amap_sol_cqe, cid) / 32] & + SOL_CID_MASK) >> 6)]; + pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol-> + dw[offsetof(struct amap_sol_cqe, wrb_index) / + 32] & SOL_WRB_INDEX_MASK) >> 16)]; + spin_lock_bh(&session->lock); + free_wrb_handle(phba, pwrb_context, pwrb_handle); + spin_unlock_bh(&session->lock); +} + +static void +be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn, + struct iscsi_task *task, struct sol_cqe *psol) +{ + struct iscsi_nopin *hdr; + struct iscsi_conn *conn = beiscsi_conn->conn; + + hdr = (struct iscsi_nopin *)task->hdr; + hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32] + & SOL_FLAGS_MASK) >> 24) | 0x80; + hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe, + i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK); + hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe, + i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) + + ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd) + / 32] & SOL_CMD_WND_MASK) >> 24) - 1); + hdr->opcode = ISCSI_OP_NOOP_IN; + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); +} + +static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, struct sol_cqe *psol) +{ + struct hwi_wrb_context *pwrb_context; + struct wrb_handle *pwrb_handle; + struct iscsi_wrb *pwrb = NULL; + struct hwi_controller *phwi_ctrlr; + struct iscsi_task *task; + struct beiscsi_io_task *io_task; + struct iscsi_conn *conn = beiscsi_conn->conn; + struct iscsi_session *session = conn->session; + + phwi_ctrlr = phba->phwi_ctrlr; + + pwrb_context = &phwi_ctrlr-> + wrb_context[((psol->dw[offsetof(struct amap_sol_cqe, cid) / 32] + & SOL_CID_MASK) >> 6)]; + pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol-> + dw[offsetof(struct amap_sol_cqe, wrb_index) / + 32] & SOL_WRB_INDEX_MASK) >> 16)]; + + task = pwrb_handle->pio_handle; + io_task = task->dd_data; + spin_lock_bh(&session->lock); + pwrb = pwrb_handle->pwrb; + switch ((pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] & + WRB_TYPE_MASK) >> 28) { + case HWH_TYPE_IO: + case HWH_TYPE_IO_RD: + if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == + ISCSI_OP_NOOP_OUT) { + be_complete_nopin_resp(beiscsi_conn, task, psol); + } else + be_complete_io(beiscsi_conn, task, psol); + break; + + case HWH_TYPE_LOGOUT: + be_complete_logout(beiscsi_conn, task, psol); + break; + + case HWH_TYPE_LOGIN: + SE_DEBUG(DBG_LVL_1, + "\t\t No HWH_TYPE_LOGIN Expected in hwi_complete_cmd" + "- Solicited path \n"); + break; + + case HWH_TYPE_TMF: + be_complete_tmf(beiscsi_conn, task, psol); + break; + + case HWH_TYPE_NOP: + be_complete_nopin_resp(beiscsi_conn, task, psol); + break; + + default: + shost_printk(KERN_WARNING, phba->shost, + "wrb_index 0x%x CID 0x%x\n", + ((psol->dw[offsetof(struct amap_iscsi_wrb, type) / + 32] & SOL_WRB_INDEX_MASK) >> 16), + ((psol->dw[offsetof(struct amap_sol_cqe, cid) / 32] + & SOL_CID_MASK) >> 6)); + break; + } + + spin_unlock_bh(&session->lock); +} + +static struct list_head *hwi_get_async_busy_list(struct hwi_async_pdu_context + *pasync_ctx, unsigned int is_header, + unsigned int host_write_ptr) +{ + if (is_header) + return &pasync_ctx->async_entry[host_write_ptr]. + header_busy_list; + else + return &pasync_ctx->async_entry[host_write_ptr].data_busy_list; +} + +static struct async_pdu_handle * +hwi_get_async_handle(struct beiscsi_hba *phba, + struct beiscsi_conn *beiscsi_conn, + struct hwi_async_pdu_context *pasync_ctx, + struct i_t_dpdu_cqe *pdpdu_cqe, unsigned int *pcq_index) +{ + struct be_bus_address phys_addr; + struct list_head *pbusy_list; + struct async_pdu_handle *pasync_handle = NULL; + int buffer_len = 0; + unsigned char buffer_index = -1; + unsigned char is_header = 0; + + phys_addr.u.a32.address_lo = + pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, db_addr_lo) / 32] - + ((pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, dpl) / 32] + & PDUCQE_DPL_MASK) >> 16); + phys_addr.u.a32.address_hi = + pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, db_addr_hi) / 32]; + + phys_addr.u.a64.address = + *((unsigned long long *)(&phys_addr.u.a64.address)); + + switch (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, code) / 32] + & PDUCQE_CODE_MASK) { + case UNSOL_HDR_NOTIFY: + is_header = 1; + + pbusy_list = hwi_get_async_busy_list(pasync_ctx, 1, + (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, + index) / 32] & PDUCQE_INDEX_MASK)); + + buffer_len = (unsigned int)(phys_addr.u.a64.address - + pasync_ctx->async_header.pa_base.u.a64.address); + + buffer_index = buffer_len / + pasync_ctx->async_header.buffer_size; + + break; + case UNSOL_DATA_NOTIFY: + pbusy_list = hwi_get_async_busy_list(pasync_ctx, 0, (pdpdu_cqe-> + dw[offsetof(struct amap_i_t_dpdu_cqe, + index) / 32] & PDUCQE_INDEX_MASK)); + buffer_len = (unsigned long)(phys_addr.u.a64.address - + pasync_ctx->async_data.pa_base.u. + a64.address); + buffer_index = buffer_len / pasync_ctx->async_data.buffer_size; + break; + default: + pbusy_list = NULL; + shost_printk(KERN_WARNING, phba->shost, + "Unexpected code=%d \n", + pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, + code) / 32] & PDUCQE_CODE_MASK); + return NULL; + } + + WARN_ON(!(buffer_index <= pasync_ctx->async_data.num_entries)); + WARN_ON(list_empty(pbusy_list)); + list_for_each_entry(pasync_handle, pbusy_list, link) { + WARN_ON(pasync_handle->consumed); + if (pasync_handle->index == buffer_index) + break; + } + + WARN_ON(!pasync_handle); + + pasync_handle->cri = (unsigned short)beiscsi_conn->beiscsi_conn_cid; + pasync_handle->is_header = is_header; + pasync_handle->buffer_len = ((pdpdu_cqe-> + dw[offsetof(struct amap_i_t_dpdu_cqe, dpl) / 32] + & PDUCQE_DPL_MASK) >> 16); + + *pcq_index = (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, + index) / 32] & PDUCQE_INDEX_MASK); + return pasync_handle; +} + +static unsigned int +hwi_update_async_writables(struct hwi_async_pdu_context *pasync_ctx, + unsigned int is_header, unsigned int cq_index) +{ + struct list_head *pbusy_list; + struct async_pdu_handle *pasync_handle; + unsigned int num_entries, writables = 0; + unsigned int *pep_read_ptr, *pwritables; + + + if (is_header) { + pep_read_ptr = &pasync_ctx->async_header.ep_read_ptr; + pwritables = &pasync_ctx->async_header.writables; + num_entries = pasync_ctx->async_header.num_entries; + } else { + pep_read_ptr = &pasync_ctx->async_data.ep_read_ptr; + pwritables = &pasync_ctx->async_data.writables; + num_entries = pasync_ctx->async_data.num_entries; + } + + while ((*pep_read_ptr) != cq_index) { + (*pep_read_ptr)++; + *pep_read_ptr = (*pep_read_ptr) % num_entries; + + pbusy_list = hwi_get_async_busy_list(pasync_ctx, is_header, + *pep_read_ptr); + if (writables == 0) + WARN_ON(list_empty(pbusy_list)); + + if (!list_empty(pbusy_list)) { + pasync_handle = list_entry(pbusy_list->next, + struct async_pdu_handle, + link); + WARN_ON(!pasync_handle); + pasync_handle->consumed = 1; + } + + writables++; + } + + if (!writables) { + SE_DEBUG(DBG_LVL_1, + "Duplicate notification received - index 0x%x!!\n", + cq_index); + WARN_ON(1); + } + + *pwritables = *pwritables + writables; + return 0; +} + +static unsigned int hwi_free_async_msg(struct beiscsi_hba *phba, + unsigned int cri) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_async_pdu_context *pasync_ctx; + struct async_pdu_handle *pasync_handle, *tmp_handle; + struct list_head *plist; + unsigned int i = 0; + + phwi_ctrlr = phba->phwi_ctrlr; + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr); + + plist = &pasync_ctx->async_entry[cri].wait_queue.list; + + list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) { + list_del(&pasync_handle->link); + + if (i == 0) { + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_header.free_list); + pasync_ctx->async_header.free_entries++; + i++; + } else { + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_data.free_list); + pasync_ctx->async_data.free_entries++; + i++; + } + } + + INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wait_queue.list); + pasync_ctx->async_entry[cri].wait_queue.hdr_received = 0; + pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0; + return 0; +} + +static struct phys_addr * +hwi_get_ring_address(struct hwi_async_pdu_context *pasync_ctx, + unsigned int is_header, unsigned int host_write_ptr) +{ + struct phys_addr *pasync_sge = NULL; + + if (is_header) + pasync_sge = pasync_ctx->async_header.ring_base; + else + pasync_sge = pasync_ctx->async_data.ring_base; + + return pasync_sge + host_write_ptr; +} + +static void hwi_post_async_buffers(struct beiscsi_hba *phba, + unsigned int is_header) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_async_pdu_context *pasync_ctx; + struct async_pdu_handle *pasync_handle; + struct list_head *pfree_link, *pbusy_list; + struct phys_addr *pasync_sge; + unsigned int ring_id, num_entries; + unsigned int host_write_num; + unsigned int writables; + unsigned int i = 0; + u32 doorbell = 0; + + phwi_ctrlr = phba->phwi_ctrlr; + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr); + + if (is_header) { + num_entries = pasync_ctx->async_header.num_entries; + writables = min(pasync_ctx->async_header.writables, + pasync_ctx->async_header.free_entries); + pfree_link = pasync_ctx->async_header.free_list.next; + host_write_num = pasync_ctx->async_header.host_write_ptr; + ring_id = phwi_ctrlr->default_pdu_hdr.id; + } else { + num_entries = pasync_ctx->async_data.num_entries; + writables = min(pasync_ctx->async_data.writables, + pasync_ctx->async_data.free_entries); + pfree_link = pasync_ctx->async_data.free_list.next; + host_write_num = pasync_ctx->async_data.host_write_ptr; + ring_id = phwi_ctrlr->default_pdu_data.id; + } + + writables = (writables / 8) * 8; + if (writables) { + for (i = 0; i < writables; i++) { + pbusy_list = + hwi_get_async_busy_list(pasync_ctx, is_header, + host_write_num); + pasync_handle = + list_entry(pfree_link, struct async_pdu_handle, + link); + WARN_ON(!pasync_handle); + pasync_handle->consumed = 0; + + pfree_link = pfree_link->next; + + pasync_sge = hwi_get_ring_address(pasync_ctx, + is_header, host_write_num); + + pasync_sge->hi = pasync_handle->pa.u.a32.address_lo; + pasync_sge->lo = pasync_handle->pa.u.a32.address_hi; + + list_move(&pasync_handle->link, pbusy_list); + + host_write_num++; + host_write_num = host_write_num % num_entries; + } + + if (is_header) { + pasync_ctx->async_header.host_write_ptr = + host_write_num; + pasync_ctx->async_header.free_entries -= writables; + pasync_ctx->async_header.writables -= writables; + pasync_ctx->async_header.busy_entries += writables; + } else { + pasync_ctx->async_data.host_write_ptr = host_write_num; + pasync_ctx->async_data.free_entries -= writables; + pasync_ctx->async_data.writables -= writables; + pasync_ctx->async_data.busy_entries += writables; + } + + doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK; + doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT; + doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT; + doorbell |= (writables & DB_DEF_PDU_CQPROC_MASK) + << DB_DEF_PDU_CQPROC_SHIFT; + + iowrite32(doorbell, phba->db_va + DB_RXULP0_OFFSET); + } +} + +static void hwi_flush_default_pdu_buffer(struct beiscsi_hba *phba, + struct beiscsi_conn *beiscsi_conn, + struct i_t_dpdu_cqe *pdpdu_cqe) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_async_pdu_context *pasync_ctx; + struct async_pdu_handle *pasync_handle = NULL; + unsigned int cq_index = -1; + + phwi_ctrlr = phba->phwi_ctrlr; + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr); + + pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx, + pdpdu_cqe, &cq_index); + BUG_ON(pasync_handle->is_header != 0); + if (pasync_handle->consumed == 0) + hwi_update_async_writables(pasync_ctx, pasync_handle->is_header, + cq_index); + + hwi_free_async_msg(phba, pasync_handle->cri); + hwi_post_async_buffers(phba, pasync_handle->is_header); +} + +static unsigned int +hwi_fwd_async_msg(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, + struct hwi_async_pdu_context *pasync_ctx, unsigned short cri) +{ + struct list_head *plist; + struct async_pdu_handle *pasync_handle; + void *phdr = NULL; + unsigned int hdr_len = 0, buf_len = 0; + unsigned int status, index = 0, offset = 0; + void *pfirst_buffer = NULL; + unsigned int num_buf = 0; + + plist = &pasync_ctx->async_entry[cri].wait_queue.list; + + list_for_each_entry(pasync_handle, plist, link) { + if (index == 0) { + phdr = pasync_handle->pbuffer; + hdr_len = pasync_handle->buffer_len; + } else { + buf_len = pasync_handle->buffer_len; + if (!num_buf) { + pfirst_buffer = pasync_handle->pbuffer; + num_buf++; + } + memcpy(pfirst_buffer + offset, + pasync_handle->pbuffer, buf_len); + offset = buf_len; + } + index++; + } + + status = beiscsi_process_async_pdu(beiscsi_conn, phba, + beiscsi_conn->beiscsi_conn_cid, + phdr, hdr_len, pfirst_buffer, + buf_len); + + if (status == 0) + hwi_free_async_msg(phba, cri); + return 0; +} + +static unsigned int +hwi_gather_async_pdu(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, + struct async_pdu_handle *pasync_handle) +{ + struct hwi_async_pdu_context *pasync_ctx; + struct hwi_controller *phwi_ctrlr; + unsigned int bytes_needed = 0, status = 0; + unsigned short cri = pasync_handle->cri; + struct pdu_base *ppdu; + + phwi_ctrlr = phba->phwi_ctrlr; + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr); + + list_del(&pasync_handle->link); + if (pasync_handle->is_header) { + pasync_ctx->async_header.busy_entries--; + if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) { + hwi_free_async_msg(phba, cri); + BUG(); + } + + pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0; + pasync_ctx->async_entry[cri].wait_queue.hdr_received = 1; + pasync_ctx->async_entry[cri].wait_queue.hdr_len = + (unsigned short)pasync_handle->buffer_len; + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_entry[cri].wait_queue.list); + + ppdu = pasync_handle->pbuffer; + bytes_needed = ((((ppdu->dw[offsetof(struct amap_pdu_base, + data_len_hi) / 32] & PDUBASE_DATALENHI_MASK) << 8) & + 0xFFFF0000) | ((be16_to_cpu((ppdu-> + dw[offsetof(struct amap_pdu_base, data_len_lo) / 32] + & PDUBASE_DATALENLO_MASK) >> 16)) & 0x0000FFFF)); + + if (status == 0) { + pasync_ctx->async_entry[cri].wait_queue.bytes_needed = + bytes_needed; + + if (bytes_needed == 0) + status = hwi_fwd_async_msg(beiscsi_conn, phba, + pasync_ctx, cri); + } + } else { + pasync_ctx->async_data.busy_entries--; + if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) { + list_add_tail(&pasync_handle->link, + &pasync_ctx->async_entry[cri].wait_queue. + list); + pasync_ctx->async_entry[cri].wait_queue. + bytes_received += + (unsigned short)pasync_handle->buffer_len; + + if (pasync_ctx->async_entry[cri].wait_queue. + bytes_received >= + pasync_ctx->async_entry[cri].wait_queue. + bytes_needed) + status = hwi_fwd_async_msg(beiscsi_conn, phba, + pasync_ctx, cri); + } + } + return status; +} + +static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, + struct i_t_dpdu_cqe *pdpdu_cqe) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_async_pdu_context *pasync_ctx; + struct async_pdu_handle *pasync_handle = NULL; + unsigned int cq_index = -1; + + phwi_ctrlr = phba->phwi_ctrlr; + pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr); + pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx, + pdpdu_cqe, &cq_index); + + if (pasync_handle->consumed == 0) + hwi_update_async_writables(pasync_ctx, pasync_handle->is_header, + cq_index); + hwi_gather_async_pdu(beiscsi_conn, phba, pasync_handle); + hwi_post_async_buffers(phba, pasync_handle->is_header); +} + +static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_queue_info *cq; + struct sol_cqe *sol; + struct dmsg_cqe *dmsg; + unsigned int num_processed = 0; + unsigned int tot_nump = 0; + struct beiscsi_conn *beiscsi_conn; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + cq = &phwi_context->be_cq; + sol = queue_tail_node(cq); + + while (sol->dw[offsetof(struct amap_sol_cqe, valid) / 32] & + CQE_VALID_MASK) { + be_dws_le_to_cpu(sol, sizeof(struct sol_cqe)); + + beiscsi_conn = phba->conn_table[(u32) (sol-> + dw[offsetof(struct amap_sol_cqe, cid) / 32] & + SOL_CID_MASK) >> 6]; + + if (!beiscsi_conn || !beiscsi_conn->ep) { + shost_printk(KERN_WARNING, phba->shost, + "Connection table empty for cid = %d\n", + (u32)(sol->dw[offsetof(struct amap_sol_cqe, + cid) / 32] & SOL_CID_MASK) >> 6); + return 0; + } + + if (num_processed >= 32) { + hwi_ring_cq_db(phba, phwi_context->be_cq.id, + num_processed, 0, 0); + tot_nump += num_processed; + num_processed = 0; + } + + switch ((u32) sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK) { + case SOL_CMD_COMPLETE: + hwi_complete_cmd(beiscsi_conn, phba, sol); + break; + case DRIVERMSG_NOTIFY: + SE_DEBUG(DBG_LVL_8, "Received DRIVERMSG_NOTIFY \n"); + dmsg = (struct dmsg_cqe *)sol; + hwi_complete_drvr_msgs(beiscsi_conn, phba, sol); + break; + case UNSOL_HDR_NOTIFY: + case UNSOL_DATA_NOTIFY: + SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR/DATA_NOTIFY\n"); + hwi_process_default_pdu_ring(beiscsi_conn, phba, + (struct i_t_dpdu_cqe *)sol); + break; + case CXN_INVALIDATE_INDEX_NOTIFY: + case CMD_INVALIDATED_NOTIFY: + case CXN_INVALIDATE_NOTIFY: + SE_DEBUG(DBG_LVL_1, + "Ignoring CQ Error notification for cmd/cxn" + "invalidate\n"); + break; + case SOL_CMD_KILLED_DATA_DIGEST_ERR: + case CMD_KILLED_INVALID_STATSN_RCVD: + case CMD_KILLED_INVALID_R2T_RCVD: + case CMD_CXN_KILLED_LUN_INVALID: + case CMD_CXN_KILLED_ICD_INVALID: + case CMD_CXN_KILLED_ITT_INVALID: + case CMD_CXN_KILLED_SEQ_OUTOFORDER: + case CMD_CXN_KILLED_INVALID_DATASN_RCVD: + SE_DEBUG(DBG_LVL_1, + "CQ Error notification for cmd.. " + "code %d cid 0x%x\n", + sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK, + (sol->dw[offsetof(struct amap_sol_cqe, cid) / + 32] & SOL_CID_MASK)); + break; + case UNSOL_DATA_DIGEST_ERROR_NOTIFY: + SE_DEBUG(DBG_LVL_1, + "Digest error on def pdu ring, dropping..\n"); + hwi_flush_default_pdu_buffer(phba, beiscsi_conn, + (struct i_t_dpdu_cqe *) sol); + break; + case CXN_KILLED_PDU_SIZE_EXCEEDS_DSL: + case CXN_KILLED_BURST_LEN_MISMATCH: + case CXN_KILLED_AHS_RCVD: + case CXN_KILLED_HDR_DIGEST_ERR: + case CXN_KILLED_UNKNOWN_HDR: + case CXN_KILLED_STALE_ITT_TTT_RCVD: + case CXN_KILLED_INVALID_ITT_TTT_RCVD: + case CXN_KILLED_TIMED_OUT: + case CXN_KILLED_FIN_RCVD: + case CXN_KILLED_BAD_UNSOL_PDU_RCVD: + case CXN_KILLED_BAD_WRB_INDEX_ERROR: + case CXN_KILLED_OVER_RUN_RESIDUAL: + case CXN_KILLED_UNDER_RUN_RESIDUAL: + case CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN: + SE_DEBUG(DBG_LVL_1, "CQ Error %d, resetting CID " + "0x%x...\n", + sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK, + sol->dw[offsetof(struct amap_sol_cqe, cid) / + 32] & CQE_CID_MASK); + iscsi_conn_failure(beiscsi_conn->conn, + ISCSI_ERR_CONN_FAILED); + break; + case CXN_KILLED_RST_SENT: + case CXN_KILLED_RST_RCVD: + SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset received/sent " + "on CID 0x%x...\n", + sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK, + sol->dw[offsetof(struct amap_sol_cqe, cid) / + 32] & CQE_CID_MASK); + iscsi_conn_failure(beiscsi_conn->conn, + ISCSI_ERR_CONN_FAILED); + break; + default: + SE_DEBUG(DBG_LVL_1, "CQ Error Invalid code= %d " + "received on CID 0x%x...\n", + sol->dw[offsetof(struct amap_sol_cqe, code) / + 32] & CQE_CODE_MASK, + sol->dw[offsetof(struct amap_sol_cqe, cid) / + 32] & CQE_CID_MASK); + break; + } + + AMAP_SET_BITS(struct amap_sol_cqe, valid, sol, 0); + queue_tail_inc(cq); + sol = queue_tail_node(cq); + num_processed++; + } + + if (num_processed > 0) { + tot_nump += num_processed; + hwi_ring_cq_db(phba, phwi_context->be_cq.id, num_processed, + 1, 0); + } + return tot_nump; +} + +static void beiscsi_process_all_cqs(struct work_struct *work) +{ + unsigned long flags; + struct beiscsi_hba *phba = + container_of(work, struct beiscsi_hba, work_cqs); + + if (phba->todo_mcc_cq) { + spin_lock_irqsave(&phba->isr_lock, flags); + phba->todo_mcc_cq = 0; + spin_unlock_irqrestore(&phba->isr_lock, flags); + SE_DEBUG(DBG_LVL_1, "MCC Interrupt Not expected \n"); + } + + if (phba->todo_cq) { + spin_lock_irqsave(&phba->isr_lock, flags); + phba->todo_cq = 0; + spin_unlock_irqrestore(&phba->isr_lock, flags); + beiscsi_process_cq(phba); + } +} + +static int be_iopoll(struct blk_iopoll *iop, int budget) +{ + static unsigned int ret; + struct beiscsi_hba *phba; + + phba = container_of(iop, struct beiscsi_hba, iopoll); + + ret = beiscsi_process_cq(phba); + if (ret < budget) { + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + blk_iopoll_complete(iop); + hwi_ring_eq_db(phba, phwi_context->be_eq.q.id, 0, + 0, 1, 1); + } + return ret; +} + +static void +hwi_write_sgl(struct iscsi_wrb *pwrb, struct scatterlist *sg, + unsigned int num_sg, struct beiscsi_io_task *io_task) +{ + struct iscsi_sge *psgl; + unsigned short sg_len, index; + unsigned int sge_len = 0; + unsigned long long addr; + struct scatterlist *l_sg; + unsigned int offset; + + AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_lo, pwrb, + io_task->bhs_pa.u.a32.address_lo); + AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_hi, pwrb, + io_task->bhs_pa.u.a32.address_hi); + + l_sg = sg; + for (index = 0; (index < num_sg) && (index < 2); index++, sg_next(sg)) { + if (index == 0) { + sg_len = sg_dma_len(sg); + addr = (u64) sg_dma_address(sg); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_lo, pwrb, + (addr & 0xFFFFFFFF)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_hi, pwrb, + (addr >> 32)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_len, pwrb, + sg_len); + sge_len = sg_len; + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb, + 1); + } else { + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb, + 0); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_r2t_offset, + pwrb, sge_len); + sg_len = sg_dma_len(sg); + addr = (u64) sg_dma_address(sg); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_addr_lo, pwrb, + (addr & 0xFFFFFFFF)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_addr_hi, pwrb, + (addr >> 32)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_len, pwrb, + sg_len); + } + } + psgl = (struct iscsi_sge *)io_task->psgl_handle->pfrag; + memset(psgl, 0, sizeof(*psgl) * BE2_SGE); + + AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, io_task->bhs_len - 2); + + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl, + io_task->bhs_pa.u.a32.address_hi); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl, + io_task->bhs_pa.u.a32.address_lo); + + if (num_sg == 2) + AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_last, pwrb, 1); + sg = l_sg; + psgl++; + psgl++; + offset = 0; + for (index = 0; index < num_sg; index++, sg_next(sg), psgl++) { + sg_len = sg_dma_len(sg); + addr = (u64) sg_dma_address(sg); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl, + (addr & 0xFFFFFFFF)); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl, + (addr >> 32)); + AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, sg_len); + AMAP_SET_BITS(struct amap_iscsi_sge, sge_offset, psgl, offset); + AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 0); + offset += sg_len; + } + psgl--; + AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 1); +} + +static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task) +{ + struct iscsi_sge *psgl; + unsigned long long addr; + struct beiscsi_io_task *io_task = task->dd_data; + struct beiscsi_conn *beiscsi_conn = io_task->conn; + struct beiscsi_hba *phba = beiscsi_conn->phba; + + io_task->bhs_len = sizeof(struct be_nonio_bhs) - 2; + AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_lo, pwrb, + io_task->bhs_pa.u.a32.address_lo); + AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_hi, pwrb, + io_task->bhs_pa.u.a32.address_hi); + + if (task->data) { + if (task->data_count) { + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1); + addr = (u64) pci_map_single(phba->pcidev, + task->data, + task->data_count, 1); + } else { + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0); + addr = 0; + } + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_lo, pwrb, + (addr & 0xFFFFFFFF)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_hi, pwrb, + (addr >> 32)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_len, pwrb, + task->data_count); + + AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb, 1); + } else { + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0); + addr = 0; + } + + psgl = (struct iscsi_sge *)io_task->psgl_handle->pfrag; + + AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, io_task->bhs_len); + + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl, + io_task->bhs_pa.u.a32.address_hi); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl, + io_task->bhs_pa.u.a32.address_lo); + if (task->data) { + psgl++; + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, sge_offset, psgl, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, rsvd0, psgl, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 0); + + psgl++; + if (task->data) { + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl, + (addr & 0xFFFFFFFF)); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl, + (addr >> 32)); + } + AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, 0x106); + } + AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 1); +} + +static void beiscsi_find_mem_req(struct beiscsi_hba *phba) +{ + unsigned int num_cq_pages, num_eq_pages, num_async_pdu_buf_pages; + unsigned int num_async_pdu_data_pages, wrb_sz_per_cxn; + unsigned int num_async_pdu_buf_sgl_pages, num_async_pdu_data_sgl_pages; + + num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \ + sizeof(struct sol_cqe)); + num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \ + sizeof(struct be_eq_entry)); + num_async_pdu_buf_pages = + PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \ + phba->params.defpdu_hdr_sz); + num_async_pdu_buf_sgl_pages = + PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \ + sizeof(struct phys_addr)); + num_async_pdu_data_pages = + PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \ + phba->params.defpdu_data_sz); + num_async_pdu_data_sgl_pages = + PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \ + sizeof(struct phys_addr)); + + phba->params.hwi_ws_sz = sizeof(struct hwi_controller); + + phba->mem_req[ISCSI_MEM_GLOBAL_HEADER] = 2 * + BE_ISCSI_PDU_HEADER_SIZE; + phba->mem_req[HWI_MEM_ADDN_CONTEXT] = + sizeof(struct hwi_context_memory); + + phba->mem_req[HWI_MEM_CQ] = num_cq_pages * PAGE_SIZE; + phba->mem_req[HWI_MEM_EQ] = num_eq_pages * PAGE_SIZE; + + phba->mem_req[HWI_MEM_WRB] = sizeof(struct iscsi_wrb) + * (phba->params.wrbs_per_cxn) + * phba->params.cxns_per_ctrl; + wrb_sz_per_cxn = sizeof(struct wrb_handle) * + (phba->params.wrbs_per_cxn); + phba->mem_req[HWI_MEM_WRBH] = roundup_pow_of_two((wrb_sz_per_cxn) * + phba->params.cxns_per_ctrl); + + phba->mem_req[HWI_MEM_SGLH] = sizeof(struct sgl_handle) * + phba->params.icds_per_ctrl; + phba->mem_req[HWI_MEM_SGE] = sizeof(struct iscsi_sge) * + phba->params.num_sge_per_io * phba->params.icds_per_ctrl; + + phba->mem_req[HWI_MEM_ASYNC_HEADER_BUF] = + num_async_pdu_buf_pages * PAGE_SIZE; + phba->mem_req[HWI_MEM_ASYNC_DATA_BUF] = + num_async_pdu_data_pages * PAGE_SIZE; + phba->mem_req[HWI_MEM_ASYNC_HEADER_RING] = + num_async_pdu_buf_sgl_pages * PAGE_SIZE; + phba->mem_req[HWI_MEM_ASYNC_DATA_RING] = + num_async_pdu_data_sgl_pages * PAGE_SIZE; + phba->mem_req[HWI_MEM_ASYNC_HEADER_HANDLE] = + phba->params.asyncpdus_per_ctrl * + sizeof(struct async_pdu_handle); + phba->mem_req[HWI_MEM_ASYNC_DATA_HANDLE] = + phba->params.asyncpdus_per_ctrl * + sizeof(struct async_pdu_handle); + phba->mem_req[HWI_MEM_ASYNC_PDU_CONTEXT] = + sizeof(struct hwi_async_pdu_context) + + (phba->params.cxns_per_ctrl * sizeof(struct hwi_async_entry)); +} + +static int beiscsi_alloc_mem(struct beiscsi_hba *phba) +{ + struct be_mem_descriptor *mem_descr; + dma_addr_t bus_add; + struct mem_array *mem_arr, *mem_arr_orig; + unsigned int i, j, alloc_size, curr_alloc_size; + + phba->phwi_ctrlr = kmalloc(phba->params.hwi_ws_sz, GFP_KERNEL); + if (!phba->phwi_ctrlr) + return -ENOMEM; + + phba->init_mem = kcalloc(SE_MEM_MAX, sizeof(*mem_descr), + GFP_KERNEL); + if (!phba->init_mem) { + kfree(phba->phwi_ctrlr); + return -ENOMEM; + } + + mem_arr_orig = kmalloc(sizeof(*mem_arr_orig) * BEISCSI_MAX_FRAGS_INIT, + GFP_KERNEL); + if (!mem_arr_orig) { + kfree(phba->init_mem); + kfree(phba->phwi_ctrlr); + return -ENOMEM; + } + + mem_descr = phba->init_mem; + for (i = 0; i < SE_MEM_MAX; i++) { + j = 0; + mem_arr = mem_arr_orig; + alloc_size = phba->mem_req[i]; + memset(mem_arr, 0, sizeof(struct mem_array) * + BEISCSI_MAX_FRAGS_INIT); + curr_alloc_size = min(be_max_phys_size * 1024, alloc_size); + do { + mem_arr->virtual_address = pci_alloc_consistent( + phba->pcidev, + curr_alloc_size, + &bus_add); + if (!mem_arr->virtual_address) { + if (curr_alloc_size <= BE_MIN_MEM_SIZE) + goto free_mem; + if (curr_alloc_size - + rounddown_pow_of_two(curr_alloc_size)) + curr_alloc_size = rounddown_pow_of_two + (curr_alloc_size); + else + curr_alloc_size = curr_alloc_size / 2; + } else { + mem_arr->bus_address.u. + a64.address = (__u64) bus_add; + mem_arr->size = curr_alloc_size; + alloc_size -= curr_alloc_size; + curr_alloc_size = min(be_max_phys_size * + 1024, alloc_size); + j++; + mem_arr++; + } + } while (alloc_size); + mem_descr->num_elements = j; + mem_descr->size_in_bytes = phba->mem_req[i]; + mem_descr->mem_array = kmalloc(sizeof(*mem_arr) * j, + GFP_KERNEL); + if (!mem_descr->mem_array) + goto free_mem; + + memcpy(mem_descr->mem_array, mem_arr_orig, + sizeof(struct mem_array) * j); + mem_descr++; + } + kfree(mem_arr_orig); + return 0; +free_mem: + mem_descr->num_elements = j; + while ((i) || (j)) { + for (j = mem_descr->num_elements; j > 0; j--) { + pci_free_consistent(phba->pcidev, + mem_descr->mem_array[j - 1].size, + mem_descr->mem_array[j - 1]. + virtual_address, + mem_descr->mem_array[j - 1]. + bus_address.u.a64.address); + } + if (i) { + i--; + kfree(mem_descr->mem_array); + mem_descr--; + } + } + kfree(mem_arr_orig); + kfree(phba->init_mem); + kfree(phba->phwi_ctrlr); + return -ENOMEM; +} + +static int beiscsi_get_memory(struct beiscsi_hba *phba) +{ + beiscsi_find_mem_req(phba); + return beiscsi_alloc_mem(phba); +} + +static void iscsi_init_global_templates(struct beiscsi_hba *phba) +{ + struct pdu_data_out *pdata_out; + struct pdu_nop_out *pnop_out; + struct be_mem_descriptor *mem_descr; + + mem_descr = phba->init_mem; + mem_descr += ISCSI_MEM_GLOBAL_HEADER; + pdata_out = + (struct pdu_data_out *)mem_descr->mem_array[0].virtual_address; + memset(pdata_out, 0, BE_ISCSI_PDU_HEADER_SIZE); + + AMAP_SET_BITS(struct amap_pdu_data_out, opcode, pdata_out, + IIOC_SCSI_DATA); + + pnop_out = + (struct pdu_nop_out *)((unsigned char *)mem_descr->mem_array[0]. + virtual_address + BE_ISCSI_PDU_HEADER_SIZE); + + memset(pnop_out, 0, BE_ISCSI_PDU_HEADER_SIZE); + AMAP_SET_BITS(struct amap_pdu_nop_out, ttt, pnop_out, 0xFFFFFFFF); + AMAP_SET_BITS(struct amap_pdu_nop_out, f_bit, pnop_out, 1); + AMAP_SET_BITS(struct amap_pdu_nop_out, i_bit, pnop_out, 0); +} + +static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba) +{ + struct be_mem_descriptor *mem_descr_wrbh, *mem_descr_wrb; + struct wrb_handle *pwrb_handle; + struct hwi_controller *phwi_ctrlr; + struct hwi_wrb_context *pwrb_context; + struct iscsi_wrb *pwrb; + unsigned int num_cxn_wrbh; + unsigned int num_cxn_wrb, j, idx, index; + + mem_descr_wrbh = phba->init_mem; + mem_descr_wrbh += HWI_MEM_WRBH; + + mem_descr_wrb = phba->init_mem; + mem_descr_wrb += HWI_MEM_WRB; + + idx = 0; + pwrb_handle = mem_descr_wrbh->mem_array[idx].virtual_address; + num_cxn_wrbh = ((mem_descr_wrbh->mem_array[idx].size) / + ((sizeof(struct wrb_handle)) * + phba->params.wrbs_per_cxn)); + phwi_ctrlr = phba->phwi_ctrlr; + + for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) { + pwrb_context = &phwi_ctrlr->wrb_context[index]; + SE_DEBUG(DBG_LVL_8, "cid=%d pwrb_context=%p \n", index, + pwrb_context); + pwrb_context->pwrb_handle_base = + kzalloc(sizeof(struct wrb_handle *) * + phba->params.wrbs_per_cxn, GFP_KERNEL); + pwrb_context->pwrb_handle_basestd = + kzalloc(sizeof(struct wrb_handle *) * + phba->params.wrbs_per_cxn, GFP_KERNEL); + if (num_cxn_wrbh) { + pwrb_context->alloc_index = 0; + pwrb_context->wrb_handles_available = 0; + for (j = 0; j < phba->params.wrbs_per_cxn; j++) { + pwrb_context->pwrb_handle_base[j] = pwrb_handle; + pwrb_context->pwrb_handle_basestd[j] = + pwrb_handle; + pwrb_context->wrb_handles_available++; + pwrb_handle++; + } + pwrb_context->free_index = 0; + num_cxn_wrbh--; + } else { + idx++; + pwrb_handle = + mem_descr_wrbh->mem_array[idx].virtual_address; + num_cxn_wrbh = + ((mem_descr_wrbh->mem_array[idx].size) / + ((sizeof(struct wrb_handle)) * + phba->params.wrbs_per_cxn)); + pwrb_context->alloc_index = 0; + for (j = 0; j < phba->params.wrbs_per_cxn; j++) { + pwrb_context->pwrb_handle_base[j] = pwrb_handle; + pwrb_context->pwrb_handle_basestd[j] = + pwrb_handle; + pwrb_context->wrb_handles_available++; + pwrb_handle++; + } + pwrb_context->free_index = 0; + num_cxn_wrbh--; + } + } + idx = 0; + pwrb = mem_descr_wrb->mem_array[idx].virtual_address; + num_cxn_wrb = + ((mem_descr_wrb->mem_array[idx].size) / (sizeof(struct iscsi_wrb)) * + phba->params.wrbs_per_cxn); + + for (index = 0; index < phba->params.cxns_per_ctrl; index += 2) { + pwrb_context = &phwi_ctrlr->wrb_context[index]; + if (num_cxn_wrb) { + for (j = 0; j < phba->params.wrbs_per_cxn; j++) { + pwrb_handle = pwrb_context->pwrb_handle_base[j]; + pwrb_handle->pwrb = pwrb; + pwrb++; + } + num_cxn_wrb--; + } else { + idx++; + pwrb = mem_descr_wrb->mem_array[idx].virtual_address; + num_cxn_wrb = ((mem_descr_wrb->mem_array[idx].size) / + (sizeof(struct iscsi_wrb)) * + phba->params.wrbs_per_cxn); + for (j = 0; j < phba->params.wrbs_per_cxn; j++) { + pwrb_handle = pwrb_context->pwrb_handle_base[j]; + pwrb_handle->pwrb = pwrb; + pwrb++; + } + num_cxn_wrb--; + } + } +} + +static void hwi_init_async_pdu_ctx(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + struct hba_parameters *p = &phba->params; + struct hwi_async_pdu_context *pasync_ctx; + struct async_pdu_handle *pasync_header_h, *pasync_data_h; + unsigned int index; + struct be_mem_descriptor *mem_descr; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_PDU_CONTEXT; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_ctrlr->phwi_ctxt->pasync_ctx = (struct hwi_async_pdu_context *) + mem_descr->mem_array[0].virtual_address; + pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx; + memset(pasync_ctx, 0, sizeof(*pasync_ctx)); + + pasync_ctx->async_header.num_entries = p->asyncpdus_per_ctrl; + pasync_ctx->async_header.buffer_size = p->defpdu_hdr_sz; + pasync_ctx->async_data.buffer_size = p->defpdu_data_sz; + pasync_ctx->async_data.num_entries = p->asyncpdus_per_ctrl; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_HEADER_BUF; + if (mem_descr->mem_array[0].virtual_address) { + SE_DEBUG(DBG_LVL_8, + "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_BUF" + "va=%p \n", mem_descr->mem_array[0].virtual_address); + } else + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + + pasync_ctx->async_header.va_base = + mem_descr->mem_array[0].virtual_address; + + pasync_ctx->async_header.pa_base.u.a64.address = + mem_descr->mem_array[0].bus_address.u.a64.address; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_HEADER_RING; + if (mem_descr->mem_array[0].virtual_address) { + SE_DEBUG(DBG_LVL_8, + "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_RING" + "va=%p \n", mem_descr->mem_array[0].virtual_address); + } else + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + pasync_ctx->async_header.ring_base = + mem_descr->mem_array[0].virtual_address; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_HEADER_HANDLE; + if (mem_descr->mem_array[0].virtual_address) { + SE_DEBUG(DBG_LVL_8, + "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_HANDLE" + "va=%p \n", mem_descr->mem_array[0].virtual_address); + } else + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + + pasync_ctx->async_header.handle_base = + mem_descr->mem_array[0].virtual_address; + pasync_ctx->async_header.writables = 0; + INIT_LIST_HEAD(&pasync_ctx->async_header.free_list); + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_DATA_BUF; + if (mem_descr->mem_array[0].virtual_address) { + SE_DEBUG(DBG_LVL_8, + "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_DATA_BUF" + "va=%p \n", mem_descr->mem_array[0].virtual_address); + } else + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + pasync_ctx->async_data.va_base = + mem_descr->mem_array[0].virtual_address; + pasync_ctx->async_data.pa_base.u.a64.address = + mem_descr->mem_array[0].bus_address.u.a64.address; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_DATA_RING; + if (mem_descr->mem_array[0].virtual_address) { + SE_DEBUG(DBG_LVL_8, + "hwi_init_async_pdu_ctx HWI_MEM_ASYNC_DATA_RING" + "va=%p \n", mem_descr->mem_array[0].virtual_address); + } else + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + + pasync_ctx->async_data.ring_base = + mem_descr->mem_array[0].virtual_address; + + mem_descr = (struct be_mem_descriptor *)phba->init_mem; + mem_descr += HWI_MEM_ASYNC_DATA_HANDLE; + if (!mem_descr->mem_array[0].virtual_address) + shost_printk(KERN_WARNING, phba->shost, + "No Virtual address \n"); + + pasync_ctx->async_data.handle_base = + mem_descr->mem_array[0].virtual_address; + pasync_ctx->async_data.writables = 0; + INIT_LIST_HEAD(&pasync_ctx->async_data.free_list); + + pasync_header_h = + (struct async_pdu_handle *)pasync_ctx->async_header.handle_base; + pasync_data_h = + (struct async_pdu_handle *)pasync_ctx->async_data.handle_base; + + for (index = 0; index < p->asyncpdus_per_ctrl; index++) { + pasync_header_h->cri = -1; + pasync_header_h->index = (char)index; + INIT_LIST_HEAD(&pasync_header_h->link); + pasync_header_h->pbuffer = + (void *)((unsigned long) + (pasync_ctx->async_header.va_base) + + (p->defpdu_hdr_sz * index)); + + pasync_header_h->pa.u.a64.address = + pasync_ctx->async_header.pa_base.u.a64.address + + (p->defpdu_hdr_sz * index); + + list_add_tail(&pasync_header_h->link, + &pasync_ctx->async_header.free_list); + pasync_header_h++; + pasync_ctx->async_header.free_entries++; + pasync_ctx->async_header.writables++; + + INIT_LIST_HEAD(&pasync_ctx->async_entry[index].wait_queue.list); + INIT_LIST_HEAD(&pasync_ctx->async_entry[index]. + header_busy_list); + pasync_data_h->cri = -1; + pasync_data_h->index = (char)index; + INIT_LIST_HEAD(&pasync_data_h->link); + pasync_data_h->pbuffer = + (void *)((unsigned long) + (pasync_ctx->async_data.va_base) + + (p->defpdu_data_sz * index)); + + pasync_data_h->pa.u.a64.address = + pasync_ctx->async_data.pa_base.u.a64.address + + (p->defpdu_data_sz * index); + + list_add_tail(&pasync_data_h->link, + &pasync_ctx->async_data.free_list); + pasync_data_h++; + pasync_ctx->async_data.free_entries++; + pasync_ctx->async_data.writables++; + + INIT_LIST_HEAD(&pasync_ctx->async_entry[index].data_busy_list); + } + + pasync_ctx->async_header.host_write_ptr = 0; + pasync_ctx->async_header.ep_read_ptr = -1; + pasync_ctx->async_data.host_write_ptr = 0; + pasync_ctx->async_data.ep_read_ptr = -1; +} + +static int +be_sgl_create_contiguous(void *virtual_address, + u64 physical_address, u32 length, + struct be_dma_mem *sgl) +{ + WARN_ON(!virtual_address); + WARN_ON(!physical_address); + WARN_ON(!length > 0); + WARN_ON(!sgl); + + sgl->va = virtual_address; + sgl->dma = physical_address; + sgl->size = length; + + return 0; +} + +static void be_sgl_destroy_contiguous(struct be_dma_mem *sgl) +{ + memset(sgl, 0, sizeof(*sgl)); +} + +static void +hwi_build_be_sgl_arr(struct beiscsi_hba *phba, + struct mem_array *pmem, struct be_dma_mem *sgl) +{ + if (sgl->va) + be_sgl_destroy_contiguous(sgl); + + be_sgl_create_contiguous(pmem->virtual_address, + pmem->bus_address.u.a64.address, + pmem->size, sgl); +} + +static void +hwi_build_be_sgl_by_offset(struct beiscsi_hba *phba, + struct mem_array *pmem, struct be_dma_mem *sgl) +{ + if (sgl->va) + be_sgl_destroy_contiguous(sgl); + + be_sgl_create_contiguous((unsigned char *)pmem->virtual_address, + pmem->bus_address.u.a64.address, + pmem->size, sgl); +} + +static int be_fill_queue(struct be_queue_info *q, + u16 len, u16 entry_size, void *vaddress) +{ + struct be_dma_mem *mem = &q->dma_mem; + + memset(q, 0, sizeof(*q)); + q->len = len; + q->entry_size = entry_size; + mem->size = len * entry_size; + mem->va = vaddress; + if (!mem->va) + return -ENOMEM; + memset(mem->va, 0, mem->size); + return 0; +} + +static int beiscsi_create_eq(struct beiscsi_hba *phba, + struct hwi_context_memory *phwi_context) +{ + unsigned int idx; + int ret; + struct be_queue_info *eq; + struct be_dma_mem *mem; + struct be_mem_descriptor *mem_descr; + void *eq_vaddress; + + idx = 0; + eq = &phwi_context->be_eq.q; + mem = &eq->dma_mem; + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_EQ; + eq_vaddress = mem_descr->mem_array[idx].virtual_address; + + ret = be_fill_queue(eq, phba->params.num_eq_entries, + sizeof(struct be_eq_entry), eq_vaddress); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_fill_queue Failed for EQ \n"); + return ret; + } + + mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address; + + ret = beiscsi_cmd_eq_create(&phba->ctrl, eq, + phwi_context->be_eq.cur_eqd); + if (ret) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_cmd_eq_create" + "Failedfor EQ \n"); + return ret; + } + SE_DEBUG(DBG_LVL_8, "eq id is %d\n", phwi_context->be_eq.q.id); + return 0; +} + +static int beiscsi_create_cq(struct beiscsi_hba *phba, + struct hwi_context_memory *phwi_context) +{ + unsigned int idx; + int ret; + struct be_queue_info *cq, *eq; + struct be_dma_mem *mem; + struct be_mem_descriptor *mem_descr; + void *cq_vaddress; + + idx = 0; + cq = &phwi_context->be_cq; + eq = &phwi_context->be_eq.q; + mem = &cq->dma_mem; + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_CQ; + cq_vaddress = mem_descr->mem_array[idx].virtual_address; + ret = be_fill_queue(cq, phba->params.icds_per_ctrl / 2, + sizeof(struct sol_cqe), cq_vaddress); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_fill_queue Failed for ISCSI CQ \n"); + return ret; + } + + mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address; + ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false, false, 0); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "beiscsi_cmd_eq_create Failed for ISCSI CQ \n"); + return ret; + } + SE_DEBUG(DBG_LVL_8, "iscsi cq id is %d\n", phwi_context->be_cq.id); + SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n"); + return 0; +} + +static int +beiscsi_create_def_hdr(struct beiscsi_hba *phba, + struct hwi_context_memory *phwi_context, + struct hwi_controller *phwi_ctrlr, + unsigned int def_pdu_ring_sz) +{ + unsigned int idx; + int ret; + struct be_queue_info *dq, *cq; + struct be_dma_mem *mem; + struct be_mem_descriptor *mem_descr; + void *dq_vaddress; + + idx = 0; + dq = &phwi_context->be_def_hdrq; + cq = &phwi_context->be_cq; + mem = &dq->dma_mem; + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_ASYNC_HEADER_RING; + dq_vaddress = mem_descr->mem_array[idx].virtual_address; + ret = be_fill_queue(dq, mem_descr->mem_array[0].size / + sizeof(struct phys_addr), + sizeof(struct phys_addr), dq_vaddress); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_fill_queue Failed for DEF PDU HDR\n"); + return ret; + } + mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address; + ret = be_cmd_create_default_pdu_queue(&phba->ctrl, cq, dq, + def_pdu_ring_sz, + phba->params.defpdu_hdr_sz); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_cmd_create_default_pdu_queue Failed DEFHDR\n"); + return ret; + } + phwi_ctrlr->default_pdu_hdr.id = phwi_context->be_def_hdrq.id; + SE_DEBUG(DBG_LVL_8, "iscsi def pdu id is %d\n", + phwi_context->be_def_hdrq.id); + hwi_post_async_buffers(phba, 1); + return 0; +} + +static int +beiscsi_create_def_data(struct beiscsi_hba *phba, + struct hwi_context_memory *phwi_context, + struct hwi_controller *phwi_ctrlr, + unsigned int def_pdu_ring_sz) +{ + unsigned int idx; + int ret; + struct be_queue_info *dataq, *cq; + struct be_dma_mem *mem; + struct be_mem_descriptor *mem_descr; + void *dq_vaddress; + + idx = 0; + dataq = &phwi_context->be_def_dataq; + cq = &phwi_context->be_cq; + mem = &dataq->dma_mem; + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_ASYNC_DATA_RING; + dq_vaddress = mem_descr->mem_array[idx].virtual_address; + ret = be_fill_queue(dataq, mem_descr->mem_array[0].size / + sizeof(struct phys_addr), + sizeof(struct phys_addr), dq_vaddress); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_fill_queue Failed for DEF PDU DATA\n"); + return ret; + } + mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address; + ret = be_cmd_create_default_pdu_queue(&phba->ctrl, cq, dataq, + def_pdu_ring_sz, + phba->params.defpdu_data_sz); + if (ret) { + shost_printk(KERN_ERR, phba->shost, + "be_cmd_create_default_pdu_queue Failed" + " for DEF PDU DATA\n"); + return ret; + } + phwi_ctrlr->default_pdu_data.id = phwi_context->be_def_dataq.id; + SE_DEBUG(DBG_LVL_8, "iscsi def data id is %d\n", + phwi_context->be_def_dataq.id); + hwi_post_async_buffers(phba, 0); + SE_DEBUG(DBG_LVL_8, "DEFAULT PDU DATA RING CREATED \n"); + return 0; +} + +static int +beiscsi_post_pages(struct beiscsi_hba *phba) +{ + struct be_mem_descriptor *mem_descr; + struct mem_array *pm_arr; + unsigned int page_offset, i; + struct be_dma_mem sgl; + int status; + + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_SGE; + pm_arr = mem_descr->mem_array; + + page_offset = (sizeof(struct iscsi_sge) * phba->params.num_sge_per_io * + phba->fw_config.iscsi_icd_start) / PAGE_SIZE; + for (i = 0; i < mem_descr->num_elements; i++) { + hwi_build_be_sgl_arr(phba, pm_arr, &sgl); + status = be_cmd_iscsi_post_sgl_pages(&phba->ctrl, &sgl, + page_offset, + (pm_arr->size / PAGE_SIZE)); + page_offset += pm_arr->size / PAGE_SIZE; + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "post sgl failed.\n"); + return status; + } + pm_arr++; + } + SE_DEBUG(DBG_LVL_8, "POSTED PAGES \n"); + return 0; +} + +static int +beiscsi_create_wrb_rings(struct beiscsi_hba *phba, + struct hwi_context_memory *phwi_context, + struct hwi_controller *phwi_ctrlr) +{ + unsigned int wrb_mem_index, offset, size, num_wrb_rings; + u64 pa_addr_lo; + unsigned int idx, num, i; + struct mem_array *pwrb_arr; + void *wrb_vaddr; + struct be_dma_mem sgl; + struct be_mem_descriptor *mem_descr; + int status; + + idx = 0; + mem_descr = phba->init_mem; + mem_descr += HWI_MEM_WRB; + pwrb_arr = kmalloc(sizeof(*pwrb_arr) * phba->params.cxns_per_ctrl, + GFP_KERNEL); + if (!pwrb_arr) { + shost_printk(KERN_ERR, phba->shost, + "Memory alloc failed in create wrb ring.\n"); + return -ENOMEM; + } + wrb_vaddr = mem_descr->mem_array[idx].virtual_address; + pa_addr_lo = mem_descr->mem_array[idx].bus_address.u.a64.address; + num_wrb_rings = mem_descr->mem_array[idx].size / + (phba->params.wrbs_per_cxn * sizeof(struct iscsi_wrb)); + + for (num = 0; num < phba->params.cxns_per_ctrl; num++) { + if (num_wrb_rings) { + pwrb_arr[num].virtual_address = wrb_vaddr; + pwrb_arr[num].bus_address.u.a64.address = pa_addr_lo; + pwrb_arr[num].size = phba->params.wrbs_per_cxn * + sizeof(struct iscsi_wrb); + wrb_vaddr += pwrb_arr[num].size; + pa_addr_lo += pwrb_arr[num].size; + num_wrb_rings--; + } else { + idx++; + wrb_vaddr = mem_descr->mem_array[idx].virtual_address; + pa_addr_lo = mem_descr->mem_array[idx].\ + bus_address.u.a64.address; + num_wrb_rings = mem_descr->mem_array[idx].size / + (phba->params.wrbs_per_cxn * + sizeof(struct iscsi_wrb)); + pwrb_arr[num].virtual_address = wrb_vaddr; + pwrb_arr[num].bus_address.u.a64.address\ + = pa_addr_lo; + pwrb_arr[num].size = phba->params.wrbs_per_cxn * + sizeof(struct iscsi_wrb); + wrb_vaddr += pwrb_arr[num].size; + pa_addr_lo += pwrb_arr[num].size; + num_wrb_rings--; + } + } + for (i = 0; i < phba->params.cxns_per_ctrl; i++) { + wrb_mem_index = 0; + offset = 0; + size = 0; + + hwi_build_be_sgl_by_offset(phba, &pwrb_arr[i], &sgl); + status = be_cmd_wrbq_create(&phba->ctrl, &sgl, + &phwi_context->be_wrbq[i]); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "wrbq create failed."); + return status; + } + phwi_ctrlr->wrb_context[i].cid = phwi_context->be_wrbq[i].id; + } + kfree(pwrb_arr); + return 0; +} + +static void free_wrb_handles(struct beiscsi_hba *phba) +{ + unsigned int index; + struct hwi_controller *phwi_ctrlr; + struct hwi_wrb_context *pwrb_context; + + phwi_ctrlr = phba->phwi_ctrlr; + for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) { + pwrb_context = &phwi_ctrlr->wrb_context[index]; + kfree(pwrb_context->pwrb_handle_base); + kfree(pwrb_context->pwrb_handle_basestd); + } +} + +static void hwi_cleanup(struct beiscsi_hba *phba) +{ + struct be_queue_info *q; + struct be_ctrl_info *ctrl = &phba->ctrl; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + int i; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + for (i = 0; i < phba->params.cxns_per_ctrl; i++) { + q = &phwi_context->be_wrbq[i]; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ); + } + + free_wrb_handles(phba); + + q = &phwi_context->be_def_hdrq; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); + + q = &phwi_context->be_def_dataq; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ); + + beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL); + + q = &phwi_context->be_cq; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ); + + q = &phwi_context->be_eq.q; + if (q->created) + beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ); +} + +static int hwi_init_port(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + unsigned int def_pdu_ring_sz; + struct be_ctrl_info *ctrl = &phba->ctrl; + int status; + + def_pdu_ring_sz = + phba->params.asyncpdus_per_ctrl * sizeof(struct phys_addr); + phwi_ctrlr = phba->phwi_ctrlr; + + phwi_context = phwi_ctrlr->phwi_ctxt; + phwi_context->be_eq.max_eqd = 0; + phwi_context->be_eq.min_eqd = 0; + phwi_context->be_eq.cur_eqd = 64; + phwi_context->be_eq.enable_aic = false; + be_cmd_fw_initialize(&phba->ctrl); + status = beiscsi_create_eq(phba, phwi_context); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, "EQ not created \n"); + goto error; + } + + status = mgmt_check_supported_fw(ctrl); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "Unsupported fw version \n"); + goto error; + } + + status = mgmt_get_fw_config(ctrl, phba); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "Error getting fw config\n"); + goto error; + } + + status = beiscsi_create_cq(phba, phwi_context); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, "CQ not created\n"); + goto error; + } + + status = beiscsi_create_def_hdr(phba, phwi_context, phwi_ctrlr, + def_pdu_ring_sz); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "Default Header not created\n"); + goto error; + } + + status = beiscsi_create_def_data(phba, phwi_context, + phwi_ctrlr, def_pdu_ring_sz); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "Default Data not created\n"); + goto error; + } + + status = beiscsi_post_pages(phba); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, "Post SGL Pages Failed\n"); + goto error; + } + + status = beiscsi_create_wrb_rings(phba, phwi_context, phwi_ctrlr); + if (status != 0) { + shost_printk(KERN_ERR, phba->shost, + "WRB Rings not created\n"); + goto error; + } + + SE_DEBUG(DBG_LVL_8, "hwi_init_port success\n"); + return 0; + +error: + shost_printk(KERN_ERR, phba->shost, "hwi_init_port failed"); + hwi_cleanup(phba); + return -ENOMEM; +} + + +static int hwi_init_controller(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + + phwi_ctrlr = phba->phwi_ctrlr; + if (1 == phba->init_mem[HWI_MEM_ADDN_CONTEXT].num_elements) { + phwi_ctrlr->phwi_ctxt = (struct hwi_context_memory *)phba-> + init_mem[HWI_MEM_ADDN_CONTEXT].mem_array[0].virtual_address; + SE_DEBUG(DBG_LVL_8, " phwi_ctrlr->phwi_ctxt=%p \n", + phwi_ctrlr->phwi_ctxt); + } else { + shost_printk(KERN_ERR, phba->shost, + "HWI_MEM_ADDN_CONTEXT is more than one element." + "Failing to load\n"); + return -ENOMEM; + } + + iscsi_init_global_templates(phba); + beiscsi_init_wrb_handle(phba); + hwi_init_async_pdu_ctx(phba); + if (hwi_init_port(phba) != 0) { + shost_printk(KERN_ERR, phba->shost, + "hwi_init_controller failed\n"); + return -ENOMEM; + } + return 0; +} + +static void beiscsi_free_mem(struct beiscsi_hba *phba) +{ + struct be_mem_descriptor *mem_descr; + int i, j; + + mem_descr = phba->init_mem; + i = 0; + j = 0; + for (i = 0; i < SE_MEM_MAX; i++) { + for (j = mem_descr->num_elements; j > 0; j--) { + pci_free_consistent(phba->pcidev, + mem_descr->mem_array[j - 1].size, + mem_descr->mem_array[j - 1].virtual_address, + mem_descr->mem_array[j - 1].bus_address. + u.a64.address); + } + kfree(mem_descr->mem_array); + mem_descr++; + } + kfree(phba->init_mem); + kfree(phba->phwi_ctrlr); +} + +static int beiscsi_init_controller(struct beiscsi_hba *phba) +{ + int ret = -ENOMEM; + + ret = beiscsi_get_memory(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe -" + "Failed in beiscsi_alloc_memory \n"); + return ret; + } + + ret = hwi_init_controller(phba); + if (ret) + goto free_init; + SE_DEBUG(DBG_LVL_8, "Return success from beiscsi_init_controller"); + return 0; + +free_init: + beiscsi_free_mem(phba); + return -ENOMEM; +} + +static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba) +{ + struct be_mem_descriptor *mem_descr_sglh, *mem_descr_sg; + struct sgl_handle *psgl_handle; + struct iscsi_sge *pfrag; + unsigned int arr_index, i, idx; + + phba->io_sgl_hndl_avbl = 0; + phba->eh_sgl_hndl_avbl = 0; + mem_descr_sglh = phba->init_mem; + mem_descr_sglh += HWI_MEM_SGLH; + if (1 == mem_descr_sglh->num_elements) { + phba->io_sgl_hndl_base = kzalloc(sizeof(struct sgl_handle *) * + phba->params.ios_per_ctrl, + GFP_KERNEL); + if (!phba->io_sgl_hndl_base) { + shost_printk(KERN_ERR, phba->shost, + "Mem Alloc Failed. Failing to load\n"); + return -ENOMEM; + } + phba->eh_sgl_hndl_base = kzalloc(sizeof(struct sgl_handle *) * + (phba->params.icds_per_ctrl - + phba->params.ios_per_ctrl), + GFP_KERNEL); + if (!phba->eh_sgl_hndl_base) { + kfree(phba->io_sgl_hndl_base); + shost_printk(KERN_ERR, phba->shost, + "Mem Alloc Failed. Failing to load\n"); + return -ENOMEM; + } + } else { + shost_printk(KERN_ERR, phba->shost, + "HWI_MEM_SGLH is more than one element." + "Failing to load\n"); + return -ENOMEM; + } + + arr_index = 0; + idx = 0; + while (idx < mem_descr_sglh->num_elements) { + psgl_handle = mem_descr_sglh->mem_array[idx].virtual_address; + + for (i = 0; i < (mem_descr_sglh->mem_array[idx].size / + sizeof(struct sgl_handle)); i++) { + if (arr_index < phba->params.ios_per_ctrl) { + phba->io_sgl_hndl_base[arr_index] = psgl_handle; + phba->io_sgl_hndl_avbl++; + arr_index++; + } else { + phba->eh_sgl_hndl_base[arr_index - + phba->params.ios_per_ctrl] = + psgl_handle; + arr_index++; + phba->eh_sgl_hndl_avbl++; + } + psgl_handle++; + } + idx++; + } + SE_DEBUG(DBG_LVL_8, + "phba->io_sgl_hndl_avbl=%d" + "phba->eh_sgl_hndl_avbl=%d \n", + phba->io_sgl_hndl_avbl, + phba->eh_sgl_hndl_avbl); + mem_descr_sg = phba->init_mem; + mem_descr_sg += HWI_MEM_SGE; + SE_DEBUG(DBG_LVL_8, "\n mem_descr_sg->num_elements=%d \n", + mem_descr_sg->num_elements); + arr_index = 0; + idx = 0; + while (idx < mem_descr_sg->num_elements) { + pfrag = mem_descr_sg->mem_array[idx].virtual_address; + + for (i = 0; + i < (mem_descr_sg->mem_array[idx].size) / + (sizeof(struct iscsi_sge) * phba->params.num_sge_per_io); + i++) { + if (arr_index < phba->params.ios_per_ctrl) + psgl_handle = phba->io_sgl_hndl_base[arr_index]; + else + psgl_handle = phba->eh_sgl_hndl_base[arr_index - + phba->params.ios_per_ctrl]; + psgl_handle->pfrag = pfrag; + AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, pfrag, 0); + AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, pfrag, 0); + pfrag += phba->params.num_sge_per_io; + psgl_handle->sgl_index = + phba->fw_config.iscsi_cid_start + arr_index++; + } + idx++; + } + phba->io_sgl_free_index = 0; + phba->io_sgl_alloc_index = 0; + phba->eh_sgl_free_index = 0; + phba->eh_sgl_alloc_index = 0; + return 0; +} + +static int hba_setup_cid_tbls(struct beiscsi_hba *phba) +{ + int i, new_cid; + + phba->cid_array = kmalloc(sizeof(void *) * phba->params.cxns_per_ctrl, + GFP_KERNEL); + if (!phba->cid_array) { + shost_printk(KERN_ERR, phba->shost, + "Failed to allocate memory in " + "hba_setup_cid_tbls\n"); + return -ENOMEM; + } + phba->ep_array = kmalloc(sizeof(struct iscsi_endpoint *) * + phba->params.cxns_per_ctrl * 2, GFP_KERNEL); + if (!phba->ep_array) { + shost_printk(KERN_ERR, phba->shost, + "Failed to allocate memory in " + "hba_setup_cid_tbls \n"); + kfree(phba->cid_array); + return -ENOMEM; + } + new_cid = phba->fw_config.iscsi_icd_start; + for (i = 0; i < phba->params.cxns_per_ctrl; i++) { + phba->cid_array[i] = new_cid; + new_cid += 2; + } + phba->avlbl_cids = phba->params.cxns_per_ctrl; + return 0; +} + +static unsigned char hwi_enable_intr(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_queue_info *eq; + u8 __iomem *addr; + u32 reg; + u32 enabled; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + + eq = &phwi_context->be_eq.q; + addr = (u8 __iomem *) ((u8 __iomem *) ctrl->pcicfg + + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET); + reg = ioread32(addr); + SE_DEBUG(DBG_LVL_8, "reg =x%08x \n", reg); + + enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; + if (!enabled) { + reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; + SE_DEBUG(DBG_LVL_8, "reg =x%08x addr=%p \n", reg, addr); + iowrite32(reg, addr); + SE_DEBUG(DBG_LVL_8, "eq->id=%d \n", eq->id); + + hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1); + } else + shost_printk(KERN_WARNING, phba->shost, + "In hwi_enable_intr, Not Enabled \n"); + return true; +} + +static void hwi_disable_intr(struct beiscsi_hba *phba) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + + u8 __iomem *addr = ctrl->pcicfg + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET; + u32 reg = ioread32(addr); + + u32 enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; + if (enabled) { + reg &= ~MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; + iowrite32(reg, addr); + } else + shost_printk(KERN_WARNING, phba->shost, + "In hwi_disable_intr, Already Disabled \n"); +} + +static int beiscsi_init_port(struct beiscsi_hba *phba) +{ + int ret; + + ret = beiscsi_init_controller(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, + "beiscsi_dev_probe - Failed in" + "beiscsi_init_controller \n"); + return ret; + } + ret = beiscsi_init_sgl_handle(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, + "beiscsi_dev_probe - Failed in" + "beiscsi_init_sgl_handle \n"); + goto do_cleanup_ctrlr; + } + + if (hba_setup_cid_tbls(phba)) { + shost_printk(KERN_ERR, phba->shost, + "Failed in hba_setup_cid_tbls\n"); + kfree(phba->io_sgl_hndl_base); + kfree(phba->eh_sgl_hndl_base); + goto do_cleanup_ctrlr; + } + + return ret; + +do_cleanup_ctrlr: + hwi_cleanup(phba); + return ret; +} + +static void hwi_purge_eq(struct beiscsi_hba *phba) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct be_queue_info *eq; + struct be_eq_entry *eqe = NULL; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + eq = &phwi_context->be_eq.q; + eqe = queue_tail_node(eq); + + while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] + & EQE_VALID_MASK) { + AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); + queue_tail_inc(eq); + eqe = queue_tail_node(eq); + } +} + +static void beiscsi_clean_port(struct beiscsi_hba *phba) +{ + unsigned char mgmt_status; + + mgmt_status = mgmt_epfw_cleanup(phba, CMD_CONNECTION_CHUTE_0); + if (mgmt_status) + shost_printk(KERN_WARNING, phba->shost, + "mgmt_epfw_cleanup FAILED \n"); + hwi_cleanup(phba); + hwi_purge_eq(phba); + kfree(phba->io_sgl_hndl_base); + kfree(phba->eh_sgl_hndl_base); + kfree(phba->cid_array); + kfree(phba->ep_array); +} + +void +beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_offload_params *params) +{ + struct wrb_handle *pwrb_handle; + struct iscsi_target_context_update_wrb *pwrb = NULL; + struct be_mem_descriptor *mem_descr; + struct beiscsi_hba *phba = beiscsi_conn->phba; + u32 doorbell = 0; + + /* + * We can always use 0 here because it is reserved by libiscsi for + * login/startup related tasks. + */ + pwrb_handle = alloc_wrb_handle(phba, beiscsi_conn->beiscsi_conn_cid, 0); + pwrb = (struct iscsi_target_context_update_wrb *)pwrb_handle->pwrb; + memset(pwrb, 0, sizeof(*pwrb)); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + max_burst_length, pwrb, params->dw[offsetof + (struct amap_beiscsi_offload_params, + max_burst_length) / 32]); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + max_send_data_segment_length, pwrb, + params->dw[offsetof(struct amap_beiscsi_offload_params, + max_send_data_segment_length) / 32]); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + first_burst_length, + pwrb, + params->dw[offsetof(struct amap_beiscsi_offload_params, + first_burst_length) / 32]); + + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, erl, pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + erl) / 32] & OFFLD_PARAMS_ERL)); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, dde, pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + dde) / 32] & OFFLD_PARAMS_DDE) >> 2); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, hde, pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + hde) / 32] & OFFLD_PARAMS_HDE) >> 3); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ir2t, pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + ir2t) / 32] & OFFLD_PARAMS_IR2T) >> 4); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, imd, pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + imd) / 32] & OFFLD_PARAMS_IMD) >> 5); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, stat_sn, + pwrb, + (params->dw[offsetof(struct amap_beiscsi_offload_params, + exp_statsn) / 32] + 1)); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, type, pwrb, + 0x7); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, wrb_idx, + pwrb, pwrb_handle->wrb_index); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ptr2nextwrb, + pwrb, pwrb_handle->nxt_wrb_index); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + session_state, pwrb, 0); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, compltonack, + pwrb, 1); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, notpredblq, + pwrb, 0); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, mode, pwrb, + 0); + + mem_descr = phba->init_mem; + mem_descr += ISCSI_MEM_GLOBAL_HEADER; + + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + pad_buffer_addr_hi, pwrb, + mem_descr->mem_array[0].bus_address.u.a32.address_hi); + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + pad_buffer_addr_lo, pwrb, + mem_descr->mem_array[0].bus_address.u.a32.address_lo); + + be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_target_context_update_wrb)); + + doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK; + doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK) << + DB_DEF_PDU_WRB_INDEX_SHIFT; + doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT; + + iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET); +} + +static void beiscsi_parse_pdu(struct iscsi_conn *conn, itt_t itt, + int *index, int *age) +{ + *index = be32_to_cpu(itt) >> 16; + if (age) + *age = conn->session->age; +} + +/** + * beiscsi_alloc_pdu - allocates pdu and related resources + * @task: libiscsi task + * @opcode: opcode of pdu for task + * + * This is called with the session lock held. It will allocate + * the wrb and sgl if needed for the command. And it will prep + * the pdu's itt. beiscsi_parse_pdu will later translate + * the pdu itt to the libiscsi task itt. + */ +static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) +{ + struct beiscsi_io_task *io_task = task->dd_data; + struct iscsi_conn *conn = task->conn; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct hwi_wrb_context *pwrb_context; + struct hwi_controller *phwi_ctrlr; + itt_t itt; + + io_task->pwrb_handle = alloc_wrb_handle(phba, + beiscsi_conn->beiscsi_conn_cid, + task->itt); + io_task->pwrb_handle->pio_handle = task; + io_task->conn = beiscsi_conn; + + task->hdr = (struct iscsi_hdr *)&io_task->cmd_bhs->iscsi_hdr; + task->hdr_max = sizeof(struct be_cmd_bhs); + + if (task->sc) { + spin_lock(&phba->io_sgl_lock); + io_task->psgl_handle = alloc_io_sgl_handle(phba); + spin_unlock(&phba->io_sgl_lock); + if (!io_task->psgl_handle) { + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = &phwi_ctrlr->wrb_context + [beiscsi_conn->beiscsi_conn_cid]; + free_wrb_handle(phba, pwrb_context, + io_task->pwrb_handle); + io_task->pwrb_handle = NULL; + SE_DEBUG(DBG_LVL_1, + "Alloc of SGL_ICD Failed \n"); + return -ENOMEM; + } + } else { + io_task->scsi_cmnd = NULL; + if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) { + if (!beiscsi_conn->login_in_progress) { + spin_lock(&phba->mgmt_sgl_lock); + io_task->psgl_handle = (struct sgl_handle *) + alloc_mgmt_sgl_handle(phba); + spin_unlock(&phba->mgmt_sgl_lock); + if (!io_task->psgl_handle) { + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = + &phwi_ctrlr->wrb_context + [beiscsi_conn->beiscsi_conn_cid]; + free_wrb_handle(phba, pwrb_context, + io_task->pwrb_handle); + io_task->pwrb_handle = NULL; + SE_DEBUG(DBG_LVL_1, "Alloc of " + "MGMT_SGL_ICD Failed \n"); + return -ENOMEM; + } + beiscsi_conn->login_in_progress = 1; + beiscsi_conn->plogin_sgl_handle = + io_task->psgl_handle; + } else { + io_task->psgl_handle = + beiscsi_conn->plogin_sgl_handle; + } + } else { + spin_lock(&phba->mgmt_sgl_lock); + io_task->psgl_handle = alloc_mgmt_sgl_handle(phba); + spin_unlock(&phba->mgmt_sgl_lock); + if (!io_task->psgl_handle) { + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = &phwi_ctrlr->wrb_context + [beiscsi_conn->beiscsi_conn_cid]; + free_wrb_handle(phba, pwrb_context, + io_task->pwrb_handle); + io_task->pwrb_handle = NULL; + SE_DEBUG(DBG_LVL_1, "Alloc of " + "MGMT_SGL_ICD Failed \n"); + return -ENOMEM; + } + } + } + itt = (itt_t) cpu_to_be32(((unsigned int)task->itt << 16) | + (unsigned int)(io_task->psgl_handle->sgl_index)); + io_task->cmd_bhs->iscsi_hdr.itt = itt; + return 0; +} + +static void beiscsi_cleanup_task(struct iscsi_task *task) +{ + struct beiscsi_io_task *io_task = task->dd_data; + struct iscsi_conn *conn = task->conn; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct hwi_wrb_context *pwrb_context; + struct hwi_controller *phwi_ctrlr; + + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = &phwi_ctrlr->wrb_context[beiscsi_conn->beiscsi_conn_cid]; + if (io_task->pwrb_handle) { + free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle); + io_task->pwrb_handle = NULL; + } + + if (task->sc) { + if (io_task->psgl_handle) { + spin_lock(&phba->io_sgl_lock); + free_io_sgl_handle(phba, io_task->psgl_handle); + spin_unlock(&phba->io_sgl_lock); + io_task->psgl_handle = NULL; + } + } else { + if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) + return; + if (io_task->psgl_handle) { + spin_lock(&phba->mgmt_sgl_lock); + free_mgmt_sgl_handle(phba, io_task->psgl_handle); + spin_unlock(&phba->mgmt_sgl_lock); + io_task->psgl_handle = NULL; + } + } +} + +static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg, + unsigned int num_sg, unsigned int xferlen, + unsigned int writedir) +{ + + struct beiscsi_io_task *io_task = task->dd_data; + struct iscsi_conn *conn = task->conn; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct iscsi_wrb *pwrb = NULL; + unsigned int doorbell = 0; + + pwrb = io_task->pwrb_handle->pwrb; + + io_task->cmd_bhs->iscsi_hdr.exp_statsn = 0; + io_task->bhs_len = sizeof(struct be_cmd_bhs); + + if (writedir) { + SE_DEBUG(DBG_LVL_4, " WRITE Command \t"); + memset(&io_task->cmd_bhs->iscsi_data_pdu, 0, 48); + AMAP_SET_BITS(struct amap_pdu_data_out, itt, + &io_task->cmd_bhs->iscsi_data_pdu, + (unsigned int)io_task->cmd_bhs->iscsi_hdr.itt); + AMAP_SET_BITS(struct amap_pdu_data_out, opcode, + &io_task->cmd_bhs->iscsi_data_pdu, + ISCSI_OPCODE_SCSI_DATA_OUT); + AMAP_SET_BITS(struct amap_pdu_data_out, final_bit, + &io_task->cmd_bhs->iscsi_data_pdu, 1); + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD); + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1); + + } else { + SE_DEBUG(DBG_LVL_4, "READ Command \t"); + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD); + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0); + } + memcpy(&io_task->cmd_bhs->iscsi_data_pdu. + dw[offsetof(struct amap_pdu_data_out, lun) / 32], + io_task->cmd_bhs->iscsi_hdr.lun, sizeof(struct scsi_lun)); + + AMAP_SET_BITS(struct amap_iscsi_wrb, lun, pwrb, + cpu_to_be16((unsigned short)io_task->cmd_bhs->iscsi_hdr. + lun[0])); + AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb, xferlen); + AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb, + io_task->pwrb_handle->wrb_index); + AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, + be32_to_cpu(task->cmdsn)); + AMAP_SET_BITS(struct amap_iscsi_wrb, sgl_icd_idx, pwrb, + io_task->psgl_handle->sgl_index); + + hwi_write_sgl(pwrb, sg, num_sg, io_task); + + AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb, + io_task->pwrb_handle->nxt_wrb_index); + be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb)); + + doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK; + doorbell |= (io_task->pwrb_handle->wrb_index & + DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT; + doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT; + + iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET); + return 0; +} + +static int beiscsi_mtask(struct iscsi_task *task) +{ + struct beiscsi_io_task *aborted_io_task, *io_task = task->dd_data; + struct iscsi_conn *conn = task->conn; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct beiscsi_hba *phba = beiscsi_conn->phba; + struct iscsi_wrb *pwrb = NULL; + unsigned int doorbell = 0; + struct iscsi_task *aborted_task; + + pwrb = io_task->pwrb_handle->pwrb; + AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, + be32_to_cpu(task->cmdsn)); + AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb, + io_task->pwrb_handle->wrb_index); + AMAP_SET_BITS(struct amap_iscsi_wrb, sgl_icd_idx, pwrb, + io_task->psgl_handle->sgl_index); + + switch (task->hdr->opcode & ISCSI_OPCODE_MASK) { + case ISCSI_OP_LOGIN: + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, TGT_DM_CMD); + AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0); + AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, 1); + hwi_write_buffer(pwrb, task); + break; + case ISCSI_OP_NOOP_OUT: + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD); + hwi_write_buffer(pwrb, task); + break; + case ISCSI_OP_TEXT: + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD); + AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1); + hwi_write_buffer(pwrb, task); + break; + case ISCSI_OP_SCSI_TMFUNC: + aborted_task = iscsi_itt_to_task(conn, + ((struct iscsi_tm *)task->hdr)->rtt); + if (!aborted_task) + return 0; + aborted_io_task = aborted_task->dd_data; + if (!aborted_io_task->scsi_cmnd) + return 0; + + mgmt_invalidate_icds(phba, + aborted_io_task->psgl_handle->sgl_index, + beiscsi_conn->beiscsi_conn_cid); + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_TMF_CMD); + AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0); + hwi_write_buffer(pwrb, task); + break; + case ISCSI_OP_LOGOUT: + AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0); + AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, + HWH_TYPE_LOGOUT); + hwi_write_buffer(pwrb, task); + break; + + default: + SE_DEBUG(DBG_LVL_1, "opcode =%d Not supported \n", + task->hdr->opcode & ISCSI_OPCODE_MASK); + return -EINVAL; + } + + AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb, + be32_to_cpu(task->data_count)); + AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb, + io_task->pwrb_handle->nxt_wrb_index); + be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb)); + + doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK; + doorbell |= (io_task->pwrb_handle->wrb_index & + DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT; + doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT; + iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET); + return 0; +} + +static int beiscsi_task_xmit(struct iscsi_task *task) +{ + struct iscsi_conn *conn = task->conn; + struct beiscsi_io_task *io_task = task->dd_data; + struct scsi_cmnd *sc = task->sc; + struct beiscsi_conn *beiscsi_conn = conn->dd_data; + struct scatterlist *sg; + int num_sg; + unsigned int writedir = 0, xferlen = 0; + + SE_DEBUG(DBG_LVL_4, "\n cid=%d In beiscsi_task_xmit task=%p conn=%p \t" + "beiscsi_conn=%p \n", beiscsi_conn->beiscsi_conn_cid, + task, conn, beiscsi_conn); + if (!sc) + return beiscsi_mtask(task); + + io_task->scsi_cmnd = sc; + num_sg = scsi_dma_map(sc); + if (num_sg < 0) { + SE_DEBUG(DBG_LVL_1, " scsi_dma_map Failed\n") + return num_sg; + } + SE_DEBUG(DBG_LVL_4, "xferlen=0x%08x scmd=%p num_sg=%d sernum=%lu\n", + (scsi_bufflen(sc)), sc, num_sg, sc->serial_number); + xferlen = scsi_bufflen(sc); + sg = scsi_sglist(sc); + if (sc->sc_data_direction == DMA_TO_DEVICE) { + writedir = 1; + SE_DEBUG(DBG_LVL_4, "task->imm_count=0x%08x \n", + task->imm_count); + } else + writedir = 0; + return beiscsi_iotask(task, sg, num_sg, xferlen, writedir); +} + +static void beiscsi_remove(struct pci_dev *pcidev) +{ + struct beiscsi_hba *phba = NULL; + + phba = (struct beiscsi_hba *)pci_get_drvdata(pcidev); + if (!phba) { + dev_err(&pcidev->dev, "beiscsi_remove called with no phba \n"); + return; + } + + hwi_disable_intr(phba); + if (phba->pcidev->irq) + free_irq(phba->pcidev->irq, phba); + destroy_workqueue(phba->wq); + if (blk_iopoll_enabled) + blk_iopoll_disable(&phba->iopoll); + + beiscsi_clean_port(phba); + beiscsi_free_mem(phba); + beiscsi_unmap_pci_function(phba); + pci_free_consistent(phba->pcidev, + phba->ctrl.mbox_mem_alloced.size, + phba->ctrl.mbox_mem_alloced.va, + phba->ctrl.mbox_mem_alloced.dma); + iscsi_host_remove(phba->shost); + pci_dev_put(phba->pcidev); + iscsi_host_free(phba->shost); +} + +static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev, + const struct pci_device_id *id) +{ + struct beiscsi_hba *phba = NULL; + int ret; + + ret = beiscsi_enable_pci(pcidev); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed to enable pci device \n"); + return ret; + } + + phba = beiscsi_hba_alloc(pcidev); + if (!phba) { + dev_err(&pcidev->dev, "beiscsi_dev_probe-" + " Failed in beiscsi_hba_alloc \n"); + goto disable_pci; + } + + pci_set_drvdata(pcidev, phba); + ret = be_ctrl_init(phba, pcidev); + if (ret) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed in be_ctrl_init\n"); + goto hba_free; + } + + spin_lock_init(&phba->io_sgl_lock); + spin_lock_init(&phba->mgmt_sgl_lock); + spin_lock_init(&phba->isr_lock); + beiscsi_get_params(phba); + ret = beiscsi_init_port(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed in beiscsi_init_port\n"); + goto free_port; + } + + snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_q_irq%u", + phba->shost->host_no); + phba->wq = create_singlethread_workqueue(phba->wq_name); + if (!phba->wq) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed to allocate work queue\n"); + goto free_twq; + } + + INIT_WORK(&phba->work_cqs, beiscsi_process_all_cqs); + + if (blk_iopoll_enabled) { + blk_iopoll_init(&phba->iopoll, be_iopoll_budget, be_iopoll); + blk_iopoll_enable(&phba->iopoll); + } + + ret = beiscsi_init_irqs(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed to beiscsi_init_irqs\n"); + goto free_blkenbld; + } + ret = hwi_enable_intr(phba); + if (ret < 0) { + shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-" + "Failed to hwi_enable_intr\n"); + goto free_ctrlr; + } + + SE_DEBUG(DBG_LVL_8, "\n\n\n SUCCESS - DRIVER LOADED \n\n\n"); + return 0; + +free_ctrlr: + if (phba->pcidev->irq) + free_irq(phba->pcidev->irq, phba); +free_blkenbld: + destroy_workqueue(phba->wq); + if (blk_iopoll_enabled) + blk_iopoll_disable(&phba->iopoll); +free_twq: + beiscsi_clean_port(phba); + beiscsi_free_mem(phba); +free_port: + pci_free_consistent(phba->pcidev, + phba->ctrl.mbox_mem_alloced.size, + phba->ctrl.mbox_mem_alloced.va, + phba->ctrl.mbox_mem_alloced.dma); + beiscsi_unmap_pci_function(phba); +hba_free: + iscsi_host_remove(phba->shost); + pci_dev_put(phba->pcidev); + iscsi_host_free(phba->shost); +disable_pci: + pci_disable_device(pcidev); + return ret; +} + +struct iscsi_transport beiscsi_iscsi_transport = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .caps = CAP_RECOVERY_L0 | CAP_HDRDGST | + CAP_MULTI_R2T | CAP_DATADGST | CAP_DATA_PATH_OFFLOAD, + .param_mask = ISCSI_MAX_RECV_DLENGTH | + ISCSI_MAX_XMIT_DLENGTH | + ISCSI_HDRDGST_EN | + ISCSI_DATADGST_EN | + ISCSI_INITIAL_R2T_EN | + ISCSI_MAX_R2T | + ISCSI_IMM_DATA_EN | + ISCSI_FIRST_BURST | + ISCSI_MAX_BURST | + ISCSI_PDU_INORDER_EN | + ISCSI_DATASEQ_INORDER_EN | + ISCSI_ERL | + ISCSI_CONN_PORT | + ISCSI_CONN_ADDRESS | + ISCSI_EXP_STATSN | + ISCSI_PERSISTENT_PORT | + ISCSI_PERSISTENT_ADDRESS | + ISCSI_TARGET_NAME | ISCSI_TPGT | + ISCSI_USERNAME | ISCSI_PASSWORD | + ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | + ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | + ISCSI_LU_RESET_TMO | + ISCSI_PING_TMO | ISCSI_RECV_TMO | + ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME, + .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS | + ISCSI_HOST_INITIATOR_NAME, + .create_session = beiscsi_session_create, + .destroy_session = beiscsi_session_destroy, + .create_conn = beiscsi_conn_create, + .bind_conn = beiscsi_conn_bind, + .destroy_conn = iscsi_conn_teardown, + .set_param = beiscsi_set_param, + .get_conn_param = beiscsi_conn_get_param, + .get_session_param = iscsi_session_get_param, + .get_host_param = beiscsi_get_host_param, + .start_conn = beiscsi_conn_start, + .stop_conn = beiscsi_conn_stop, + .send_pdu = iscsi_conn_send_pdu, + .xmit_task = beiscsi_task_xmit, + .cleanup_task = beiscsi_cleanup_task, + .alloc_pdu = beiscsi_alloc_pdu, + .parse_pdu_itt = beiscsi_parse_pdu, + .get_stats = beiscsi_conn_get_stats, + .ep_connect = beiscsi_ep_connect, + .ep_poll = beiscsi_ep_poll, + .ep_disconnect = beiscsi_ep_disconnect, + .session_recovery_timedout = iscsi_session_recovery_timedout, +}; + +static struct pci_driver beiscsi_pci_driver = { + .name = DRV_NAME, + .probe = beiscsi_dev_probe, + .remove = beiscsi_remove, + .id_table = beiscsi_pci_id_table +}; + +static int __init beiscsi_module_init(void) +{ + int ret; + + beiscsi_scsi_transport = + iscsi_register_transport(&beiscsi_iscsi_transport); + if (!beiscsi_scsi_transport) { + SE_DEBUG(DBG_LVL_1, + "beiscsi_module_init - Unable to register beiscsi" + "transport.\n"); + ret = -ENOMEM; + } + SE_DEBUG(DBG_LVL_8, "In beiscsi_module_init, tt=%p \n", + &beiscsi_iscsi_transport); + + ret = pci_register_driver(&beiscsi_pci_driver); + if (ret) { + SE_DEBUG(DBG_LVL_1, + "beiscsi_module_init - Unable to register" + "beiscsi pci driver.\n"); + goto unregister_iscsi_transport; + } + return 0; + +unregister_iscsi_transport: + iscsi_unregister_transport(&beiscsi_iscsi_transport); + return ret; +} + +static void __exit beiscsi_module_exit(void) +{ + pci_unregister_driver(&beiscsi_pci_driver); + iscsi_unregister_transport(&beiscsi_iscsi_transport); +} + +module_init(beiscsi_module_init); +module_exit(beiscsi_module_exit); diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h new file mode 100644 index 000000000000..2520c39c594d --- /dev/null +++ b/drivers/scsi/be2iscsi/be_main.h @@ -0,0 +1,833 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ + +#ifndef _BEISCSI_MAIN_ +#define _BEISCSI_MAIN_ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "be.h" + + + +#define DRV_NAME "be2iscsi" +#define BUILD_STR "2.0.527.0" + +#define BE_NAME "ServerEngines BladeEngine2" \ + "Linux iSCSI Driver version" BUILD_STR +#define DRV_DESC BE_NAME " " "Driver" + +#define BE_VENDOR_ID 0x19A2 +#define BE_DEVICE_ID1 0x212 +#define OC_DEVICE_ID1 0x702 +#define OC_DEVICE_ID2 0x703 + +#define BE2_MAX_SESSIONS 64 +#define BE2_CMDS_PER_CXN 128 +#define BE2_LOGOUTS BE2_MAX_SESSIONS +#define BE2_TMFS 16 +#define BE2_NOPOUT_REQ 16 +#define BE2_ASYNCPDUS BE2_MAX_SESSIONS +#define BE2_MAX_ICDS 2048 +#define BE2_SGE 32 +#define BE2_DEFPDU_HDR_SZ 64 +#define BE2_DEFPDU_DATA_SZ 8192 +#define BE2_IO_DEPTH \ + (BE2_MAX_ICDS / 2 - (BE2_LOGOUTS + BE2_TMFS + BE2_NOPOUT_REQ)) + +#define BEISCSI_SGLIST_ELEMENTS BE2_SGE + +#define BEISCSI_MAX_CMNDS 1024 /* Max IO's per Ctrlr sht->can_queue */ +#define BEISCSI_CMD_PER_LUN 128 /* scsi_host->cmd_per_lun */ +#define BEISCSI_MAX_SECTORS 2048 /* scsi_host->max_sectors */ + +#define BEISCSI_MAX_CMD_LEN 16 /* scsi_host->max_cmd_len */ +#define BEISCSI_NUM_MAX_LUN 256 /* scsi_host->max_lun */ +#define BEISCSI_NUM_DEVICES_SUPPORTED 0x01 +#define BEISCSI_MAX_FRAGS_INIT 192 +#define BE_NUM_MSIX_ENTRIES 1 +#define MPU_EP_SEMAPHORE 0xac + +#define BE_SENSE_INFO_SIZE 258 +#define BE_ISCSI_PDU_HEADER_SIZE 64 +#define BE_MIN_MEM_SIZE 16384 + +#define IIOC_SCSI_DATA 0x05 /* Write Operation */ + +#define DBG_LVL 0x00000001 +#define DBG_LVL_1 0x00000001 +#define DBG_LVL_2 0x00000002 +#define DBG_LVL_3 0x00000004 +#define DBG_LVL_4 0x00000008 +#define DBG_LVL_5 0x00000010 +#define DBG_LVL_6 0x00000020 +#define DBG_LVL_7 0x00000040 +#define DBG_LVL_8 0x00000080 + +#define SE_DEBUG(debug_mask, fmt, args...) \ +do { \ + if (debug_mask & DBG_LVL) { \ + printk(KERN_ERR "(%s():%d):", __func__, __LINE__);\ + printk(fmt, ##args); \ + } \ +} while (0); + +/** + * hardware needs the async PDU buffers to be posted in multiples of 8 + * So have atleast 8 of them by default + */ + +#define HWI_GET_ASYNC_PDU_CTX(phwi) (phwi->phwi_ctxt->pasync_ctx) + +/********* Memory BAR register ************/ +#define PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET 0xfc +/** + * Host Interrupt Enable, if set interrupts are enabled although "PCI Interrupt + * Disable" may still globally block interrupts in addition to individual + * interrupt masks; a mechanism for the device driver to block all interrupts + * atomically without having to arbitrate for the PCI Interrupt Disable bit + * with the OS. + */ +#define MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK (1 << 29) /* bit 29 */ + +/********* ISR0 Register offset **********/ +#define CEV_ISR0_OFFSET 0xC18 +#define CEV_ISR_SIZE 4 + +/** + * Macros for reading/writing a protection domain or CSR registers + * in BladeEngine. + */ + +#define DB_TXULP0_OFFSET 0x40 +#define DB_RXULP0_OFFSET 0xA0 +/********* Event Q door bell *************/ +#define DB_EQ_OFFSET DB_CQ_OFFSET +#define DB_EQ_RING_ID_MASK 0x1FF /* bits 0 - 8 */ +/* Clear the interrupt for this eq */ +#define DB_EQ_CLR_SHIFT (9) /* bit 9 */ +/* Must be 1 */ +#define DB_EQ_EVNT_SHIFT (10) /* bit 10 */ +/* Number of event entries processed */ +#define DB_EQ_NUM_POPPED_SHIFT (16) /* bits 16 - 28 */ +/* Rearm bit */ +#define DB_EQ_REARM_SHIFT (29) /* bit 29 */ + +/********* Compl Q door bell *************/ +#define DB_CQ_OFFSET 0x120 +#define DB_CQ_RING_ID_MASK 0x3FF /* bits 0 - 9 */ +/* Number of event entries processed */ +#define DB_CQ_NUM_POPPED_SHIFT (16) /* bits 16 - 28 */ +/* Rearm bit */ +#define DB_CQ_REARM_SHIFT (29) /* bit 29 */ + +#define GET_HWI_CONTROLLER_WS(pc) (pc->phwi_ctrlr) +#define HWI_GET_DEF_BUFQ_ID(pc) (((struct hwi_controller *)\ + (GET_HWI_CONTROLLER_WS(pc)))->default_pdu_data.id) +#define HWI_GET_DEF_HDRQ_ID(pc) (((struct hwi_controller *)\ + (GET_HWI_CONTROLLER_WS(pc)))->default_pdu_hdr.id) + +#define PAGES_REQUIRED(x) \ + ((x < PAGE_SIZE) ? 1 : ((x + PAGE_SIZE - 1) / PAGE_SIZE)) + +enum be_mem_enum { + HWI_MEM_ADDN_CONTEXT, + HWI_MEM_CQ, + HWI_MEM_EQ, + HWI_MEM_WRB, + HWI_MEM_WRBH, + HWI_MEM_SGLH, /* 5 */ + HWI_MEM_SGE, + HWI_MEM_ASYNC_HEADER_BUF, + HWI_MEM_ASYNC_DATA_BUF, + HWI_MEM_ASYNC_HEADER_RING, + HWI_MEM_ASYNC_DATA_RING, /* 10 */ + HWI_MEM_ASYNC_HEADER_HANDLE, + HWI_MEM_ASYNC_DATA_HANDLE, + HWI_MEM_ASYNC_PDU_CONTEXT, + ISCSI_MEM_GLOBAL_HEADER, + SE_MEM_MAX /* 15 */ +}; + +struct be_bus_address32 { + unsigned int address_lo; + unsigned int address_hi; +}; + +struct be_bus_address64 { + unsigned long long address; +}; + +struct be_bus_address { + union { + struct be_bus_address32 a32; + struct be_bus_address64 a64; + } u; +}; + +struct mem_array { + struct be_bus_address bus_address; /* Bus address of location */ + void *virtual_address; /* virtual address to the location */ + unsigned int size; /* Size required by memory block */ +}; + +struct be_mem_descriptor { + unsigned int index; /* Index of this memory parameter */ + unsigned int category; /* type indicates cached/non-cached */ + unsigned int num_elements; /* number of elements in this + * descriptor + */ + unsigned int alignment_mask; /* Alignment mask for this block */ + unsigned int size_in_bytes; /* Size required by memory block */ + struct mem_array *mem_array; +}; + +struct sgl_handle { + unsigned int sgl_index; + struct iscsi_sge *pfrag; +}; + +struct hba_parameters { + unsigned int ios_per_ctrl; + unsigned int cxns_per_ctrl; + unsigned int asyncpdus_per_ctrl; + unsigned int icds_per_ctrl; + unsigned int num_sge_per_io; + unsigned int defpdu_hdr_sz; + unsigned int defpdu_data_sz; + unsigned int num_cq_entries; + unsigned int num_eq_entries; + unsigned int wrbs_per_cxn; + unsigned int crashmode; + unsigned int hba_num; + + unsigned int mgmt_ws_sz; + unsigned int hwi_ws_sz; + + unsigned int eto; + unsigned int ldto; + + unsigned int dbg_flags; + unsigned int num_cxn; + + unsigned int eq_timer; + /** + * These are calculated from other params. They're here + * for debug purposes + */ + unsigned int num_mcc_pages; + unsigned int num_mcc_cq_pages; + unsigned int num_cq_pages; + unsigned int num_eq_pages; + + unsigned int num_async_pdu_buf_pages; + unsigned int num_async_pdu_buf_sgl_pages; + unsigned int num_async_pdu_buf_cq_pages; + + unsigned int num_async_pdu_hdr_pages; + unsigned int num_async_pdu_hdr_sgl_pages; + unsigned int num_async_pdu_hdr_cq_pages; + + unsigned int num_sge; +}; + +struct beiscsi_hba { + struct hba_parameters params; + struct hwi_controller *phwi_ctrlr; + unsigned int mem_req[SE_MEM_MAX]; + /* PCI BAR mapped addresses */ + u8 __iomem *csr_va; /* CSR */ + u8 __iomem *db_va; /* Door Bell */ + u8 __iomem *pci_va; /* PCI Config */ + struct be_bus_address csr_pa; /* CSR */ + struct be_bus_address db_pa; /* CSR */ + struct be_bus_address pci_pa; /* CSR */ + /* PCI representation of our HBA */ + struct pci_dev *pcidev; + unsigned int state; + unsigned short asic_revision; + struct blk_iopoll iopoll; + struct be_mem_descriptor *init_mem; + + unsigned short io_sgl_alloc_index; + unsigned short io_sgl_free_index; + unsigned short io_sgl_hndl_avbl; + struct sgl_handle **io_sgl_hndl_base; + + unsigned short eh_sgl_alloc_index; + unsigned short eh_sgl_free_index; + unsigned short eh_sgl_hndl_avbl; + struct sgl_handle **eh_sgl_hndl_base; + spinlock_t io_sgl_lock; + spinlock_t mgmt_sgl_lock; + spinlock_t isr_lock; + unsigned int age; + unsigned short avlbl_cids; + unsigned short cid_alloc; + unsigned short cid_free; + struct beiscsi_conn *conn_table[BE2_MAX_SESSIONS * 2]; + struct list_head hba_queue; + unsigned short *cid_array; + struct iscsi_endpoint **ep_array; + struct Scsi_Host *shost; + struct { + /** + * group together since they are used most frequently + * for cid to cri conversion + */ + unsigned int iscsi_cid_start; + unsigned int phys_port; + + unsigned int isr_offset; + unsigned int iscsi_icd_start; + unsigned int iscsi_cid_count; + unsigned int iscsi_icd_count; + unsigned int pci_function; + + unsigned short cid_alloc; + unsigned short cid_free; + unsigned short avlbl_cids; + spinlock_t cid_lock; + } fw_config; + + u8 mac_address[ETH_ALEN]; + unsigned short todo_cq; + unsigned short todo_mcc_cq; + char wq_name[20]; + struct workqueue_struct *wq; /* The actuak work queue */ + struct work_struct work_cqs; /* The work being queued */ + struct be_ctrl_info ctrl; +}; + +/** + * struct beiscsi_conn - iscsi connection structure + */ +struct beiscsi_conn { + struct iscsi_conn *conn; + struct beiscsi_hba *phba; + u32 exp_statsn; + u32 beiscsi_conn_cid; + struct beiscsi_endpoint *ep; + unsigned short login_in_progress; + struct sgl_handle *plogin_sgl_handle; +}; + +/* This structure is used by the chip */ +struct pdu_data_out { + u32 dw[12]; +}; +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_pdu_data_out { + u8 opcode[6]; /* opcode */ + u8 rsvd0[2]; /* should be 0 */ + u8 rsvd1[7]; + u8 final_bit; /* F bit */ + u8 rsvd2[16]; + u8 ahs_length[8]; /* no AHS */ + u8 data_len_hi[8]; + u8 data_len_lo[16]; /* DataSegmentLength */ + u8 lun[64]; + u8 itt[32]; /* ITT; initiator task tag */ + u8 ttt[32]; /* TTT; valid for R2T or 0xffffffff */ + u8 rsvd3[32]; + u8 exp_stat_sn[32]; + u8 rsvd4[32]; + u8 data_sn[32]; + u8 buffer_offset[32]; + u8 rsvd5[32]; +}; + +struct be_cmd_bhs { + struct iscsi_cmd iscsi_hdr; + unsigned char pad1[16]; + struct pdu_data_out iscsi_data_pdu; + unsigned char pad2[BE_SENSE_INFO_SIZE - + sizeof(struct pdu_data_out)]; +}; + +struct beiscsi_io_task { + struct wrb_handle *pwrb_handle; + struct sgl_handle *psgl_handle; + struct beiscsi_conn *conn; + struct scsi_cmnd *scsi_cmnd; + unsigned int cmd_sn; + unsigned int flags; + unsigned short cid; + unsigned short header_len; + + unsigned int alloc_size; + struct be_cmd_bhs *cmd_bhs; + struct be_bus_address bhs_pa; + unsigned short bhs_len; +}; + +struct be_nonio_bhs { + struct iscsi_hdr iscsi_hdr; + unsigned char pad1[16]; + struct pdu_data_out iscsi_data_pdu; + unsigned char pad2[BE_SENSE_INFO_SIZE - + sizeof(struct pdu_data_out)]; +}; + +struct be_status_bhs { + struct iscsi_cmd iscsi_hdr; + unsigned char pad1[16]; + /** + * The plus 2 below is to hold the sense info length that gets + * DMA'ed by RxULP + */ + unsigned char sense_info[BE_SENSE_INFO_SIZE]; +}; + +struct iscsi_sge { + u32 dw[4]; +}; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_iscsi_sge { + u8 addr_hi[32]; + u8 addr_lo[32]; + u8 sge_offset[22]; /* DWORD 2 */ + u8 rsvd0[9]; /* DWORD 2 */ + u8 last_sge; /* DWORD 2 */ + u8 len[17]; /* DWORD 3 */ + u8 rsvd1[15]; /* DWORD 3 */ +}; + +struct beiscsi_offload_params { + u32 dw[5]; +}; + +#define OFFLD_PARAMS_ERL 0x00000003 +#define OFFLD_PARAMS_DDE 0x00000004 +#define OFFLD_PARAMS_HDE 0x00000008 +#define OFFLD_PARAMS_IR2T 0x00000010 +#define OFFLD_PARAMS_IMD 0x00000020 + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_beiscsi_offload_params { + u8 max_burst_length[32]; + u8 max_send_data_segment_length[32]; + u8 first_burst_length[32]; + u8 erl[2]; + u8 dde[1]; + u8 hde[1]; + u8 ir2t[1]; + u8 imd[1]; + u8 pad[26]; + u8 exp_statsn[32]; +}; + +/* void hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn, + struct beiscsi_hba *phba, struct sol_cqe *psol);*/ + +struct async_pdu_handle { + struct list_head link; + struct be_bus_address pa; + void *pbuffer; + unsigned int consumed; + unsigned char index; + unsigned char is_header; + unsigned short cri; + unsigned long buffer_len; +}; + +struct hwi_async_entry { + struct { + unsigned char hdr_received; + unsigned char hdr_len; + unsigned short bytes_received; + unsigned int bytes_needed; + struct list_head list; + } wait_queue; + + struct list_head header_busy_list; + struct list_head data_busy_list; +}; + +#define BE_MIN_ASYNC_ENTRIES 128 + +struct hwi_async_pdu_context { + struct { + struct be_bus_address pa_base; + void *va_base; + void *ring_base; + struct async_pdu_handle *handle_base; + + unsigned int host_write_ptr; + unsigned int ep_read_ptr; + unsigned int writables; + + unsigned int free_entries; + unsigned int busy_entries; + unsigned int buffer_size; + unsigned int num_entries; + + struct list_head free_list; + } async_header; + + struct { + struct be_bus_address pa_base; + void *va_base; + void *ring_base; + struct async_pdu_handle *handle_base; + + unsigned int host_write_ptr; + unsigned int ep_read_ptr; + unsigned int writables; + + unsigned int free_entries; + unsigned int busy_entries; + unsigned int buffer_size; + struct list_head free_list; + unsigned int num_entries; + } async_data; + + /** + * This is a varying size list! Do not add anything + * after this entry!! + */ + struct hwi_async_entry async_entry[BE_MIN_ASYNC_ENTRIES]; +}; + +#define PDUCQE_CODE_MASK 0x0000003F +#define PDUCQE_DPL_MASK 0xFFFF0000 +#define PDUCQE_INDEX_MASK 0x0000FFFF + +struct i_t_dpdu_cqe { + u32 dw[4]; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_i_t_dpdu_cqe { + u8 db_addr_hi[32]; + u8 db_addr_lo[32]; + u8 code[6]; + u8 cid[10]; + u8 dpl[16]; + u8 index[16]; + u8 num_cons[10]; + u8 rsvd0[4]; + u8 final; + u8 valid; +} __packed; + +#define CQE_VALID_MASK 0x80000000 +#define CQE_CODE_MASK 0x0000003F +#define CQE_CID_MASK 0x0000FFC0 + +#define EQE_VALID_MASK 0x00000001 +#define EQE_MAJORCODE_MASK 0x0000000E +#define EQE_RESID_MASK 0xFFFF0000 + +struct be_eq_entry { + u32 dw[1]; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_eq_entry { + u8 valid; /* DWORD 0 */ + u8 major_code[3]; /* DWORD 0 */ + u8 minor_code[12]; /* DWORD 0 */ + u8 resource_id[16]; /* DWORD 0 */ + +} __packed; + +struct cq_db { + u32 dw[1]; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_cq_db { + u8 qid[10]; + u8 event[1]; + u8 rsvd0[5]; + u8 num_popped[13]; + u8 rearm[1]; + u8 rsvd1[2]; +} __packed; + +void beiscsi_process_eq(struct beiscsi_hba *phba); + + +struct iscsi_wrb { + u32 dw[16]; +} __packed; + +#define WRB_TYPE_MASK 0xF0000000 + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_iscsi_wrb { + u8 lun[14]; /* DWORD 0 */ + u8 lt; /* DWORD 0 */ + u8 invld; /* DWORD 0 */ + u8 wrb_idx[8]; /* DWORD 0 */ + u8 dsp; /* DWORD 0 */ + u8 dmsg; /* DWORD 0 */ + u8 undr_run; /* DWORD 0 */ + u8 over_run; /* DWORD 0 */ + u8 type[4]; /* DWORD 0 */ + u8 ptr2nextwrb[8]; /* DWORD 1 */ + u8 r2t_exp_dtl[24]; /* DWORD 1 */ + u8 sgl_icd_idx[12]; /* DWORD 2 */ + u8 rsvd0[20]; /* DWORD 2 */ + u8 exp_data_sn[32]; /* DWORD 3 */ + u8 iscsi_bhs_addr_hi[32]; /* DWORD 4 */ + u8 iscsi_bhs_addr_lo[32]; /* DWORD 5 */ + u8 cmdsn_itt[32]; /* DWORD 6 */ + u8 dif_ref_tag[32]; /* DWORD 7 */ + u8 sge0_addr_hi[32]; /* DWORD 8 */ + u8 sge0_addr_lo[32]; /* DWORD 9 */ + u8 sge0_offset[22]; /* DWORD 10 */ + u8 pbs; /* DWORD 10 */ + u8 dif_mode[2]; /* DWORD 10 */ + u8 rsvd1[6]; /* DWORD 10 */ + u8 sge0_last; /* DWORD 10 */ + u8 sge0_len[17]; /* DWORD 11 */ + u8 dif_meta_tag[14]; /* DWORD 11 */ + u8 sge0_in_ddr; /* DWORD 11 */ + u8 sge1_addr_hi[32]; /* DWORD 12 */ + u8 sge1_addr_lo[32]; /* DWORD 13 */ + u8 sge1_r2t_offset[22]; /* DWORD 14 */ + u8 rsvd2[9]; /* DWORD 14 */ + u8 sge1_last; /* DWORD 14 */ + u8 sge1_len[17]; /* DWORD 15 */ + u8 ref_sgl_icd_idx[12]; /* DWORD 15 */ + u8 rsvd3[2]; /* DWORD 15 */ + u8 sge1_in_ddr; /* DWORD 15 */ + +} __packed; + +struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid, + int index); +void +free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle); + +struct pdu_nop_out { + u32 dw[12]; +}; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_pdu_nop_out { + u8 opcode[6]; /* opcode 0x00 */ + u8 i_bit; /* I Bit */ + u8 x_bit; /* reserved; should be 0 */ + u8 fp_bit_filler1[7]; + u8 f_bit; /* always 1 */ + u8 reserved1[16]; + u8 ahs_length[8]; /* no AHS */ + u8 data_len_hi[8]; + u8 data_len_lo[16]; /* DataSegmentLength */ + u8 lun[64]; + u8 itt[32]; /* initiator id for ping or 0xffffffff */ + u8 ttt[32]; /* target id for ping or 0xffffffff */ + u8 cmd_sn[32]; + u8 exp_stat_sn[32]; + u8 reserved5[128]; +}; + +#define PDUBASE_OPCODE_MASK 0x0000003F +#define PDUBASE_DATALENHI_MASK 0x0000FF00 +#define PDUBASE_DATALENLO_MASK 0xFFFF0000 + +struct pdu_base { + u32 dw[16]; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_pdu_base { + u8 opcode[6]; + u8 i_bit; /* immediate bit */ + u8 x_bit; /* reserved, always 0 */ + u8 reserved1[24]; /* opcode-specific fields */ + u8 ahs_length[8]; /* length units is 4 byte words */ + u8 data_len_hi[8]; + u8 data_len_lo[16]; /* DatasegmentLength */ + u8 lun[64]; /* lun or opcode-specific fields */ + u8 itt[32]; /* initiator task tag */ + u8 reserved4[224]; +}; + +struct iscsi_target_context_update_wrb { + u32 dw[16]; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_iscsi_target_context_update_wrb { + u8 lun[14]; /* DWORD 0 */ + u8 lt; /* DWORD 0 */ + u8 invld; /* DWORD 0 */ + u8 wrb_idx[8]; /* DWORD 0 */ + u8 dsp; /* DWORD 0 */ + u8 dmsg; /* DWORD 0 */ + u8 undr_run; /* DWORD 0 */ + u8 over_run; /* DWORD 0 */ + u8 type[4]; /* DWORD 0 */ + u8 ptr2nextwrb[8]; /* DWORD 1 */ + u8 max_burst_length[19]; /* DWORD 1 */ + u8 rsvd0[5]; /* DWORD 1 */ + u8 rsvd1[15]; /* DWORD 2 */ + u8 max_send_data_segment_length[17]; /* DWORD 2 */ + u8 first_burst_length[14]; /* DWORD 3 */ + u8 rsvd2[2]; /* DWORD 3 */ + u8 tx_wrbindex_drv_msg[8]; /* DWORD 3 */ + u8 rsvd3[5]; /* DWORD 3 */ + u8 session_state[3]; /* DWORD 3 */ + u8 rsvd4[16]; /* DWORD 4 */ + u8 tx_jumbo; /* DWORD 4 */ + u8 hde; /* DWORD 4 */ + u8 dde; /* DWORD 4 */ + u8 erl[2]; /* DWORD 4 */ + u8 domain_id[5]; /* DWORD 4 */ + u8 mode; /* DWORD 4 */ + u8 imd; /* DWORD 4 */ + u8 ir2t; /* DWORD 4 */ + u8 notpredblq[2]; /* DWORD 4 */ + u8 compltonack; /* DWORD 4 */ + u8 stat_sn[32]; /* DWORD 5 */ + u8 pad_buffer_addr_hi[32]; /* DWORD 6 */ + u8 pad_buffer_addr_lo[32]; /* DWORD 7 */ + u8 pad_addr_hi[32]; /* DWORD 8 */ + u8 pad_addr_lo[32]; /* DWORD 9 */ + u8 rsvd5[32]; /* DWORD 10 */ + u8 rsvd6[32]; /* DWORD 11 */ + u8 rsvd7[32]; /* DWORD 12 */ + u8 rsvd8[32]; /* DWORD 13 */ + u8 rsvd9[32]; /* DWORD 14 */ + u8 rsvd10[32]; /* DWORD 15 */ + +} __packed; + +struct be_ring { + u32 pages; /* queue size in pages */ + u32 id; /* queue id assigned by beklib */ + u32 num; /* number of elements in queue */ + u32 cidx; /* consumer index */ + u32 pidx; /* producer index -- not used by most rings */ + u32 item_size; /* size in bytes of one object */ + + void *va; /* The virtual address of the ring. This + * should be last to allow 32 & 64 bit debugger + * extensions to work. + */ +}; + +struct hwi_wrb_context { + struct list_head wrb_handle_list; + struct list_head wrb_handle_drvr_list; + struct wrb_handle **pwrb_handle_base; + struct wrb_handle **pwrb_handle_basestd; + struct iscsi_wrb *plast_wrb; + unsigned short alloc_index; + unsigned short free_index; + unsigned short wrb_handles_available; + unsigned short cid; +}; + +struct hwi_controller { + struct list_head io_sgl_list; + struct list_head eh_sgl_list; + struct sgl_handle *psgl_handle_base; + unsigned int wrb_mem_index; + + struct hwi_wrb_context wrb_context[BE2_MAX_SESSIONS * 2]; + struct mcc_wrb *pmcc_wrb_base; + struct be_ring default_pdu_hdr; + struct be_ring default_pdu_data; + struct hwi_context_memory *phwi_ctxt; + unsigned short cq_errors[CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN]; +}; + +enum hwh_type_enum { + HWH_TYPE_IO = 1, + HWH_TYPE_LOGOUT = 2, + HWH_TYPE_TMF = 3, + HWH_TYPE_NOP = 4, + HWH_TYPE_IO_RD = 5, + HWH_TYPE_LOGIN = 11, + HWH_TYPE_INVALID = 0xFFFFFFFF +}; + +struct wrb_handle { + enum hwh_type_enum type; + unsigned short wrb_index; + unsigned short nxt_wrb_index; + + struct iscsi_task *pio_handle; + struct iscsi_wrb *pwrb; +}; + +struct hwi_context_memory { + struct be_eq_obj be_eq; + struct be_queue_info be_cq; + struct be_queue_info be_mcc_cq; + struct be_queue_info be_mcc; + + struct be_queue_info be_def_hdrq; + struct be_queue_info be_def_dataq; + + struct be_queue_info be_wrbq[BE2_MAX_SESSIONS]; + struct be_mcc_wrb_context *pbe_mcc_context; + + struct hwi_async_pdu_context *pasync_ctx; +}; + +#endif diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c new file mode 100644 index 000000000000..12e644fc746e --- /dev/null +++ b/drivers/scsi/be2iscsi/be_mgmt.c @@ -0,0 +1,321 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ + +#include "be_mgmt.h" +#include "be_iscsi.h" + +unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_fw_cfg *req = embedded_payload(wrb); + int status = 0; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req)); + + status = be_mbox_notify(ctrl); + if (!status) { + struct be_fw_cfg *pfw_cfg; + pfw_cfg = req; + phba->fw_config.phys_port = pfw_cfg->phys_port; + phba->fw_config.iscsi_icd_start = + pfw_cfg->ulp[0].icd_base; + phba->fw_config.iscsi_icd_count = + pfw_cfg->ulp[0].icd_count; + phba->fw_config.iscsi_cid_start = + pfw_cfg->ulp[0].sq_base; + phba->fw_config.iscsi_cid_count = + pfw_cfg->ulp[0].sq_count; + } else { + shost_printk(KERN_WARNING, phba->shost, + "Failed in mgmt_get_fw_config \n"); + } + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl) +{ + struct be_dma_mem nonemb_cmd; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_mgmt_controller_attributes *req; + struct be_sge *sge = nonembedded_sgl(wrb); + int status = 0; + + nonemb_cmd.va = pci_alloc_consistent(ctrl->pdev, + sizeof(struct be_mgmt_controller_attributes), + &nonemb_cmd.dma); + if (nonemb_cmd.va == NULL) { + SE_DEBUG(DBG_LVL_1, + "Failed to allocate memory for mgmt_check_supported_fw" + "\n"); + return -1; + } + nonemb_cmd.size = sizeof(struct be_mgmt_controller_attributes); + req = nonemb_cmd.va; + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, + OPCODE_COMMON_GET_CNTL_ATTRIBUTES, sizeof(*req)); + sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma)); + sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF); + sge->len = cpu_to_le32(nonemb_cmd.size); + + status = be_mbox_notify(ctrl); + if (!status) { + struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va; + SE_DEBUG(DBG_LVL_8, "Firmware version of CMD: %s\n", + resp->params.hba_attribs.flashrom_version_string); + SE_DEBUG(DBG_LVL_8, "Firmware version is : %s\n", + resp->params.hba_attribs.firmware_version_string); + SE_DEBUG(DBG_LVL_8, + "Developer Build, not performing version check...\n"); + + } else + SE_DEBUG(DBG_LVL_1, " Failed in mgmt_check_supported_fw\n"); + if (nonemb_cmd.va) + pci_free_consistent(ctrl->pdev, nonemb_cmd.size, + nonemb_cmd.va, nonemb_cmd.dma); + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct iscsi_cleanup_req *req = embedded_payload(wrb); + int status = 0; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_CLEANUP, sizeof(*req)); + + req->chute = chute; + req->hdr_ring_id = 0; + req->data_ring_id = 0; + + status = be_mbox_notify(ctrl); + if (status) + shost_printk(KERN_WARNING, phba->shost, + " mgmt_epfw_cleanup , FAILED\n"); + spin_unlock(&ctrl->mbox_lock); + return status; +} + +unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba, + unsigned int icd, unsigned int cid) +{ + struct be_dma_mem nonemb_cmd; + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_sge *sge = nonembedded_sgl(wrb); + struct invalidate_commands_params_in *req; + int status = 0; + + nonemb_cmd.va = pci_alloc_consistent(ctrl->pdev, + sizeof(struct invalidate_commands_params_in), + &nonemb_cmd.dma); + if (nonemb_cmd.va == NULL) { + SE_DEBUG(DBG_LVL_1, + "Failed to allocate memory for" + "mgmt_invalidate_icds \n"); + return -1; + } + nonemb_cmd.size = sizeof(struct invalidate_commands_params_in); + req = nonemb_cmd.va; + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS, + sizeof(*req)); + req->ref_handle = 0; + req->cleanup_type = CMD_ISCSI_COMMAND_INVALIDATE; + req->icd_count = 0; + req->table[req->icd_count].icd = icd; + req->table[req->icd_count].cid = cid; + sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma)); + sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF); + sge->len = cpu_to_le32(nonemb_cmd.size); + + status = be_mbox_notify(ctrl); + if (status) + SE_DEBUG(DBG_LVL_1, "ICDS Invalidation Failed\n"); + spin_unlock(&ctrl->mbox_lock); + if (nonemb_cmd.va) + pci_free_consistent(ctrl->pdev, nonemb_cmd.size, + nonemb_cmd.va, nonemb_cmd.dma); + return status; +} + +unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba, + struct beiscsi_endpoint *beiscsi_ep, + unsigned short cid, + unsigned short issue_reset, + unsigned short savecfg_flag) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct iscsi_invalidate_connection_params_in *req = + embedded_payload(wrb); + int status = 0; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, + OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION, + sizeof(*req)); + req->session_handle = beiscsi_ep->fw_handle; + req->cid = cid; + if (issue_reset) + req->cleanup_type = CMD_ISCSI_CONNECTION_ISSUE_TCP_RST; + else + req->cleanup_type = CMD_ISCSI_CONNECTION_INVALIDATE; + req->save_cfg = savecfg_flag; + status = be_mbox_notify(ctrl); + if (status) + SE_DEBUG(DBG_LVL_1, "Invalidation Failed\n"); + + spin_unlock(&ctrl->mbox_lock); + return status; +} + +unsigned char mgmt_upload_connection(struct beiscsi_hba *phba, + unsigned short cid, unsigned int upload_flag) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct tcp_upload_params_in *req = embedded_payload(wrb); + int status = 0; + + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_COMMON_TCP_UPLOAD, + OPCODE_COMMON_TCP_UPLOAD, sizeof(*req)); + req->id = (unsigned short)cid; + req->upload_type = (unsigned char)upload_flag; + status = be_mbox_notify(ctrl); + if (status) + SE_DEBUG(DBG_LVL_1, "mgmt_upload_connection Failed\n"); + spin_unlock(&ctrl->mbox_lock); + return status; +} + +int mgmt_open_connection(struct beiscsi_hba *phba, + struct sockaddr *dst_addr, + struct beiscsi_endpoint *beiscsi_ep) +{ + struct hwi_controller *phwi_ctrlr; + struct hwi_context_memory *phwi_context; + struct sockaddr_in *daddr_in = (struct sockaddr_in *)dst_addr; + struct sockaddr_in6 *daddr_in6 = (struct sockaddr_in6 *)dst_addr; + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct tcp_connect_and_offload_in *req = embedded_payload(wrb); + unsigned short def_hdr_id; + unsigned short def_data_id; + struct phys_addr template_address = { 0, 0 }; + struct phys_addr *ptemplate_address; + int status = 0; + unsigned short cid = beiscsi_ep->ep_cid; + + phwi_ctrlr = phba->phwi_ctrlr; + phwi_context = phwi_ctrlr->phwi_ctxt; + def_hdr_id = (unsigned short)HWI_GET_DEF_HDRQ_ID(phba); + def_data_id = (unsigned short)HWI_GET_DEF_BUFQ_ID(phba); + + ptemplate_address = &template_address; + ISCSI_GET_PDU_TEMPLATE_ADDRESS(phba, ptemplate_address); + spin_lock(&ctrl->mbox_lock); + memset(wrb, 0, sizeof(*wrb)); + + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI, + OPCODE_COMMON_ISCSI_TCP_CONNECT_AND_OFFLOAD, + sizeof(*req)); + if (dst_addr->sa_family == PF_INET) { + __be32 s_addr = daddr_in->sin_addr.s_addr; + req->ip_address.ip_type = BE2_IPV4; + req->ip_address.ip_address[0] = s_addr & 0x000000ff; + req->ip_address.ip_address[1] = (s_addr & 0x0000ff00) >> 8; + req->ip_address.ip_address[2] = (s_addr & 0x00ff0000) >> 16; + req->ip_address.ip_address[3] = (s_addr & 0xff000000) >> 24; + req->tcp_port = ntohs(daddr_in->sin_port); + beiscsi_ep->dst_addr = daddr_in->sin_addr.s_addr; + beiscsi_ep->dst_tcpport = ntohs(daddr_in->sin_port); + beiscsi_ep->ip_type = BE2_IPV4; + } else if (dst_addr->sa_family == PF_INET6) { + req->ip_address.ip_type = BE2_IPV6; + memcpy(&req->ip_address.ip_address, + &daddr_in6->sin6_addr.in6_u.u6_addr8, 16); + req->tcp_port = ntohs(daddr_in6->sin6_port); + beiscsi_ep->dst_tcpport = ntohs(daddr_in6->sin6_port); + memcpy(&beiscsi_ep->dst6_addr, + &daddr_in6->sin6_addr.in6_u.u6_addr8, 16); + beiscsi_ep->ip_type = BE2_IPV6; + } else{ + shost_printk(KERN_ERR, phba->shost, "unknown addr family %d\n", + dst_addr->sa_family); + spin_unlock(&ctrl->mbox_lock); + return -EINVAL; + + } + req->cid = cid; + req->cq_id = phwi_context->be_cq.id; + req->defq_id = def_hdr_id; + req->hdr_ring_id = def_hdr_id; + req->data_ring_id = def_data_id; + req->do_offload = 1; + req->dataout_template_pa.lo = ptemplate_address->lo; + req->dataout_template_pa.hi = ptemplate_address->hi; + status = be_mbox_notify(ctrl); + if (!status) { + struct iscsi_endpoint *ep; + struct tcp_connect_and_offload_out *ptcpcnct_out = + embedded_payload(wrb); + + ep = phba->ep_array[ptcpcnct_out->cid]; + beiscsi_ep = ep->dd_data; + beiscsi_ep->fw_handle = 0; + beiscsi_ep->cid_vld = 1; + SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n"); + } else + SE_DEBUG(DBG_LVL_1, "mgmt_open_connection Failed\n"); + spin_unlock(&ctrl->mbox_lock); + return status; +} diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h new file mode 100644 index 000000000000..00e816ee8070 --- /dev/null +++ b/drivers/scsi/be2iscsi/be_mgmt.h @@ -0,0 +1,249 @@ +/** + * Copyright (C) 2005 - 2009 ServerEngines + * All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. The full GNU General + * Public License is included in this distribution in the file called COPYING. + * + * Written by: Jayamohan Kallickal (jayamohank@serverengines.com) + * + * Contact Information: + * linux-drivers@serverengines.com + * + * ServerEngines + * 209 N. Fair Oaks Ave + * Sunnyvale, CA 94085 + * + */ + +#ifndef _BEISCSI_MGMT_ +#define _BEISCSI_MGMT_ + +#include +#include +#include "be_iscsi.h" +#include "be_main.h" + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_mcc_sge { + u8 pa_lo[32]; /* dword 0 */ + u8 pa_hi[32]; /* dword 1 */ + u8 length[32]; /* DWORD 2 */ +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_mcc_wrb_payload { + union { + struct amap_mcc_sge sgl[19]; + u8 embedded[59 * 32]; /* DWORDS 57 to 115 */ + } u; +} __packed; + +/** + * Pseudo amap definition in which each bit of the actual structure is defined + * as a byte: used to calculate offset/shift/mask of each field + */ +struct amap_mcc_wrb { + u8 embedded; /* DWORD 0 */ + u8 rsvd0[2]; /* DWORD 0 */ + u8 sge_count[5]; /* DWORD 0 */ + u8 rsvd1[16]; /* DWORD 0 */ + u8 special[8]; /* DWORD 0 */ + u8 payload_length[32]; + u8 tag[64]; /* DWORD 2 */ + u8 rsvd2[32]; /* DWORD 4 */ + struct amap_mcc_wrb_payload payload; +}; + +struct mcc_sge { + u32 pa_lo; /* dword 0 */ + u32 pa_hi; /* dword 1 */ + u32 length; /* DWORD 2 */ +} __packed; + +struct mcc_wrb_payload { + union { + struct mcc_sge sgl[19]; + u32 embedded[59]; /* DWORDS 57 to 115 */ + } u; +} __packed; + +#define MCC_WRB_EMBEDDED_MASK 0x00000001 + +struct mcc_wrb { + u32 dw[0]; /* DWORD 0 */ + u32 payload_length; + u32 tag[2]; /* DWORD 2 */ + u32 rsvd2[1]; /* DWORD 4 */ + struct mcc_wrb_payload payload; +}; + +unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute); +int mgmt_open_connection(struct beiscsi_hba *phba, struct sockaddr *dst_addr, + struct beiscsi_endpoint *beiscsi_ep); + +unsigned char mgmt_upload_connection(struct beiscsi_hba *phba, + unsigned short cid, + unsigned int upload_flag); +unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba, + unsigned int icd, unsigned int cid); + +struct iscsi_invalidate_connection_params_in { + struct be_cmd_req_hdr hdr; + unsigned int session_handle; + unsigned short cid; + unsigned short unused; + unsigned short cleanup_type; + unsigned short save_cfg; +} __packed; + +struct iscsi_invalidate_connection_params_out { + unsigned int session_handle; + unsigned short cid; + unsigned short unused; +} __packed; + +union iscsi_invalidate_connection_params { + struct iscsi_invalidate_connection_params_in request; + struct iscsi_invalidate_connection_params_out response; +} __packed; + +struct invalidate_command_table { + unsigned short icd; + unsigned short cid; +} __packed; + +struct invalidate_commands_params_in { + struct be_cmd_req_hdr hdr; + unsigned int ref_handle; + unsigned int icd_count; + struct invalidate_command_table table[128]; + unsigned short cleanup_type; + unsigned short unused; +} __packed; + +struct invalidate_commands_params_out { + unsigned int ref_handle; + unsigned int icd_count; + unsigned int icd_status[128]; +} __packed; + +union invalidate_commands_params { + struct invalidate_commands_params_in request; + struct invalidate_commands_params_out response; +} __packed; + +struct mgmt_hba_attributes { + u8 flashrom_version_string[32]; + u8 manufacturer_name[32]; + u32 supported_modes; + u8 seeprom_version_lo; + u8 seeprom_version_hi; + u8 rsvd0[2]; + u32 fw_cmd_data_struct_version; + u32 ep_fw_data_struct_version; + u32 future_reserved[12]; + u32 default_extended_timeout; + u8 controller_model_number[32]; + u8 controller_description[64]; + u8 controller_serial_number[32]; + u8 ip_version_string[32]; + u8 firmware_version_string[32]; + u8 bios_version_string[32]; + u8 redboot_version_string[32]; + u8 driver_version_string[32]; + u8 fw_on_flash_version_string[32]; + u32 functionalities_supported; + u16 max_cdblength; + u8 asic_revision; + u8 generational_guid[16]; + u8 hba_port_count; + u16 default_link_down_timeout; + u8 iscsi_ver_min_max; + u8 multifunction_device; + u8 cache_valid; + u8 hba_status; + u8 max_domains_supported; + u8 phy_port; + u32 firmware_post_status; + u32 hba_mtu[8]; + u32 future_u32[4]; +} __packed; + +struct mgmt_controller_attributes { + struct mgmt_hba_attributes hba_attribs; + u16 pci_vendor_id; + u16 pci_device_id; + u16 pci_sub_vendor_id; + u16 pci_sub_system_id; + u8 pci_bus_number; + u8 pci_device_number; + u8 pci_function_number; + u8 interface_type; + u64 unique_identifier; + u8 netfilters; + u8 rsvd0[3]; + u8 future_u32[4]; +} __packed; + +struct be_mgmt_controller_attributes { + struct be_cmd_req_hdr hdr; + struct mgmt_controller_attributes params; +} __packed; + +struct be_mgmt_controller_attributes_resp { + struct be_cmd_resp_hdr hdr; + struct mgmt_controller_attributes params; +} __packed; + +/* configuration management */ + +#define GET_MGMT_CONTROLLER_WS(phba) (phba->pmgmt_ws) + +/* MGMT CMD flags */ + +#define MGMT_CMDH_FREE (1<<0) + +/* --- MGMT_ERROR_CODES --- */ +/* Error Codes returned in the status field of the CMD response header */ +#define MGMT_STATUS_SUCCESS 0 /* The CMD completed without errors */ +#define MGMT_STATUS_FAILED 1 /* Error status in the Status field of */ + /* the CMD_RESPONSE_HEADER */ + +#define ISCSI_GET_PDU_TEMPLATE_ADDRESS(pc, pa) {\ + pa->lo = phba->init_mem[ISCSI_MEM_GLOBAL_HEADER].mem_array[0].\ + bus_address.u.a32.address_lo; \ + pa->hi = phba->init_mem[ISCSI_MEM_GLOBAL_HEADER].mem_array[0].\ + bus_address.u.a32.address_hi; \ +} + +struct beiscsi_endpoint { + struct beiscsi_hba *phba; + struct beiscsi_sess *sess; + struct beiscsi_conn *conn; + unsigned short ip_type; + char dst6_addr[ISCSI_ADDRESS_BUF_LEN]; + unsigned long dst_addr; + unsigned short ep_cid; + unsigned int fw_handle; + u16 dst_tcpport; + u16 cid_vld; +}; + +unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl, + struct beiscsi_hba *phba); + +unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba, + struct beiscsi_endpoint *beiscsi_ep, + unsigned short cid, + unsigned short issue_reset, + unsigned short savecfg_flag); +#endif -- cgit v1.2.3 From b8b9e1b8128d8854cf55740f9ceba3010143520d Mon Sep 17 00:00:00 2001 From: Jayamohan Kallickal Date: Tue, 22 Sep 2009 08:21:22 +0530 Subject: [SCSI] libiscsi: iscsi_session_setup to allow for private space This patch contains changes that allow iscsi_session_setup to allocate private space for LLD's Signed-off-by: Jayamohan Kallickal Acked-by: Mike Christie Signed-off-by: James Bottomley --- drivers/infiniband/ulp/iser/iscsi_iser.c | 2 +- drivers/scsi/be2iscsi/be_iscsi.c | 6 ++++-- drivers/scsi/be2iscsi/be_main.h | 5 +++++ drivers/scsi/bnx2i/bnx2i_iscsi.c | 2 +- drivers/scsi/cxgb3i/cxgb3i_iscsi.c | 2 +- drivers/scsi/iscsi_tcp.c | 2 +- drivers/scsi/libiscsi.c | 6 ++++-- include/scsi/libiscsi.h | 3 ++- 8 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 0ba6ec876296..add9188663ff 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -426,7 +426,7 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep, * because we preallocate so many resources */ cls_session = iscsi_session_setup(&iscsi_iser_transport, shost, - ISCSI_DEF_XMIT_CMDS_MAX, + ISCSI_DEF_XMIT_CMDS_MAX, 0, sizeof(struct iscsi_iser_task), initial_cmdsn, 0); if (!cls_session) diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index b23526cb39d7..f18e643f3589 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -44,9 +44,10 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, struct Scsi_Host *shost; struct beiscsi_endpoint *beiscsi_ep; struct iscsi_cls_session *cls_session; - struct iscsi_session *sess; struct beiscsi_hba *phba; struct iscsi_task *task; + struct iscsi_session *sess; + struct beiscsi_session *beiscsi_sess; struct beiscsi_io_task *io_task; unsigned int max_size, num_cmd; dma_addr_t bus_add; @@ -73,7 +74,8 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, cls_session = iscsi_session_setup(&beiscsi_iscsi_transport, shost, cmds_max, - sizeof(struct beiscsi_io_task), + sizeof(*beiscsi_sess), + sizeof(*io_task), initial_cmdsn, ISCSI_MAX_TARGET); if (!cls_session) return NULL; diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 2520c39c594d..387e363b0ec7 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -327,6 +327,10 @@ struct beiscsi_hba { struct be_ctrl_info ctrl; }; +struct beiscsi_session { + struct pci_pool *bhs_pool; +}; + /** * struct beiscsi_conn - iscsi connection structure */ @@ -338,6 +342,7 @@ struct beiscsi_conn { struct beiscsi_endpoint *ep; unsigned short login_in_progress; struct sgl_handle *plogin_sgl_handle; + struct beiscsi_session *beiscsi_sess; }; /* This structure is used by the chip */ diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index 9a7ba71f1af4..cafb888c2376 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -1243,7 +1243,7 @@ bnx2i_session_create(struct iscsi_endpoint *ep, cmds_max = BNX2I_SQ_WQES_MIN; cls_session = iscsi_session_setup(&bnx2i_iscsi_transport, shost, - cmds_max, sizeof(struct bnx2i_cmd), + cmds_max, 0, sizeof(struct bnx2i_cmd), initial_cmdsn, ISCSI_MAX_TARGET); if (!cls_session) return NULL; diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c index c399f485aa7d..2631bddd255e 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c +++ b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c @@ -422,7 +422,7 @@ cxgb3i_session_create(struct iscsi_endpoint *ep, u16 cmds_max, u16 qdepth, BUG_ON(hba != iscsi_host_priv(shost)); cls_session = iscsi_session_setup(&cxgb3i_iscsi_transport, shost, - cmds_max, + cmds_max, 0, sizeof(struct iscsi_tcp_task) + sizeof(struct cxgb3i_task_data), initial_cmdsn, ISCSI_MAX_TARGET); diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 2b1b834a098b..edc49ca49cea 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -811,7 +811,7 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, goto free_host; cls_session = iscsi_session_setup(&iscsi_sw_tcp_transport, shost, - cmds_max, + cmds_max, 0, sizeof(struct iscsi_tcp_task) + sizeof(struct iscsi_sw_tcp_hdrbuf), initial_cmdsn, 0); diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 8dc73c489a17..f1a4246f890c 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -2436,7 +2436,7 @@ static void iscsi_host_dec_session_cnt(struct Scsi_Host *shost) */ struct iscsi_cls_session * iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, - uint16_t cmds_max, int cmd_task_size, + uint16_t cmds_max, int dd_size, int cmd_task_size, uint32_t initial_cmdsn, unsigned int id) { struct iscsi_host *ihost = shost_priv(shost); @@ -2486,7 +2486,8 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, scsi_cmds = total_cmds - ISCSI_MGMT_CMDS_MAX; cls_session = iscsi_alloc_session(shost, iscsit, - sizeof(struct iscsi_session)); + sizeof(struct iscsi_session) + + dd_size); if (!cls_session) goto dec_session_count; session = cls_session->dd_data; @@ -2503,6 +2504,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, session->max_cmdsn = initial_cmdsn + 1; session->max_r2t = 1; session->tt = iscsit; + session->dd_data = cls_session->dd_data + sizeof(*session); mutex_init(&session->eh_mutex); spin_lock_init(&session->lock); diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 887e57e3e223..a72edd4eceec 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -303,6 +303,7 @@ struct iscsi_session { int cmds_max; /* size of cmds array */ struct iscsi_task **cmds; /* Original Cmds arr */ struct iscsi_pool cmdpool; /* PDU's pool */ + void *dd_data; /* LLD private data */ }; enum { @@ -363,7 +364,7 @@ extern int iscsi_target_alloc(struct scsi_target *starget); */ extern struct iscsi_cls_session * iscsi_session_setup(struct iscsi_transport *, struct Scsi_Host *shost, - uint16_t, int, uint32_t, unsigned int); + uint16_t, int, int, uint32_t, unsigned int); extern void iscsi_session_teardown(struct iscsi_cls_session *); extern void iscsi_session_recovery_timedout(struct iscsi_cls_session *); extern int iscsi_set_param(struct iscsi_cls_conn *cls_conn, -- cgit v1.2.3 From 2afc95bf546a961d2936d886c3802e159f1bae6b Mon Sep 17 00:00:00 2001 From: Jayamohan Kallickal Date: Tue, 22 Sep 2009 08:22:26 +0530 Subject: [SCSI] be2iscsi: Moving to pci_pools v3 This patch contains changes to use pci_pools for iscsi hdr instead of pci_alloc_consistent. Here we alloc and free to pool for every IO v3: - Remove cleanup loop in beiscsi_session_destroy - Fixup for allocation failure handling in beiscsi_alloc_pdu - Removed unused variable in beiscsi_session_destroy. [jejb: fix up pci_pool_alloc address sizing problem] Signed-off-by: Jayamohan Kallickal Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/be2iscsi/be_iscsi.c | 46 +++++++++++---------------- drivers/scsi/be2iscsi/be_main.c | 69 +++++++++++++++++++--------------------- drivers/scsi/be2iscsi/be_main.h | 1 - 3 files changed, 51 insertions(+), 65 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index f18e643f3589..2fd25442cfaf 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -45,14 +45,9 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, struct beiscsi_endpoint *beiscsi_ep; struct iscsi_cls_session *cls_session; struct beiscsi_hba *phba; - struct iscsi_task *task; struct iscsi_session *sess; struct beiscsi_session *beiscsi_sess; struct beiscsi_io_task *io_task; - unsigned int max_size, num_cmd; - dma_addr_t bus_add; - u64 pa_addr; - void *vaddr; SE_DEBUG(DBG_LVL_8, "In beiscsi_session_create\n"); @@ -80,20 +75,18 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, if (!cls_session) return NULL; sess = cls_session->dd_data; - max_size = ALIGN(sizeof(struct be_cmd_bhs), 64) * sess->cmds_max; - vaddr = pci_alloc_consistent(phba->pcidev, max_size, &bus_add); - pa_addr = (__u64) bus_add; - - for (num_cmd = 0; num_cmd < sess->cmds_max; num_cmd++) { - task = sess->cmds[num_cmd]; - io_task = task->dd_data; - io_task->cmd_bhs = vaddr; - io_task->bhs_pa.u.a64.address = pa_addr; - io_task->alloc_size = max_size; - vaddr += ALIGN(sizeof(struct be_cmd_bhs), 64); - pa_addr += ALIGN(sizeof(struct be_cmd_bhs), 64); - } + beiscsi_sess = sess->dd_data; + beiscsi_sess->bhs_pool = pci_pool_create("beiscsi_bhs_pool", + phba->pcidev, + sizeof(struct be_cmd_bhs), + 64, 0); + if (!beiscsi_sess->bhs_pool) + goto destroy_sess; + return cls_session; +destroy_sess: + iscsi_session_teardown(cls_session); + return NULL; } /** @@ -105,18 +98,10 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep, */ void beiscsi_session_destroy(struct iscsi_cls_session *cls_session) { - struct iscsi_task *task; - struct beiscsi_io_task *io_task; struct iscsi_session *sess = cls_session->dd_data; - struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); - struct beiscsi_hba *phba = iscsi_host_priv(shost); + struct beiscsi_session *beiscsi_sess = sess->dd_data; - task = sess->cmds[0]; - io_task = task->dd_data; - pci_free_consistent(phba->pcidev, - io_task->alloc_size, - io_task->cmd_bhs, - io_task->bhs_pa.u.a64.address); + pci_pool_destroy(beiscsi_sess->bhs_pool); iscsi_session_teardown(cls_session); } @@ -133,6 +118,8 @@ beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid) struct iscsi_cls_conn *cls_conn; struct beiscsi_conn *beiscsi_conn; struct iscsi_conn *conn; + struct iscsi_session *sess; + struct beiscsi_session *beiscsi_sess; SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_create ,cid" "from iscsi layer=%d\n", cid); @@ -148,6 +135,9 @@ beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid) beiscsi_conn->ep = NULL; beiscsi_conn->phba = phba; beiscsi_conn->conn = conn; + sess = cls_session->dd_data; + beiscsi_sess = sess->dd_data; + beiscsi_conn->beiscsi_sess = beiscsi_sess; return cls_conn; } diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index d520fe875859..4f1aca346e38 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -2880,7 +2880,16 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) struct hwi_wrb_context *pwrb_context; struct hwi_controller *phwi_ctrlr; itt_t itt; + struct beiscsi_session *beiscsi_sess = beiscsi_conn->beiscsi_sess; + dma_addr_t paddr; + io_task->cmd_bhs = pci_pool_alloc(beiscsi_sess->bhs_pool, + GFP_KERNEL, &paddr); + + if (!io_task->cmd_bhs) + return -ENOMEM; + + io_task->bhs_pa.u.a64.address = paddr; io_task->pwrb_handle = alloc_wrb_handle(phba, beiscsi_conn->beiscsi_conn_cid, task->itt); @@ -2894,17 +2903,9 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) spin_lock(&phba->io_sgl_lock); io_task->psgl_handle = alloc_io_sgl_handle(phba); spin_unlock(&phba->io_sgl_lock); - if (!io_task->psgl_handle) { - phwi_ctrlr = phba->phwi_ctrlr; - pwrb_context = &phwi_ctrlr->wrb_context - [beiscsi_conn->beiscsi_conn_cid]; - free_wrb_handle(phba, pwrb_context, - io_task->pwrb_handle); - io_task->pwrb_handle = NULL; - SE_DEBUG(DBG_LVL_1, - "Alloc of SGL_ICD Failed \n"); - return -ENOMEM; - } + if (!io_task->psgl_handle) + goto free_hndls; + } else { io_task->scsi_cmnd = NULL; if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) { @@ -2913,18 +2914,9 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) io_task->psgl_handle = (struct sgl_handle *) alloc_mgmt_sgl_handle(phba); spin_unlock(&phba->mgmt_sgl_lock); - if (!io_task->psgl_handle) { - phwi_ctrlr = phba->phwi_ctrlr; - pwrb_context = - &phwi_ctrlr->wrb_context - [beiscsi_conn->beiscsi_conn_cid]; - free_wrb_handle(phba, pwrb_context, - io_task->pwrb_handle); - io_task->pwrb_handle = NULL; - SE_DEBUG(DBG_LVL_1, "Alloc of " - "MGMT_SGL_ICD Failed \n"); - return -ENOMEM; - } + if (!io_task->psgl_handle) + goto free_hndls; + beiscsi_conn->login_in_progress = 1; beiscsi_conn->plogin_sgl_handle = io_task->psgl_handle; @@ -2936,23 +2928,24 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) spin_lock(&phba->mgmt_sgl_lock); io_task->psgl_handle = alloc_mgmt_sgl_handle(phba); spin_unlock(&phba->mgmt_sgl_lock); - if (!io_task->psgl_handle) { - phwi_ctrlr = phba->phwi_ctrlr; - pwrb_context = &phwi_ctrlr->wrb_context - [beiscsi_conn->beiscsi_conn_cid]; - free_wrb_handle(phba, pwrb_context, - io_task->pwrb_handle); - io_task->pwrb_handle = NULL; - SE_DEBUG(DBG_LVL_1, "Alloc of " - "MGMT_SGL_ICD Failed \n"); - return -ENOMEM; - } + if (!io_task->psgl_handle) + goto free_hndls; } } itt = (itt_t) cpu_to_be32(((unsigned int)task->itt << 16) | (unsigned int)(io_task->psgl_handle->sgl_index)); io_task->cmd_bhs->iscsi_hdr.itt = itt; return 0; + +free_hndls: + phwi_ctrlr = phba->phwi_ctrlr; + pwrb_context = &phwi_ctrlr->wrb_context[beiscsi_conn->beiscsi_conn_cid]; + free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle); + io_task->pwrb_handle = NULL; + pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs, + io_task->bhs_pa.u.a64.address); + SE_DEBUG(DBG_LVL_1, "Alloc of SGL_ICD Failed \n"); + return -ENOMEM; } static void beiscsi_cleanup_task(struct iscsi_task *task) @@ -2961,6 +2954,7 @@ static void beiscsi_cleanup_task(struct iscsi_task *task) struct iscsi_conn *conn = task->conn; struct beiscsi_conn *beiscsi_conn = conn->dd_data; struct beiscsi_hba *phba = beiscsi_conn->phba; + struct beiscsi_session *beiscsi_sess = beiscsi_conn->beiscsi_sess; struct hwi_wrb_context *pwrb_context; struct hwi_controller *phwi_ctrlr; @@ -2971,6 +2965,11 @@ static void beiscsi_cleanup_task(struct iscsi_task *task) io_task->pwrb_handle = NULL; } + if (io_task->cmd_bhs) { + pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs, + io_task->bhs_pa.u.a64.address); + } + if (task->sc) { if (io_task->psgl_handle) { spin_lock(&phba->io_sgl_lock); @@ -3003,7 +3002,6 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg, unsigned int doorbell = 0; pwrb = io_task->pwrb_handle->pwrb; - io_task->cmd_bhs->iscsi_hdr.exp_statsn = 0; io_task->bhs_len = sizeof(struct be_cmd_bhs); @@ -3020,7 +3018,6 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg, &io_task->cmd_bhs->iscsi_data_pdu, 1); AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD); AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1); - } else { SE_DEBUG(DBG_LVL_4, "READ Command \t"); AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD); diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h index 387e363b0ec7..53c9b70ac7ac 100644 --- a/drivers/scsi/be2iscsi/be_main.h +++ b/drivers/scsi/be2iscsi/be_main.h @@ -391,7 +391,6 @@ struct beiscsi_io_task { unsigned short cid; unsigned short header_len; - unsigned int alloc_size; struct be_cmd_bhs *cmd_bhs; struct be_bus_address bhs_pa; unsigned short bhs_len; -- cgit v1.2.3 From 2c2ed8bfd899b84ecbf71d28fcc2cc4ace36c8d9 Mon Sep 17 00:00:00 2001 From: David Jeffery Date: Mon, 28 Sep 2009 13:54:24 -0400 Subject: [SCSI] st: fix possible memory use after free after MTSETBLK ioctl A memory use after free bug can manifest if the MTSETBLK or SET_DENS_AND_BLK ioctl features are used to set the tape's blocksize from 0 to non-zero. After the driver sets the new block size, in this one case it calls normalize_buffer() to free the device's internal data buffers. However, the ioctl code assumes there is always a buffer and does not check or allocate a buffer if there isn't one. So any following ioctl calls can corrupt a part of memory by writing data to memory that the st driver had freed. This patch removes the normalize_buffer() call and the specialness of changing from a 0 to non-zero blocksize to fix the possible use of memory after it has been freed by the st driver. signed-off-by: David Jeffery Acked-by: Kai Makisara Signed-off-by: James Bottomley --- drivers/scsi/st.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index b33d04250bbc..12d58a7ed6bc 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -2859,11 +2859,8 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon ioctl_result = st_int_ioctl(STp, MTBSF, 1); if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) { - int old_block_size = STp->block_size; STp->block_size = arg & MT_ST_BLKSIZE_MASK; if (STp->block_size != 0) { - if (old_block_size == 0) - normalize_buffer(STp->buffer); (STp->buffer)->buffer_blocks = (STp->buffer)->buffer_size / STp->block_size; } -- cgit v1.2.3 From c7922a911c42c5a8bdee6cc75eb6bd66937d4217 Mon Sep 17 00:00:00 2001 From: Anirban Chakraborty Date: Mon, 28 Sep 2009 13:52:58 -0700 Subject: [SCSI] qla2xxx: Fix NULL ptr deref bug in fail path during queue create Current code attempts to clean up resources when queue create fails and there it invokes queue free call with a (NULL) pointer to the queue which could not be allocated in the first place. Fix it by returning directly without invoking the queue free call as no resources has been allocated at that point of time. Reported-by: Jiri Slaby Signed-off-by: Anirban Chakraborty Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_mid.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index 42b799abba57..e07b3617f019 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -568,7 +568,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, if (req == NULL) { qla_printk(KERN_WARNING, ha, "could not allocate memory" "for request que\n"); - goto que_failed; + goto failed; } req->length = REQUEST_ENTRY_CNT_24XX; @@ -632,6 +632,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, que_failed: qla25xx_free_req_que(base_vha, req); +failed: return 0; } @@ -659,7 +660,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, if (rsp == NULL) { qla_printk(KERN_WARNING, ha, "could not allocate memory for" " response que\n"); - goto que_failed; + goto failed; } rsp->length = RESPONSE_ENTRY_CNT_MQ; @@ -728,6 +729,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, que_failed: qla25xx_free_rsp_que(base_vha, rsp); +failed: return 0; } -- cgit v1.2.3 From 61d92c328c16419fc96dc50dd16f8b8c695409ec Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 2 Oct 2009 19:11:56 -0400 Subject: Btrfs: fix deadlock on async thread startup The btrfs async worker threads are used for a wide variety of things, including processing bio end_io functions. This means that when the endio threads aren't running, the rest of the FS isn't able to do the final processing required to clear PageWriteback. The endio threads also try to exit as they become idle and start more as the work piles up. The problem is that starting more threads means kthreadd may need to allocate ram, and that allocation may wait until the global number of writeback pages on the system is below a certain limit. The result of that throttling is that end IO threads wait on kthreadd, who is waiting on IO to end, which will never happen. This commit fixes the deadlock by handing off thread startup to a dedicated thread. It also fixes a bug where the on-demand thread creation was creating far too many threads because it didn't take into account threads being started by other procs. Signed-off-by: Chris Mason --- fs/btrfs/async-thread.c | 81 ++++++++++++++++++++++++++++++++++++++++++++----- fs/btrfs/async-thread.h | 10 ++++-- fs/btrfs/ctree.h | 1 + fs/btrfs/disk-io.c | 42 +++++++++++++------------ fs/btrfs/relocation.c | 4 +-- 5 files changed, 106 insertions(+), 32 deletions(-) diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c index 282ca085c2fb..c0861e781cdb 100644 --- a/fs/btrfs/async-thread.c +++ b/fs/btrfs/async-thread.c @@ -63,6 +63,51 @@ struct btrfs_worker_thread { int idle; }; +/* + * btrfs_start_workers uses kthread_run, which can block waiting for memory + * for a very long time. It will actually throttle on page writeback, + * and so it may not make progress until after our btrfs worker threads + * process all of the pending work structs in their queue + * + * This means we can't use btrfs_start_workers from inside a btrfs worker + * thread that is used as part of cleaning dirty memory, which pretty much + * involves all of the worker threads. + * + * Instead we have a helper queue who never has more than one thread + * where we scheduler thread start operations. This worker_start struct + * is used to contain the work and hold a pointer to the queue that needs + * another worker. + */ +struct worker_start { + struct btrfs_work work; + struct btrfs_workers *queue; +}; + +static void start_new_worker_func(struct btrfs_work *work) +{ + struct worker_start *start; + start = container_of(work, struct worker_start, work); + btrfs_start_workers(start->queue, 1); + kfree(start); +} + +static int start_new_worker(struct btrfs_workers *queue) +{ + struct worker_start *start; + int ret; + + start = kzalloc(sizeof(*start), GFP_NOFS); + if (!start) + return -ENOMEM; + + start->work.func = start_new_worker_func; + start->queue = queue; + ret = btrfs_queue_worker(queue->atomic_worker_start, &start->work); + if (ret) + kfree(start); + return ret; +} + /* * helper function to move a thread onto the idle list after it * has finished some requests. @@ -118,11 +163,13 @@ static void check_pending_worker_creates(struct btrfs_worker_thread *worker) goto out; workers->atomic_start_pending = 0; - if (workers->num_workers >= workers->max_workers) + if (workers->num_workers + workers->num_workers_starting >= + workers->max_workers) goto out; + workers->num_workers_starting += 1; spin_unlock_irqrestore(&workers->lock, flags); - btrfs_start_workers(workers, 1); + start_new_worker(workers); return; out: @@ -390,9 +437,11 @@ int btrfs_stop_workers(struct btrfs_workers *workers) /* * simple init on struct btrfs_workers */ -void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max) +void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max, + struct btrfs_workers *async_helper) { workers->num_workers = 0; + workers->num_workers_starting = 0; INIT_LIST_HEAD(&workers->worker_list); INIT_LIST_HEAD(&workers->idle_list); INIT_LIST_HEAD(&workers->order_list); @@ -404,14 +453,15 @@ void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max) workers->name = name; workers->ordered = 0; workers->atomic_start_pending = 0; - workers->atomic_worker_start = 0; + workers->atomic_worker_start = async_helper; } /* * starts new worker threads. This does not enforce the max worker * count in case you need to temporarily go past it. */ -int btrfs_start_workers(struct btrfs_workers *workers, int num_workers) +static int __btrfs_start_workers(struct btrfs_workers *workers, + int num_workers) { struct btrfs_worker_thread *worker; int ret = 0; @@ -444,6 +494,8 @@ int btrfs_start_workers(struct btrfs_workers *workers, int num_workers) list_add_tail(&worker->worker_list, &workers->idle_list); worker->idle = 1; workers->num_workers++; + workers->num_workers_starting--; + WARN_ON(workers->num_workers_starting < 0); spin_unlock_irq(&workers->lock); } return 0; @@ -452,6 +504,14 @@ fail: return ret; } +int btrfs_start_workers(struct btrfs_workers *workers, int num_workers) +{ + spin_lock_irq(&workers->lock); + workers->num_workers_starting += num_workers; + spin_unlock_irq(&workers->lock); + return __btrfs_start_workers(workers, num_workers); +} + /* * run through the list and find a worker thread that doesn't have a lot * to do right now. This can return null if we aren't yet at the thread @@ -461,7 +521,10 @@ static struct btrfs_worker_thread *next_worker(struct btrfs_workers *workers) { struct btrfs_worker_thread *worker; struct list_head *next; - int enforce_min = workers->num_workers < workers->max_workers; + int enforce_min; + + enforce_min = (workers->num_workers + workers->num_workers_starting) < + workers->max_workers; /* * if we find an idle thread, don't move it to the end of the @@ -509,15 +572,17 @@ again: worker = next_worker(workers); if (!worker) { - if (workers->num_workers >= workers->max_workers) { + if (workers->num_workers + workers->num_workers_starting >= + workers->max_workers) { goto fallback; } else if (workers->atomic_worker_start) { workers->atomic_start_pending = 1; goto fallback; } else { + workers->num_workers_starting++; spin_unlock_irqrestore(&workers->lock, flags); /* we're below the limit, start another worker */ - btrfs_start_workers(workers, 1); + __btrfs_start_workers(workers, 1); goto again; } } diff --git a/fs/btrfs/async-thread.h b/fs/btrfs/async-thread.h index fc089b95ec14..5077746cf85e 100644 --- a/fs/btrfs/async-thread.h +++ b/fs/btrfs/async-thread.h @@ -64,6 +64,8 @@ struct btrfs_workers { /* current number of running workers */ int num_workers; + int num_workers_starting; + /* max number of workers allowed. changed by btrfs_start_workers */ int max_workers; @@ -78,9 +80,10 @@ struct btrfs_workers { /* * are we allowed to sleep while starting workers or are we required - * to start them at a later time? + * to start them at a later time? If we can't sleep, this indicates + * which queue we need to use to schedule thread creation. */ - int atomic_worker_start; + struct btrfs_workers *atomic_worker_start; /* list with all the work threads. The workers on the idle thread * may be actively servicing jobs, but they haven't yet hit the @@ -109,7 +112,8 @@ struct btrfs_workers { int btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work); int btrfs_start_workers(struct btrfs_workers *workers, int num_workers); int btrfs_stop_workers(struct btrfs_workers *workers); -void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max); +void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max, + struct btrfs_workers *async_starter); int btrfs_requeue_work(struct btrfs_work *work); void btrfs_set_work_high_prio(struct btrfs_work *work); #endif diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 8184f2feb2f3..1b920ffc6a59 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -907,6 +907,7 @@ struct btrfs_fs_info { * A third pool does submit_bio to avoid deadlocking with the other * two */ + struct btrfs_workers generic_worker; struct btrfs_workers workers; struct btrfs_workers delalloc_workers; struct btrfs_workers endio_workers; diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 69dce50aabd2..9903f042765d 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1746,21 +1746,22 @@ struct btrfs_root *open_ctree(struct super_block *sb, err = -EINVAL; goto fail_iput; } -printk("thread pool is %d\n", fs_info->thread_pool_size); - /* - * we need to start all the end_io workers up front because the - * queue work function gets called at interrupt time, and so it - * cannot dynamically grow. - */ + + btrfs_init_workers(&fs_info->generic_worker, + "genwork", 1, NULL); + btrfs_init_workers(&fs_info->workers, "worker", - fs_info->thread_pool_size); + fs_info->thread_pool_size, + &fs_info->generic_worker); btrfs_init_workers(&fs_info->delalloc_workers, "delalloc", - fs_info->thread_pool_size); + fs_info->thread_pool_size, + &fs_info->generic_worker); btrfs_init_workers(&fs_info->submit_workers, "submit", min_t(u64, fs_devices->num_devices, - fs_info->thread_pool_size)); + fs_info->thread_pool_size), + &fs_info->generic_worker); /* a higher idle thresh on the submit workers makes it much more * likely that bios will be send down in a sane order to the @@ -1774,15 +1775,20 @@ printk("thread pool is %d\n", fs_info->thread_pool_size); fs_info->delalloc_workers.idle_thresh = 2; fs_info->delalloc_workers.ordered = 1; - btrfs_init_workers(&fs_info->fixup_workers, "fixup", 1); + btrfs_init_workers(&fs_info->fixup_workers, "fixup", 1, + &fs_info->generic_worker); btrfs_init_workers(&fs_info->endio_workers, "endio", - fs_info->thread_pool_size); + fs_info->thread_pool_size, + &fs_info->generic_worker); btrfs_init_workers(&fs_info->endio_meta_workers, "endio-meta", - fs_info->thread_pool_size); + fs_info->thread_pool_size, + &fs_info->generic_worker); btrfs_init_workers(&fs_info->endio_meta_write_workers, - "endio-meta-write", fs_info->thread_pool_size); + "endio-meta-write", fs_info->thread_pool_size, + &fs_info->generic_worker); btrfs_init_workers(&fs_info->endio_write_workers, "endio-write", - fs_info->thread_pool_size); + fs_info->thread_pool_size, + &fs_info->generic_worker); /* * endios are largely parallel and should have a very @@ -1794,12 +1800,8 @@ printk("thread pool is %d\n", fs_info->thread_pool_size); fs_info->endio_write_workers.idle_thresh = 2; fs_info->endio_meta_write_workers.idle_thresh = 2; - fs_info->endio_workers.atomic_worker_start = 1; - fs_info->endio_meta_workers.atomic_worker_start = 1; - fs_info->endio_write_workers.atomic_worker_start = 1; - fs_info->endio_meta_write_workers.atomic_worker_start = 1; - btrfs_start_workers(&fs_info->workers, 1); + btrfs_start_workers(&fs_info->generic_worker, 1); btrfs_start_workers(&fs_info->submit_workers, 1); btrfs_start_workers(&fs_info->delalloc_workers, 1); btrfs_start_workers(&fs_info->fixup_workers, 1); @@ -2012,6 +2014,7 @@ fail_chunk_root: free_extent_buffer(chunk_root->node); free_extent_buffer(chunk_root->commit_root); fail_sb_buffer: + btrfs_stop_workers(&fs_info->generic_worker); btrfs_stop_workers(&fs_info->fixup_workers); btrfs_stop_workers(&fs_info->delalloc_workers); btrfs_stop_workers(&fs_info->workers); @@ -2437,6 +2440,7 @@ int close_ctree(struct btrfs_root *root) iput(fs_info->btree_inode); + btrfs_stop_workers(&fs_info->generic_worker); btrfs_stop_workers(&fs_info->fixup_workers); btrfs_stop_workers(&fs_info->delalloc_workers); btrfs_stop_workers(&fs_info->workers); diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 361ad323faac..cfcc93c93a7b 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -3518,7 +3518,7 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start) BUG_ON(!rc->block_group); btrfs_init_workers(&rc->workers, "relocate", - fs_info->thread_pool_size); + fs_info->thread_pool_size, NULL); rc->extent_root = extent_root; btrfs_prepare_block_group_relocation(extent_root, rc->block_group); @@ -3701,7 +3701,7 @@ int btrfs_recover_relocation(struct btrfs_root *root) mapping_tree_init(&rc->reloc_root_tree); INIT_LIST_HEAD(&rc->reloc_roots); btrfs_init_workers(&rc->workers, "relocate", - root->fs_info->thread_pool_size); + root->fs_info->thread_pool_size, NULL); rc->extent_root = root->fs_info->extent_root; set_reloc_control(rc); -- cgit v1.2.3 From e89e04fcdce6146cab3a34d4073f8a1714b457ec Mon Sep 17 00:00:00 2001 From: Srinidhi Kasagar Date: Mon, 5 Oct 2009 06:13:53 +0100 Subject: ARM: 5741/1: pl022: fix peripheral id for ST vendor This fixes the wrong peripheral id being used in pl022 driver for ST derivative. Signed-off-by: srinidhi kasagar Signed-off-by: Russell King --- drivers/spi/amba-pl022.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c index 958a3ffc8987..ff5bbb9c43c9 100644 --- a/drivers/spi/amba-pl022.c +++ b/drivers/spi/amba-pl022.c @@ -1826,7 +1826,7 @@ static struct amba_id pl022_ids[] = { * ST Micro derivative, this has 32bit wide * and 32 locations deep TX/RX FIFO */ - .id = 0x00108022, + .id = 0x01080022, .mask = 0xffffffff, .data = &vendor_st, }, -- cgit v1.2.3 From 1d2127123db9b1821959c2b8b7473dd7ffcdf527 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 5 Oct 2009 13:40:44 +0100 Subject: ARM: 5742/1: ARM: add debug check for invalid kernel page faults According to the following in arch/arm/mm/fault.c page faults from kernel mode are invalid if mmap_sem is already held and there is no exception handler defined for the faulting instruction: /* * As per x86, we may deadlock here. However, since the kernel only * validly references user space from well defined areas of the code, * we can bug out early if this is from code which shouldn't. */ if (!down_read_trylock(&mm->mmap_sem)) { if (!user_mode(regs) && !search_exception_tables(regs->ARM_pc)) goto no_context; Since mmap_sem can be held at arbitrary times by another thread this also means that any page faults from kernel mode are invalid if no exception handler is defined for them, regardless whether mmap_sem is held at the time of fault. To easier detect code that can trigger the above error, add a check also for the case where mmap_sem is acquired. As this has an overhead make it a VM debug check. Signed-off-by: Imre Deak Signed-off-by: Russell King --- arch/arm/mm/fault.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index ae0e25f5a70e..10e06801afb3 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -292,6 +292,11 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) * down_read() */ might_sleep(); +#ifdef CONFIG_DEBUG_VM + if (!user_mode(regs) && + !search_exception_tables(regs->ARM_pc)) + goto no_context; +#endif } fault = __do_page_fault(mm, addr, fsr, tsk); -- cgit v1.2.3 From 52a21f2cee108ea1c8abc4fdaf64a66f21af26db Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 6 Oct 2009 10:33:55 +0200 Subject: [S390] fix build breakage with CONFIG_AIO=n next-20090925 randconfig build breaks on s390x, with CONFIG_AIO=n. arch/s390/mm/pgtable.c: In function 's390_enable_sie': arch/s390/mm/pgtable.c:282: error: 'struct mm_struct' has no member named 'ioctx_list' arch/s390/mm/pgtable.c:298: error: 'struct mm_struct' has no member named 'ioctx_list' make[1]: *** [arch/s390/mm/pgtable.o] Error 1 Reported-by: Kamalesh Babulal Signed-off-by: Martin Schwidefsky --- arch/s390/mm/pgtable.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index c60bfb309ce6..2757c5616a07 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -279,7 +279,10 @@ int s390_enable_sie(void) /* lets check if we are allowed to replace the mm */ task_lock(tsk); if (!tsk->mm || atomic_read(&tsk->mm->mm_users) > 1 || - tsk->mm != tsk->active_mm || !hlist_empty(&tsk->mm->ioctx_list)) { +#ifdef CONFIG_AIO + !hlist_empty(&tsk->mm->ioctx_list) || +#endif + tsk->mm != tsk->active_mm) { task_unlock(tsk); return -EINVAL; } @@ -295,7 +298,10 @@ int s390_enable_sie(void) /* Now lets check again if something happened */ task_lock(tsk); if (!tsk->mm || atomic_read(&tsk->mm->mm_users) > 1 || - tsk->mm != tsk->active_mm || !hlist_empty(&tsk->mm->ioctx_list)) { +#ifdef CONFIG_AIO + !hlist_empty(&tsk->mm->ioctx_list) || +#endif + tsk->mm != tsk->active_mm) { mmput(mm); task_unlock(tsk); return -EINVAL; -- cgit v1.2.3 From 34483cac2310846b2f210378ee7cd8150c4ae768 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 6 Oct 2009 10:33:56 +0200 Subject: [S390] 3270 console build fix Fix this build failure: drivers/s390/built-in.o: In function `raw3270_pm_unfreeze': (.text+0x3ac04): undefined reference to `ccw_device_force_console' with: CONFIG_TN3270=y CONFIG_TN3270_CONSOLE=n CONFIG_TN3215_CONSOLE=n Reported-by: Kamalesh Babulal Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/char/raw3270.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index d6a022f55e92..62ddf5202b79 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -1361,11 +1361,13 @@ static int raw3270_pm_start(struct ccw_device *cdev) void raw3270_pm_unfreeze(struct raw3270_view *view) { +#ifdef CONFIG_TN3270_CONSOLE struct raw3270 *rp; rp = view->dev; if (rp && test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) ccw_device_force_console(); +#endif } static struct ccw_device_id raw3270_id[] = { -- cgit v1.2.3 From dfcc3e6a8b8c10bccc532e6bd5a25d50ccb14b25 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 6 Oct 2009 10:33:57 +0200 Subject: [S390] Enable kmemleak on s390. Also increase the maximum possible kmemleak early log entries since 2000 are not sufficient on s390. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/vmlinux.lds.S | 1 + lib/Kconfig.debug | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index bc15ef93e656..a68ac10213b2 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -51,6 +51,7 @@ SECTIONS . = ALIGN(PAGE_SIZE); _eshared = .; /* End of shareable data */ + _sdata = .; /* Start of data section */ EXCEPTION_TABLE(16) :data diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 891155817bc6..30df5865ecbe 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -346,8 +346,9 @@ config SLUB_STATS config DEBUG_KMEMLEAK bool "Kernel memory leak detector" - depends on DEBUG_KERNEL && EXPERIMENTAL && (X86 || ARM || PPC) && \ - !MEMORY_HOTPLUG + depends on DEBUG_KERNEL && EXPERIMENTAL && !MEMORY_HOTPLUG && \ + (X86 || ARM || PPC || S390) + select DEBUG_FS if SYSFS select STACKTRACE if STACKTRACE_SUPPORT select KALLSYMS @@ -370,7 +371,7 @@ config DEBUG_KMEMLEAK config DEBUG_KMEMLEAK_EARLY_LOG_SIZE int "Maximum kmemleak early log entries" depends on DEBUG_KMEMLEAK - range 200 2000 + range 200 40000 default 400 help Kmemleak must track all the memory allocations to avoid -- cgit v1.2.3 From 6a03f5f0a0a660895eac03749165c1f9313ef2b8 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 6 Oct 2009 10:33:58 +0200 Subject: [S390] module: fix memory leak in s390 module loader The s390 version of module_frob_arch_sections allocates additional syminfos for got and plt offsets. These syminfos are freed on sucessful module load. If the module fails to load (e.g. missing dependency when using insmod instead of modprobe) this area is not freed. This patch lets module_free free this area. Please note, we have to set the pointer to NULL since module_free is called several times from the generic code. Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/module.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c index ab2e3ed28abc..639380a0c45c 100644 --- a/arch/s390/kernel/module.c +++ b/arch/s390/kernel/module.c @@ -55,6 +55,8 @@ void *module_alloc(unsigned long size) /* Free memory returned from module_alloc */ void module_free(struct module *mod, void *module_region) { + vfree(mod->arch.syminfo); + mod->arch.syminfo = NULL; vfree(module_region); } @@ -402,6 +404,7 @@ int module_finalize(const Elf_Ehdr *hdr, struct module *me) { vfree(me->arch.syminfo); + me->arch.syminfo = NULL; return module_bug_finalize(hdr, sechdrs, me); } -- cgit v1.2.3 From ec00440786f413133997396308f41184eb705a6d Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Tue, 6 Oct 2009 10:33:59 +0200 Subject: [S390] cio: channel path memory leak Move dev_set_name to when we know that the device will actually be registered in order to avoid a memory leak if the allocated memory for the channel path has to be freed. Signed-off-by: Michael Ernst Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 40002830d48a..8ab51608da55 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -393,7 +393,6 @@ int chp_new(struct chp_id chpid) chp->state = 1; chp->dev.parent = &channel_subsystems[chpid.cssid]->device; chp->dev.release = chp_release; - dev_set_name(&chp->dev, "chp%x.%02x", chpid.cssid, chpid.id); /* Obtain channel path description and fill it in. */ ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc); @@ -411,6 +410,7 @@ int chp_new(struct chp_id chpid) } else { chp->cmg = -1; } + dev_set_name(&chp->dev, "chp%x.%02x", chpid.cssid, chpid.id); /* make it known to the system */ ret = device_register(&chp->dev); -- cgit v1.2.3 From 05d419b11fa2445f71ff495de6394ce8c2960343 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 6 Oct 2009 10:34:00 +0200 Subject: [S390] Fix memory leak in /proc/cio_ignore There is a memory leak in /proc/cio_ignore. The iterator is allocated in cio_ignore_proc_seq_start, but never freed in cio_ignore_proc_seq_stop, because we cannot use the iterator that was passed by seqfile. The seqfile interface passes the last seen iterator to the stop function and not the first one. Since our next function will return NULL at the end, the iter passed to cio_ignore_proc_seq_stop is NULL. The original iter has leaked. The solution is to use seq_open_private. Found with kmemleak: unreferenced object 0x1c720580 (size 32): comm "head", pid 973, jiffies 4294958302 hex dump (first 32 bytes): 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<0000000000203154>] kmem_cache_alloc+0x190/0x19c [<00000000003fb462>] cio_ignore_proc_seq_start+0x5e/0x128 [<0000000000231018>] seq_read+0xc8/0x4bc [<0000000000273954>] proc_reg_read+0xa8/0xf4 [<000000000020e3d8>] vfs_read+0xac/0x1a4 [<000000000020e5c6>] SyS_read+0x52/0xa8 [<000000000011836e>] sysc_noemu+0x10/0x16 [<0000004690b7936c>] 0x4690b7936c Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/blacklist.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index 6565f027791e..7eab9ab9f406 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -265,13 +265,11 @@ struct ccwdev_iter { static void * cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset) { - struct ccwdev_iter *iter; + struct ccwdev_iter *iter = s->private; if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1)) return NULL; - iter = kzalloc(sizeof(struct ccwdev_iter), GFP_KERNEL); - if (!iter) - return ERR_PTR(-ENOMEM); + memset(iter, 0, sizeof(*iter)); iter->ssid = *offset / (__MAX_SUBCHANNEL + 1); iter->devno = *offset % (__MAX_SUBCHANNEL + 1); return iter; @@ -280,8 +278,6 @@ cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset) static void cio_ignore_proc_seq_stop(struct seq_file *s, void *it) { - if (!IS_ERR(it)) - kfree(it); } static void * @@ -378,14 +374,15 @@ static const struct seq_operations cio_ignore_proc_seq_ops = { static int cio_ignore_proc_open(struct inode *inode, struct file *file) { - return seq_open(file, &cio_ignore_proc_seq_ops); + return seq_open_private(file, &cio_ignore_proc_seq_ops, + sizeof(struct ccwdev_iter)); } static const struct file_operations cio_ignore_proc_fops = { .open = cio_ignore_proc_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = seq_release_private, .write = cio_ignore_write, }; -- cgit v1.2.3 From 9a332116948955bd25d122efd91feed103f0e3e4 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Tue, 6 Oct 2009 10:34:01 +0200 Subject: [S390] cio: make disconnected handling consistent When there is no path left to a ccw device, inform the associated device driver and act according to the response: if the driver wants to keep the device, put it into the disconnected state. If not, or if there is no driver or if the device is not online, unregister it. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_fsm.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index e728ce447f6e..3db3847ee137 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -387,19 +387,33 @@ ccw_device_done(struct ccw_device *cdev, int state) cdev->private->state = state; - if (state == DEV_STATE_BOXED) { + switch (state) { + case DEV_STATE_BOXED: CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (cdev->online && !ccw_device_notify(cdev, CIO_BOXED)) ccw_device_schedule_sch_unregister(cdev); cdev->private->flags.donotify = 0; - } - if (state == DEV_STATE_NOT_OPER) { + break; + case DEV_STATE_NOT_OPER: CIO_MSG_EVENT(0, "Device %04x gone on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (!ccw_device_notify(cdev, CIO_GONE)) ccw_device_schedule_sch_unregister(cdev); cdev->private->flags.donotify = 0; + break; + case DEV_STATE_DISCONNECTED: + CIO_MSG_EVENT(0, "Disconnected device %04x on subchannel " + "%04x\n", cdev->private->dev_id.devno, + sch->schid.sch_no); + if (!ccw_device_notify(cdev, CIO_NO_PATH)) + ccw_device_schedule_sch_unregister(cdev); + else + ccw_device_set_disconnected(cdev); + cdev->private->flags.donotify = 0; + break; + default: + break; } if (cdev->private->flags.donotify) { -- cgit v1.2.3 From 6afcc775d9d66fe550fad6c579f78b3c3da895b8 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Tue, 6 Oct 2009 10:34:02 +0200 Subject: [S390] cio: make not operational handling consistent When a ccw device appears not operational, inform the associated device driver and act according to the response: if the driver wants to keep the device, put it into the disconnected state. If not, or if there is no driver or if the device is not online, unregister it. This approach is consistent with no-path event handling. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 4 ++-- drivers/s390/cio/device.h | 1 + drivers/s390/cio/device_fsm.c | 11 ++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index f780bdd3a04e..2ee093ec86e4 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1609,7 +1609,7 @@ int ccw_purge_blacklisted(void) return 0; } -static void device_set_disconnected(struct ccw_device *cdev) +void ccw_device_set_disconnected(struct ccw_device *cdev) { if (!cdev) return; @@ -1705,7 +1705,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow) ccw_device_trigger_reprobe(cdev); break; case DISC: - device_set_disconnected(cdev); + ccw_device_set_disconnected(cdev); break; default: break; diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index ed39a2caaf47..246c6482842c 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -125,6 +125,7 @@ int ccw_device_stlck(struct ccw_device *); void ccw_device_trigger_reprobe(struct ccw_device *); void ccw_device_kill_io(struct ccw_device *); int ccw_device_notify(struct ccw_device *, int); +void ccw_device_set_disconnected(struct ccw_device *cdev); void ccw_device_set_notoper(struct ccw_device *cdev); /* qdio needs this. */ diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 3db3847ee137..3b0f408a896c 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -400,6 +400,8 @@ ccw_device_done(struct ccw_device *cdev, int state) cdev->private->dev_id.devno, sch->schid.sch_no); if (!ccw_device_notify(cdev, CIO_GONE)) ccw_device_schedule_sch_unregister(cdev); + else + ccw_device_set_disconnected(cdev); cdev->private->flags.donotify = 0; break; case DEV_STATE_DISCONNECTED: @@ -744,11 +746,10 @@ ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event) static void ccw_device_generic_notoper(struct ccw_device *cdev, enum dev_event dev_event) { - struct subchannel *sch; - - ccw_device_set_notoper(cdev); - sch = to_subchannel(cdev->dev.parent); - css_schedule_eval(sch->schid); + if (!ccw_device_notify(cdev, CIO_GONE)) + ccw_device_schedule_sch_unregister(cdev); + else + ccw_device_set_disconnected(cdev); } /* -- cgit v1.2.3 From 102e835d5152e4299c1d150d6481b9bd47095998 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Tue, 6 Oct 2009 10:34:03 +0200 Subject: [S390] cio: allow setting boxed devices offline Allow users to set boxed devices offline. After setting them offline, the device state will still be boxed. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_fsm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 3b0f408a896c..b9613d7df9ef 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -687,6 +687,10 @@ ccw_device_offline(struct ccw_device *cdev) ccw_device_done(cdev, DEV_STATE_NOT_OPER); return 0; } + if (cdev->private->state == DEV_STATE_BOXED) { + ccw_device_done(cdev, DEV_STATE_BOXED); + return 0; + } if (ccw_device_is_orphan(cdev)) { ccw_device_done(cdev, DEV_STATE_OFFLINE); return 0; -- cgit v1.2.3 From 78d81f2f844b739b377817cfd279fb6067e191a7 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 6 Oct 2009 10:34:04 +0200 Subject: [S390] Fix enabled udelay for short delays. When udelay() gets called with a delay that would expire before the next clock event it reprograms the clock comparator. When the interrupt happens the clock comparator won't be resetted therefore the interrupt condition doesn't get cleared. The result is an endless timer interrupt loop until the next clock event would expire (stored in lowcore). So udelay() usually would wait much longer for small delays than it should. Fix this by disabling the local tick which makes sure that the clock comparator will be resetted when a timer interrupt happens. Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky --- arch/s390/lib/delay.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/s390/lib/delay.c b/arch/s390/lib/delay.c index 97c1eca83cc2..2c3094342789 100644 --- a/arch/s390/lib/delay.c +++ b/arch/s390/lib/delay.c @@ -49,17 +49,22 @@ static void __udelay_disabled(unsigned long usecs) static void __udelay_enabled(unsigned long usecs) { unsigned long mask; - u64 end, time; + u64 clock_saved; + u64 end; mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT | PSW_MASK_IO; end = get_clock() + ((u64) usecs << 12); do { - time = end < S390_lowcore.clock_comparator ? - end : S390_lowcore.clock_comparator; - set_clock_comparator(time); + clock_saved = 0; + if (end < S390_lowcore.clock_comparator) { + clock_saved = local_tick_disable(); + set_clock_comparator(end); + } trace_hardirqs_on(); __load_psw_mask(mask); local_irq_disable(); + if (clock_saved) + local_tick_enable(clock_saved); } while (get_clock() < end); set_clock_comparator(S390_lowcore.clock_comparator); } -- cgit v1.2.3 From 0cd6a403e8f86bb24975e4c16ce640a063475515 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 6 Oct 2009 10:34:05 +0200 Subject: [S390] Provide arch specific mdelay implementation. Use an own implementation instead of the common code udelay loop. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/delay.h | 7 ++++--- arch/s390/lib/delay.c | 14 +++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/s390/include/asm/delay.h b/arch/s390/include/asm/delay.h index a356c958e260..8a096b83f51f 100644 --- a/arch/s390/include/asm/delay.h +++ b/arch/s390/include/asm/delay.h @@ -14,10 +14,11 @@ #ifndef _S390_DELAY_H #define _S390_DELAY_H -extern void __udelay(unsigned long usecs); -extern void udelay_simple(unsigned long usecs); +extern void __udelay(unsigned long long usecs); +extern void udelay_simple(unsigned long long usecs); extern void __delay(unsigned long loops); -#define udelay(n) __udelay(n) +#define udelay(n) __udelay((unsigned long long) (n)) +#define mdelay(n) __udelay((unsigned long long) (n) * 1000) #endif /* defined(_S390_DELAY_H) */ diff --git a/arch/s390/lib/delay.c b/arch/s390/lib/delay.c index 2c3094342789..752b362bf651 100644 --- a/arch/s390/lib/delay.c +++ b/arch/s390/lib/delay.c @@ -25,13 +25,13 @@ void __delay(unsigned long loops) asm volatile("0: brct %0,0b" : : "d" ((loops/2) + 1)); } -static void __udelay_disabled(unsigned long usecs) +static void __udelay_disabled(unsigned long long usecs) { unsigned long mask, cr0, cr0_saved; u64 clock_saved; clock_saved = local_tick_disable(); - set_clock_comparator(get_clock() + ((u64) usecs << 12)); + set_clock_comparator(get_clock() + (usecs << 12)); __ctl_store(cr0_saved, 0, 0); cr0 = (cr0_saved & 0xffff00e0) | 0x00000800; __ctl_load(cr0 , 0, 0); @@ -46,14 +46,14 @@ static void __udelay_disabled(unsigned long usecs) set_clock_comparator(S390_lowcore.clock_comparator); } -static void __udelay_enabled(unsigned long usecs) +static void __udelay_enabled(unsigned long long usecs) { unsigned long mask; u64 clock_saved; u64 end; mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_EXT | PSW_MASK_IO; - end = get_clock() + ((u64) usecs << 12); + end = get_clock() + (usecs << 12); do { clock_saved = 0; if (end < S390_lowcore.clock_comparator) { @@ -72,7 +72,7 @@ static void __udelay_enabled(unsigned long usecs) /* * Waits for 'usecs' microseconds using the TOD clock comparator. */ -void __udelay(unsigned long usecs) +void __udelay(unsigned long long usecs) { unsigned long flags; @@ -106,11 +106,11 @@ EXPORT_SYMBOL(__udelay); * Simple udelay variant. To be used on startup and reboot * when the interrupt handler isn't working. */ -void udelay_simple(unsigned long usecs) +void udelay_simple(unsigned long long usecs) { u64 end; - end = get_clock() + ((u64) usecs << 12); + end = get_clock() + (usecs << 12); while (get_clock() < end) cpu_relax(); } -- cgit v1.2.3 From 593c4f739859594dc4824b6d29f9abb1f0b3c669 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 6 Oct 2009 10:34:06 +0200 Subject: [S390] compat: fix truncate system call wrapper The system call takes a signed length parameter. So perform sign extension instead of zero extension. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/compat_wrapper.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index 682fb69dba21..cbd9901dc0f8 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -409,7 +409,7 @@ sys32_munmap_wrapper: .globl sys32_truncate_wrapper sys32_truncate_wrapper: llgtr %r2,%r2 # const char * - llgfr %r3,%r3 # unsigned long + lgfr %r3,%r3 # long jg sys_truncate # branch to system call .globl sys32_ftruncate_wrapper -- cgit v1.2.3 From 22ceaf408f22680b7448f2699567ba22202e6281 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 6 Oct 2009 10:34:07 +0200 Subject: [S390] ftrace: drop nmi protection The function graph tracer used to have a protection against NMI while entering a function entry tracing. But this is useless now, the tracer is reentrant and the ring buffer supports NMI tracing. Same as 07868b086cca784f4b532fc2ab574ec3a73b468a for x86. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/ftrace.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c index 57bdcb1e3cdf..f5fe34dd821b 100644 --- a/arch/s390/kernel/ftrace.c +++ b/arch/s390/kernel/ftrace.c @@ -185,9 +185,6 @@ unsigned long prepare_ftrace_return(unsigned long ip, unsigned long parent) { struct ftrace_graph_ent trace; - /* Nmi's are currently unsupported. */ - if (unlikely(in_nmi())) - goto out; if (unlikely(atomic_read(¤t->tracing_graph_pause))) goto out; if (ftrace_push_return_trace(parent, ip, &trace.depth, 0) == -EBUSY) -- cgit v1.2.3 From 930e44fbeaaccb412194a2d011359714158fd9e0 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 6 Oct 2009 10:34:08 +0200 Subject: [S390] perf_counter: fix vdso detection s390 version of f2053f1a "powerpc/perf_counter: Fix vdso detection". Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/vdso.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c index 45a3e9a7ae21..adfb32aa6d59 100644 --- a/arch/s390/kernel/vdso.c +++ b/arch/s390/kernel/vdso.c @@ -246,6 +246,13 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) goto out_up; } + /* + * Put vDSO base into mm struct. We need to do this before calling + * install_special_mapping or the perf counter mmap tracking code + * will fail to recognise it as a vDSO (since arch_vma_name fails). + */ + current->mm->context.vdso_base = vdso_base; + /* * our vma flags don't have VM_WRITE so by default, the process * isn't allowed to write those pages. @@ -267,14 +274,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) VM_ALWAYSDUMP, vdso_pagelist); if (rc) - goto out_up; - - /* Put vDSO base into mm struct */ - current->mm->context.vdso_base = vdso_base; - - up_write(&mm->mmap_sem); - return 0; - + current->mm->context.vdso_base = 0; out_up: up_write(&mm->mmap_sem); return rc; -- cgit v1.2.3 From 6458abc925e8ade4ad02f4d69b0281f6b3da5e14 Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Tue, 6 Oct 2009 10:34:09 +0200 Subject: [S390] zcrypt: Fix sparse warning. Do not shadow earlier symbol. Signed-off-by: Felix Beck Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_pcixcc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index c20d4790258e..2218cef1749b 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -361,7 +361,7 @@ static void rng_type6CPRB_msgX(struct ap_device *ap_dev, .ToCardLen1 = sizeof *msg - sizeof(msg->hdr), .FromCardLen1 = sizeof *msg - sizeof(msg->hdr), }; - static struct CPRBX static_cprbx = { + static struct CPRBX local_cprbx = { .cprb_len = 0x00dc, .cprb_ver_id = 0x02, .func_id = {0x54, 0x32}, @@ -372,7 +372,7 @@ static void rng_type6CPRB_msgX(struct ap_device *ap_dev, msg->hdr = static_type6_hdrX; msg->hdr.FromCardLen2 = random_number_length, - msg->cprbx = static_cprbx; + msg->cprbx = local_cprbx; msg->cprbx.rpl_datal = random_number_length, msg->cprbx.domain = AP_QID_QUEUE(ap_dev->qid); memcpy(msg->function_code, msg->hdr.function_code, 0x02); -- cgit v1.2.3 From 942b7e65c321b72e1d73daee45291300be87c62b Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Tue, 6 Oct 2009 10:34:10 +0200 Subject: [S390] zcrypt: Improve some comments Improve the comments for switch cases without a break. This fixes some warnings of a code checker tool. Signed-off-by: Felix Beck Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_pcixcc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 2218cef1749b..5677b40e4ac0 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -561,7 +561,8 @@ static int convert_response_ica(struct zcrypt_device *zdev, if (msg->cprbx.cprb_ver_id == 0x02) return convert_type86_ica(zdev, reply, outputdata, outputdatalength); - /* no break, incorrect cprb version is an unknown response */ + /* Fall through, no break, incorrect cprb version is an unknown + * response */ default: /* Unknown response type, this should NEVER EVER happen */ zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ @@ -587,7 +588,8 @@ static int convert_response_xcrb(struct zcrypt_device *zdev, } if (msg->cprbx.cprb_ver_id == 0x02) return convert_type86_xcrb(zdev, reply, xcRB); - /* no break, incorrect cprb version is an unknown response */ + /* Fall through, no break, incorrect cprb version is an unknown + * response */ default: /* Unknown response type, this should NEVER EVER happen */ xcRB->status = 0x0008044DL; /* HDD_InvalidParm */ zdev->online = 0; @@ -610,7 +612,8 @@ static int convert_response_rng(struct zcrypt_device *zdev, return -EINVAL; if (msg->cprbx.cprb_ver_id == 0x02) return convert_type86_rng(zdev, reply, data); - /* no break, incorrect cprb version is an unknown response */ + /* Fall through, no break, incorrect cprb version is an unknown + * response */ default: /* Unknown response type, this should NEVER EVER happen */ zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ -- cgit v1.2.3 From 623c08e4cbf47c29c2516d53f1d78c20896bb712 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 6 Oct 2009 10:34:11 +0200 Subject: [S390] pm: ignore time spend in suspended state The time a system has been suspended should not show up in any of the cputime accounting fields. The time of inactivity is definitly not any form of real cputime nor is it idle time. Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/swsusp_asm64.S | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/arch/s390/kernel/swsusp_asm64.S b/arch/s390/kernel/swsusp_asm64.S index fe927d0bc20b..008e35af339f 100644 --- a/arch/s390/kernel/swsusp_asm64.S +++ b/arch/s390/kernel/swsusp_asm64.S @@ -69,8 +69,21 @@ swsusp_arch_suspend: stmg %r0,%r15,0x280(%r1) /* store general registers */ stpt 0x328(%r1) /* store timer */ + stck __SF_EMPTY(%r15) /* store clock */ stckc 0x330(%r1) /* store clock comparator */ + /* Update cputime accounting before going to sleep */ + lg %r0,__LC_LAST_UPDATE_TIMER + slg %r0,0x328(%r1) + alg %r0,__LC_SYSTEM_TIMER + stg %r0,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),0x328(%r1) + lg %r0,__LC_LAST_UPDATE_CLOCK + slg %r0,__SF_EMPTY(%r15) + alg %r0,__LC_STEAL_TIMER + stg %r0,__LC_STEAL_TIMER + mvc __LC_LAST_UPDATE_CLOCK(8),__SF_EMPTY(%r15) + /* Activate DAT */ stosm __SF_EMPTY(%r15),0x04 @@ -200,8 +213,11 @@ restart_suspend: restore_registers: /* Restore registers */ - lghi %r13,0x1000 /* %r1 = pointer to save arae */ + lghi %r13,0x1000 /* %r1 = pointer to save area */ + /* Ignore time spent in suspended state. */ + llgf %r1,0x318(%r13) + stck __LC_LAST_UPDATE_CLOCK(%r1) spt 0x328(%r13) /* reprogram timer */ //sckc 0x330(%r13) /* set clock comparator */ @@ -229,9 +245,6 @@ restore_registers: /* Load old stack */ lg %r15,0x2f8(%r13) - /* Pointer to save area */ - lghi %r13,0x1000 - /* Restore prefix register */ spx 0x318(%r13) -- cgit v1.2.3 From dd43bfca431b02117e8598e01b301e001a68295e Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Tue, 6 Oct 2009 10:34:12 +0200 Subject: [S390] hibernate: Use correct place for CPU address in lowcore We used address 0x1084 instead of 0x84 to store the suspend CPU address. With this patch we use the correct address 0x84 as it is defined in the POP. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/swsusp_asm64.S | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/s390/kernel/swsusp_asm64.S b/arch/s390/kernel/swsusp_asm64.S index 008e35af339f..7c8653e27db6 100644 --- a/arch/s390/kernel/swsusp_asm64.S +++ b/arch/s390/kernel/swsusp_asm64.S @@ -43,7 +43,7 @@ swsusp_arch_suspend: lghi %r1,0x1000 /* Save CPU address */ - stap __LC_CPU_ADDRESS(%r1) + stap __LC_CPU_ADDRESS(%r0) /* Store registers */ mvc 0x318(4,%r1),__SF_EMPTY(%r15) /* move prefix to lowcore */ @@ -172,8 +172,7 @@ pgm_check_entry: larl %r1,.Lresume_cpu /* Resume CPU address: r2 */ stap 0(%r1) llgh %r2,0(%r1) - lghi %r3,0x1000 - llgh %r1,__LC_CPU_ADDRESS(%r3) /* Suspend CPU address: r1 */ + llgh %r1,__LC_CPU_ADDRESS(%r0) /* Suspend CPU address: r1 */ cgr %r1,%r2 je restore_registers /* r1 = r2 -> nothing to do */ larl %r4,.Lrestart_suspend_psw /* Set new restart PSW */ -- cgit v1.2.3 From ea2a4d3a3a929ef494952bba57a0ef1a8a877881 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 6 Oct 2009 10:34:13 +0200 Subject: [S390] 64-bit register support for 31-bit processes From: Heiko Carstens From: Martin Schwidefsky Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/elf.h | 12 +++++++ arch/s390/include/asm/ptrace.h | 4 +++ arch/s390/include/asm/ucontext.h | 15 +++++++++ arch/s390/kernel/compat_signal.c | 35 +++++++++++++++++++- arch/s390/kernel/ptrace.c | 70 ++++++++++++++++++++++++++++++++++++++++ arch/s390/kernel/setup.c | 15 +++++++-- include/linux/elf.h | 1 + 7 files changed, 148 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/elf.h b/arch/s390/include/asm/elf.h index 74d0bbb7d955..e885442c1dfe 100644 --- a/arch/s390/include/asm/elf.h +++ b/arch/s390/include/asm/elf.h @@ -92,6 +92,18 @@ /* Keep this the last entry. */ #define R_390_NUM 61 +/* Bits present in AT_HWCAP. */ +#define HWCAP_S390_ESAN3 1 +#define HWCAP_S390_ZARCH 2 +#define HWCAP_S390_STFLE 4 +#define HWCAP_S390_MSA 8 +#define HWCAP_S390_LDISP 16 +#define HWCAP_S390_EIMM 32 +#define HWCAP_S390_DFP 64 +#define HWCAP_S390_HPAGE 128 +#define HWCAP_S390_ETF3EH 256 +#define HWCAP_S390_HIGH_GPRS 512 + /* * These are used to set parameters in the core dumps. */ diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 539263fc9ab9..95dcf183a28d 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h @@ -311,6 +311,10 @@ typedef struct __u32 orig_gpr2; } s390_compat_regs; +typedef struct +{ + __u32 gprs_high[NUM_GPRS]; +} s390_compat_regs_high; #ifdef __KERNEL__ diff --git a/arch/s390/include/asm/ucontext.h b/arch/s390/include/asm/ucontext.h index d69bec0b03f5..cfb874e66c9a 100644 --- a/arch/s390/include/asm/ucontext.h +++ b/arch/s390/include/asm/ucontext.h @@ -9,6 +9,21 @@ #ifndef _ASM_S390_UCONTEXT_H #define _ASM_S390_UCONTEXT_H +#define UC_EXTENDED 0x00000001 + +#ifndef __s390x__ + +struct ucontext_extended { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + _sigregs uc_mcontext; + unsigned long uc_sigmask[2]; + unsigned long uc_gprs_high[16]; +}; + +#endif + struct ucontext { unsigned long uc_flags; struct ucontext *uc_link; diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index b537cb0e9b55..eee999853a7c 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c @@ -39,6 +39,7 @@ typedef struct struct sigcontext32 sc; _sigregs32 sregs; int signo; + __u32 gprs_high[NUM_GPRS]; __u8 retcode[S390_SYSCALL_SIZE]; } sigframe32; @@ -48,6 +49,7 @@ typedef struct __u8 retcode[S390_SYSCALL_SIZE]; compat_siginfo_t info; struct ucontext32 uc; + __u32 gprs_high[NUM_GPRS]; } rt_sigframe32; int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from) @@ -344,6 +346,30 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) return 0; } +static int save_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs) +{ + __u32 gprs_high[NUM_GPRS]; + int i; + + for (i = 0; i < NUM_GPRS; i++) + gprs_high[i] = regs->gprs[i] >> 32; + + return __copy_to_user(uregs, &gprs_high, sizeof(gprs_high)); +} + +static int restore_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs) +{ + __u32 gprs_high[NUM_GPRS]; + int err, i; + + err = __copy_from_user(&gprs_high, uregs, sizeof(gprs_high)); + if (err) + return err; + for (i = 0; i < NUM_GPRS; i++) + *(__u32 *)®s->gprs[i] = gprs_high[i]; + return 0; +} + asmlinkage long sys32_sigreturn(void) { struct pt_regs *regs = task_pt_regs(current); @@ -363,6 +389,8 @@ asmlinkage long sys32_sigreturn(void) if (restore_sigregs32(regs, &frame->sregs)) goto badframe; + if (restore_sigregs_gprs_high(regs, frame->gprs_high)) + goto badframe; return regs->gprs[2]; @@ -394,6 +422,8 @@ asmlinkage long sys32_rt_sigreturn(void) if (restore_sigregs32(regs, &frame->uc.uc_mcontext)) goto badframe; + if (restore_sigregs_gprs_high(regs, frame->gprs_high)) + goto badframe; err = __get_user(ss_sp, &frame->uc.uc_stack.ss_sp); st.ss_sp = compat_ptr(ss_sp); @@ -474,6 +504,8 @@ static int setup_frame32(int sig, struct k_sigaction *ka, if (save_sigregs32(regs, &frame->sregs)) goto give_sigsegv; + if (save_sigregs_gprs_high(regs, frame->gprs_high)) + goto give_sigsegv; if (__put_user((unsigned long) &frame->sregs, &frame->sc.sregs)) goto give_sigsegv; @@ -529,13 +561,14 @@ static int setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info, goto give_sigsegv; /* Create the ucontext. */ - err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(UC_EXTENDED, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); err |= __put_user(sas_ss_flags(regs->gprs[15]), &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); err |= save_sigregs32(regs, &frame->uc.uc_mcontext); + err |= save_sigregs_gprs_high(regs, frame->gprs_high); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (err) goto give_sigsegv; diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index a8738676b26c..653c6a178740 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -57,6 +57,7 @@ enum s390_regset { REGSET_GENERAL, REGSET_FP, + REGSET_GENERAL_EXTENDED, }; static void @@ -879,6 +880,67 @@ static int s390_compat_regs_set(struct task_struct *target, return rc; } +static int s390_compat_regs_high_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + compat_ulong_t *gprs_high; + + gprs_high = (compat_ulong_t *) + &task_pt_regs(target)->gprs[pos / sizeof(compat_ulong_t)]; + if (kbuf) { + compat_ulong_t *k = kbuf; + while (count > 0) { + *k++ = *gprs_high; + gprs_high += 2; + count -= sizeof(*k); + } + } else { + compat_ulong_t __user *u = ubuf; + while (count > 0) { + if (__put_user(*gprs_high, u++)) + return -EFAULT; + gprs_high += 2; + count -= sizeof(*u); + } + } + return 0; +} + +static int s390_compat_regs_high_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + compat_ulong_t *gprs_high; + int rc = 0; + + gprs_high = (compat_ulong_t *) + &task_pt_regs(target)->gprs[pos / sizeof(compat_ulong_t)]; + if (kbuf) { + const compat_ulong_t *k = kbuf; + while (count > 0) { + *gprs_high = *k++; + *gprs_high += 2; + count -= sizeof(*k); + } + } else { + const compat_ulong_t __user *u = ubuf; + while (count > 0 && !rc) { + unsigned long word; + rc = __get_user(word, u++); + if (rc) + break; + *gprs_high = word; + *gprs_high += 2; + count -= sizeof(*u); + } + } + + return rc; +} + static const struct user_regset s390_compat_regsets[] = { [REGSET_GENERAL] = { .core_note_type = NT_PRSTATUS, @@ -896,6 +958,14 @@ static const struct user_regset s390_compat_regsets[] = { .get = s390_fpregs_get, .set = s390_fpregs_set, }, + [REGSET_GENERAL_EXTENDED] = { + .core_note_type = NT_PRXSTATUS, + .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), + .size = sizeof(compat_long_t), + .align = sizeof(compat_long_t), + .get = s390_compat_regs_high_get, + .set = s390_compat_regs_high_set, + }, }; static const struct user_regset_view user_s390_compat_view = { diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 9ed13a1ed376..061479ff029f 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -729,7 +729,7 @@ static void __init setup_hwcaps(void) if ((facility_list & (1UL << (31 - 22))) && (facility_list & (1UL << (31 - 30)))) - elf_hwcap |= 1UL << 8; + elf_hwcap |= HWCAP_S390_ETF3EH; /* * Check for additional facilities with store-facility-list-extended. @@ -748,11 +748,20 @@ static void __init setup_hwcaps(void) __stfle(&facility_list_extended, 1) > 0) { if ((facility_list_extended & (1ULL << (63 - 42))) && (facility_list_extended & (1ULL << (63 - 44)))) - elf_hwcap |= 1UL << 6; + elf_hwcap |= HWCAP_S390_DFP; } + /* + * Huge page support HWCAP_S390_HPAGE is bit 7. + */ if (MACHINE_HAS_HPAGE) - elf_hwcap |= 1UL << 7; + elf_hwcap |= HWCAP_S390_HPAGE; + + /* + * 64-bit register support for 31-bit processes + * HWCAP_S390_HIGH_GPRS is bit 9. + */ + elf_hwcap |= HWCAP_S390_HIGH_GPRS; switch (S390_lowcore.cpu_id.machine) { case 0x9672: diff --git a/include/linux/elf.h b/include/linux/elf.h index 45a937be6d38..90a4ed0ea0e5 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -361,6 +361,7 @@ typedef struct elf64_shdr { #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ #define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ +#define NT_PRXSTATUS 0x300 /* s390 upper register halves */ /* Note header in a PT_NOTE section */ -- cgit v1.2.3 From af9d2ff9afaae8040dbf09238b2579f92c93579e Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Tue, 6 Oct 2009 10:34:14 +0200 Subject: [S390] Add EX_TABLE for addressing exception in usercopy functions. This patch adds an EX_TABLE entry to mvc{p|s|os} usercopy functions that may be called with KERNEL_DS. In combination with collaborative memory management, kernel pages marked as unused may trigger an adressing exception in the usercopy functions. This fixes an unhandled addressing exception bug where strncpy_from_user() is used with len > strnlen and KERNEL_DS, crossing a page boundary to an unused page. Signed-off-by: Gerald Schaefer Signed-off-by: Martin Schwidefsky --- arch/s390/lib/uaccess_mvcos.c | 12 ++++++------ arch/s390/lib/uaccess_std.c | 14 ++++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/arch/s390/lib/uaccess_mvcos.c b/arch/s390/lib/uaccess_mvcos.c index 3f15aaf54855..58da3f461214 100644 --- a/arch/s390/lib/uaccess_mvcos.c +++ b/arch/s390/lib/uaccess_mvcos.c @@ -36,7 +36,7 @@ static size_t copy_from_user_mvcos(size_t size, const void __user *ptr, void *x) tmp1 = -4096UL; asm volatile( "0: .insn ss,0xc80000000000,0(%0,%2),0(%1),0\n" - " jz 7f\n" + "9: jz 7f\n" "1:"ALR" %0,%3\n" " "SLR" %1,%3\n" " "SLR" %2,%3\n" @@ -47,7 +47,7 @@ static size_t copy_from_user_mvcos(size_t size, const void __user *ptr, void *x) " "CLR" %0,%4\n" /* copy crosses next page boundary? */ " jnh 4f\n" "3: .insn ss,0xc80000000000,0(%4,%2),0(%1),0\n" - " "SLR" %0,%4\n" + "10:"SLR" %0,%4\n" " "ALR" %2,%4\n" "4:"LHI" %4,-1\n" " "ALR" %4,%0\n" /* copy remaining size, subtract 1 */ @@ -61,7 +61,7 @@ static size_t copy_from_user_mvcos(size_t size, const void __user *ptr, void *x) " j 8f\n" "7:"SLR" %0,%0\n" "8: \n" - EX_TABLE(0b,2b) EX_TABLE(3b,4b) + EX_TABLE(0b,2b) EX_TABLE(3b,4b) EX_TABLE(9b,2b) EX_TABLE(10b,4b) : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2) : "d" (reg0) : "cc", "memory"); return size; @@ -82,7 +82,7 @@ static size_t copy_to_user_mvcos(size_t size, void __user *ptr, const void *x) tmp1 = -4096UL; asm volatile( "0: .insn ss,0xc80000000000,0(%0,%1),0(%2),0\n" - " jz 4f\n" + "6: jz 4f\n" "1:"ALR" %0,%3\n" " "SLR" %1,%3\n" " "SLR" %2,%3\n" @@ -93,11 +93,11 @@ static size_t copy_to_user_mvcos(size_t size, void __user *ptr, const void *x) " "CLR" %0,%4\n" /* copy crosses next page boundary? */ " jnh 5f\n" "3: .insn ss,0xc80000000000,0(%4,%1),0(%2),0\n" - " "SLR" %0,%4\n" + "7:"SLR" %0,%4\n" " j 5f\n" "4:"SLR" %0,%0\n" "5: \n" - EX_TABLE(0b,2b) EX_TABLE(3b,5b) + EX_TABLE(0b,2b) EX_TABLE(3b,5b) EX_TABLE(6b,2b) EX_TABLE(7b,5b) : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2) : "d" (reg0) : "cc", "memory"); return size; diff --git a/arch/s390/lib/uaccess_std.c b/arch/s390/lib/uaccess_std.c index d2ffbadb51a7..07deaeee14c8 100644 --- a/arch/s390/lib/uaccess_std.c +++ b/arch/s390/lib/uaccess_std.c @@ -36,12 +36,12 @@ size_t copy_from_user_std(size_t size, const void __user *ptr, void *x) tmp1 = -256UL; asm volatile( "0: mvcp 0(%0,%2),0(%1),%3\n" - " jz 8f\n" + "10:jz 8f\n" "1:"ALR" %0,%3\n" " la %1,256(%1)\n" " la %2,256(%2)\n" "2: mvcp 0(%0,%2),0(%1),%3\n" - " jnz 1b\n" + "11:jnz 1b\n" " j 8f\n" "3: la %4,255(%1)\n" /* %4 = ptr + 255 */ " "LHI" %3,-4096\n" @@ -50,7 +50,7 @@ size_t copy_from_user_std(size_t size, const void __user *ptr, void *x) " "CLR" %0,%4\n" /* copy crosses next page boundary? */ " jnh 5f\n" "4: mvcp 0(%4,%2),0(%1),%3\n" - " "SLR" %0,%4\n" + "12:"SLR" %0,%4\n" " "ALR" %2,%4\n" "5:"LHI" %4,-1\n" " "ALR" %4,%0\n" /* copy remaining size, subtract 1 */ @@ -65,6 +65,7 @@ size_t copy_from_user_std(size_t size, const void __user *ptr, void *x) "8:"SLR" %0,%0\n" "9: \n" EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,5b) + EX_TABLE(10b,3b) EX_TABLE(11b,3b) EX_TABLE(12b,5b) : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2) : : "cc", "memory"); return size; @@ -85,12 +86,12 @@ size_t copy_to_user_std(size_t size, void __user *ptr, const void *x) tmp1 = -256UL; asm volatile( "0: mvcs 0(%0,%1),0(%2),%3\n" - " jz 5f\n" + "7: jz 5f\n" "1:"ALR" %0,%3\n" " la %1,256(%1)\n" " la %2,256(%2)\n" "2: mvcs 0(%0,%1),0(%2),%3\n" - " jnz 1b\n" + "8: jnz 1b\n" " j 5f\n" "3: la %4,255(%1)\n" /* %4 = ptr + 255 */ " "LHI" %3,-4096\n" @@ -99,11 +100,12 @@ size_t copy_to_user_std(size_t size, void __user *ptr, const void *x) " "CLR" %0,%4\n" /* copy crosses next page boundary? */ " jnh 6f\n" "4: mvcs 0(%4,%1),0(%2),%3\n" - " "SLR" %0,%4\n" + "9:"SLR" %0,%4\n" " j 6f\n" "5:"SLR" %0,%0\n" "6: \n" EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b) + EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b) : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2) : : "cc", "memory"); return size; -- cgit v1.2.3 From 6fca97a958bc3c67566aa91eafc6a5be2e66d6b3 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Tue, 6 Oct 2009 10:34:15 +0200 Subject: [S390] dasd: fix race condition in resume code There is a race while re-reading the device characteristics. After cleaning the memory area a cqr is build which reads the device characteristics. This may take a rather long time and the device characteristics structure is zero during this. Now it could be possible that the block tasklet starts working and a new cqr will be build. The build_cp command refers to the device characteristics structure and this may lead into a divide by zero exception. Fix this by re-reading the device characteristics into a temporary structur and copy the data to the original structure. Also take the ccwdev_lock. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 5 +++-- drivers/s390/block/dasd_eckd.c | 9 +++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index dad0449475b6..53b8c255360a 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -2508,8 +2508,6 @@ int dasd_generic_restore_device(struct ccw_device *cdev) device->stopped &= ~DASD_UNRESUMED_PM; dasd_schedule_device_bh(device); - if (device->block) - dasd_schedule_block_bh(device->block); if (device->discipline->restore) rc = device->discipline->restore(device); @@ -2520,6 +2518,9 @@ int dasd_generic_restore_device(struct ccw_device *cdev) */ device->stopped |= DASD_UNRESUMED_PM; + if (device->block) + dasd_schedule_block_bh(device->block); + dasd_put_device(device); return 0; } diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index ab3521755588..0be7c15f45c5 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2338,6 +2338,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, /* Calculate number of blocks/records per track. */ blksize = block->bp_block; blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); + if (blk_per_trk == 0) + return ERR_PTR(-EINVAL); /* Calculate record id of first and last block. */ first_rec = first_trk = blk_rq_pos(req) >> block->s2b_shift; first_offs = sector_div(first_trk, blk_per_trk); @@ -3211,6 +3213,7 @@ int dasd_eckd_pm_freeze(struct dasd_device *device) int dasd_eckd_restore_device(struct dasd_device *device) { struct dasd_eckd_private *private; + struct dasd_eckd_characteristics temp_rdc_data; int is_known, rc; struct dasd_uid temp_uid; @@ -3245,15 +3248,17 @@ int dasd_eckd_restore_device(struct dasd_device *device) dasd_eckd_read_features(device); /* Read Device Characteristics */ - memset(&private->rdc_data, 0, sizeof(private->rdc_data)); rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC, - &private->rdc_data, 64); + &temp_rdc_data, 64); if (rc) { DBF_EVENT(DBF_WARNING, "Read device characteristics failed, rc=%d for " "device: %s", rc, dev_name(&device->cdev->dev)); goto out_err; } + spin_lock(get_ccwdev_lock(device->cdev)); + memcpy(&private->rdc_data, &temp_rdc_data, sizeof(temp_rdc_data)); + spin_unlock(get_ccwdev_lock(device->cdev)); /* add device to alias management */ dasd_alias_add_device(device); -- cgit v1.2.3 From 1cdda9b81ac0e6ee986f034fa02f221679e1c11a Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Tue, 6 Oct 2009 10:04:28 -0400 Subject: Btrfs: fix possible softlockup in the allocator Like the cluster allocating stuff, we can lockup the box with the normal allocation path. This happens when we 1) Start to cache a block group that is severely fragmented, but has a decent amount of free space. 2) Start to commit a transaction 3) Have the commit try and empty out some of the delalloc inodes with extents that are relatively large. The inodes will not be able to make the allocations because they will ask for allocations larger than a contiguous area in the free space cache. So we will wait for more progress to be made on the block group, but since we're in a commit the caching kthread won't make any more progress and it already has enough free space that wait_block_group_cache_progress will just return. So, if we wait and fail to make the allocation the next time around, just loop and go to the next block group. This keeps us from getting stuck in a softlockup. Thanks, Signed-off-by: Josef Bacik Signed-off-by: Chris Mason --- fs/btrfs/extent-tree.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index d119c0388af1..2f82fabd7011 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4028,6 +4028,7 @@ static noinline int find_free_extent(struct btrfs_trans_handle *trans, int loop = 0; bool found_uncached_bg = false; bool failed_cluster_refill = false; + bool failed_alloc = false; WARN_ON(num_bytes < root->sectorsize); btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY); @@ -4232,14 +4233,23 @@ refill_cluster: offset = btrfs_find_space_for_alloc(block_group, search_start, num_bytes, empty_size); - if (!offset && (cached || (!cached && - loop == LOOP_CACHING_NOWAIT))) { - goto loop; - } else if (!offset && (!cached && - loop > LOOP_CACHING_NOWAIT)) { + /* + * If we didn't find a chunk, and we haven't failed on this + * block group before, and this block group is in the middle of + * caching and we are ok with waiting, then go ahead and wait + * for progress to be made, and set failed_alloc to true. + * + * If failed_alloc is true then we've already waited on this + * block group once and should move on to the next block group. + */ + if (!offset && !failed_alloc && !cached && + loop > LOOP_CACHING_NOWAIT) { wait_block_group_cache_progress(block_group, - num_bytes + empty_size); + num_bytes + empty_size); + failed_alloc = true; goto have_block_group; + } else if (!offset) { + goto loop; } checks: search_start = stripe_align(root, offset); @@ -4287,6 +4297,7 @@ checks: break; loop: failed_cluster_refill = false; + failed_alloc = false; btrfs_put_block_group(block_group); } up_read(&space_info->groups_sem); -- cgit v1.2.3 From e3fb20f9c8783d6e27cf84389a9606e410733eef Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 5 Oct 2009 16:47:34 -0600 Subject: PCI: PCIe portdrv: remove "-driver" from driver name No need to include "-driver" in the driver name. Signed-off-by: Bjorn Helgaas CC: Tom Long Nguyen Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_pci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 6df5c984a791..f635e476d632 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -30,7 +30,6 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* global data */ -static const char device_name[] = "pcieport-driver"; static int pcie_portdrv_restore_config(struct pci_dev *dev) { @@ -262,7 +261,7 @@ static struct pci_error_handlers pcie_portdrv_err_handler = { }; static struct pci_driver pcie_portdriver = { - .name = (char *)device_name, + .name = "pcieport", .id_table = &port_pci_ids[0], .probe = pcie_portdrv_probe, -- cgit v1.2.3 From b812cca4e2efe9a05de20ccf3f8587e7ac6e12fa Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 5 Oct 2009 16:38:13 -0600 Subject: PCI: remove pci_assign_resource_fixed() Adrian commented out this function in 2baad5f96b49, but I don't think it's even worth cluttering the file with the unused code. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/setup-res.c | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 706f82d8111f..c54526b206b5 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -205,43 +205,6 @@ int pci_assign_resource(struct pci_dev *dev, int resno) return ret; } -#if 0 -int pci_assign_resource_fixed(struct pci_dev *dev, int resno) -{ - struct pci_bus *bus = dev->bus; - struct resource *res = dev->resource + resno; - unsigned int type_mask; - int i, ret = -EBUSY; - - type_mask = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; - - for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { - struct resource *r = bus->resource[i]; - if (!r) - continue; - - /* type_mask must match */ - if ((res->flags ^ r->flags) & type_mask) - continue; - - ret = request_resource(r, res); - - if (ret == 0) - break; - } - - if (ret) { - dev_err(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", - resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res); - } else if (resno < PCI_BRIDGE_RESOURCES) { - pci_update_resource(dev, resno); - } - - return ret; -} -EXPORT_SYMBOL_GPL(pci_assign_resource_fixed); -#endif - /* Sort resources by alignment */ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) { -- cgit v1.2.3 From e13cdbd71fe12c4e191b737c4a3dbfdb4b2de03b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 5 Oct 2009 00:48:40 +0200 Subject: PCI PM: Read device power state from register after updating it After attempting to change the power state of a PCI device pci_raw_set_power_state() doesn't check if the value it wrote into the device's PCI_PM_CTRL register has been stored in there, but unconditionally modifies the device's current_state field to reflect the change. This may cause problems to happen if the power state of the device hasn't been changed in fact, because it will make the PCI PM core make a wrong assumption. To prevent such situations from happening modify pci_raw_set_power_state() so that it reads the device's PCI_PM_CTRL register after writing into it and uses the value read from the register to update the device's current_state field. Also make it print a message saying that the device refused to change its power state as requested (returning an error code in such cases would cause suspend regressions to appear on some systems, where device drivers' suspend routines return error codes if pci_set_power_state() fails). Reviewed-by: Alex Chiang Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6edecff0b419..6e1da5aa7bb9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -513,7 +513,11 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) else if (state == PCI_D2 || dev->current_state == PCI_D2) udelay(PCI_PM_D2_DELAY); - dev->current_state = state; + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); + dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); + if (dev->current_state != state && printk_ratelimit()) + dev_info(&dev->dev, "Refused to change power state, " + "currently in D%d\n", dev->current_state); /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning -- cgit v1.2.3 From 1f8db4fc1271a42496778c0ff18153da56f0f844 Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Mon, 5 Oct 2009 19:34:43 +0100 Subject: ARM: 5744/1: ep93xx: add remaining phys offset selections This adds the missing Kconfig options for the first SDRAM bank address on ep93xx boards. Cc: Hubert Feurstein <(address hidden)> Signed-off-by: H Hartley Sweeten <(address hidden)> Acked-by: Ryan Mallon <(address hidden)> Signed-off-by: Russell King --- arch/arm/mach-ep93xx/Kconfig | 22 ++++++++++++++++++++-- arch/arm/mach-ep93xx/Makefile.boot | 9 +++++++++ arch/arm/mach-ep93xx/include/mach/memory.h | 6 ++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-ep93xx/Kconfig b/arch/arm/mach-ep93xx/Kconfig index d7291c682a64..6675c051f62f 100644 --- a/arch/arm/mach-ep93xx/Kconfig +++ b/arch/arm/mach-ep93xx/Kconfig @@ -17,13 +17,31 @@ config EP93XX_SDCE3_SYNC_PHYS_OFFSET bool "0x00000000 - SDCE3/SyncBoot" help Select this option if you want support for EP93xx boards with the - first SDRAM bank at 0x00000000 + first SDRAM bank at 0x00000000. config EP93XX_SDCE0_PHYS_OFFSET bool "0xc0000000 - SDCEO" help Select this option if you want support for EP93xx boards with the - first SDRAM bank at 0xc0000000 + first SDRAM bank at 0xc0000000. + +config EP93XX_SDCE1_PHYS_OFFSET + bool "0xd0000000 - SDCE1" + help + Select this option if you want support for EP93xx boards with the + first SDRAM bank at 0xd0000000. + +config EP93XX_SDCE2_PHYS_OFFSET + bool "0xe0000000 - SDCE2" + help + Select this option if you want support for EP93xx boards with the + first SDRAM bank at 0xe0000000. + +config EP93XX_SDCE3_ASYNC_PHYS_OFFSET + bool "0xf0000000 - SDCE3/AsyncBoot" + help + Select this option if you want support for EP93xx boards with the + first SDRAM bank at 0xf0000000. endchoice diff --git a/arch/arm/mach-ep93xx/Makefile.boot b/arch/arm/mach-ep93xx/Makefile.boot index 27a085a8f12a..0ad33f15c622 100644 --- a/arch/arm/mach-ep93xx/Makefile.boot +++ b/arch/arm/mach-ep93xx/Makefile.boot @@ -3,3 +3,12 @@ params_phys-$(CONFIG_EP93XX_SDCE3_SYNC_PHYS_OFFSET) := 0x00000100 zreladdr-$(CONFIG_EP93XX_SDCE0_PHYS_OFFSET) := 0xc0008000 params_phys-$(CONFIG_EP93XX_SDCE0_PHYS_OFFSET) := 0xc0000100 + + zreladdr-$(CONFIG_EP93XX_SDCE1_PHYS_OFFSET) := 0xd0008000 +params_phys-$(CONFIG_EP93XX_SDCE1_PHYS_OFFSET) := 0xd0000100 + + zreladdr-$(CONFIG_EP93XX_SDCE2_PHYS_OFFSET) := 0xe0008000 +params_phys-$(CONFIG_EP93XX_SDCE2_PHYS_OFFSET) := 0xe0000100 + + zreladdr-$(CONFIG_EP93XX_SDCE3_ASYNC_PHYS_OFFSET) := 0xf0008000 +params_phys-$(CONFIG_EP93XX_SDCE3_ASYNC_PHYS_OFFSET) := 0xf0000100 diff --git a/arch/arm/mach-ep93xx/include/mach/memory.h b/arch/arm/mach-ep93xx/include/mach/memory.h index 925b12ea0990..554064e90307 100644 --- a/arch/arm/mach-ep93xx/include/mach/memory.h +++ b/arch/arm/mach-ep93xx/include/mach/memory.h @@ -9,6 +9,12 @@ #define PHYS_OFFSET UL(0x00000000) #elif defined(CONFIG_EP93XX_SDCE0_PHYS_OFFSET) #define PHYS_OFFSET UL(0xc0000000) +#elif defined(CONFIG_EP93XX_SDCE1_PHYS_OFFSET) +#define PHYS_OFFSET UL(0xd0000000) +#elif defined(CONFIG_EP93XX_SDCE2_PHYS_OFFSET) +#define PHYS_OFFSET UL(0xe0000000) +#elif defined(CONFIG_EP93XX_SDCE3_ASYNC_PHYS_OFFSET) +#define PHYS_OFFSET UL(0xf0000000) #else #error "Kconfig bug: No EP93xx PHYS_OFFSET set" #endif -- cgit v1.2.3 From cc1ad4a69667be885ac6036a315066854ef8c871 Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Mon, 5 Oct 2009 23:15:32 +0100 Subject: ARM: 5745/1: Change ep93xx gpio_irq static inlines to macros From: Ryan Mallon Change the gpio_to_irq and irq_to_gpio static inline functions to macros so that they can be used in variable initialisers. Signed-off-by: Ryan Mallon Acked-by: H Hartley Sweeten Signed-off-by: Russell King --- arch/arm/mach-ep93xx/include/mach/gpio.h | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-ep93xx/include/mach/gpio.h b/arch/arm/mach-ep93xx/include/mach/gpio.h index 0a1498ae899a..c991b149bdf2 100644 --- a/arch/arm/mach-ep93xx/include/mach/gpio.h +++ b/arch/arm/mach-ep93xx/include/mach/gpio.h @@ -114,17 +114,9 @@ extern void ep93xx_gpio_int_debounce(unsigned int irq, int enable); * B0..B7 (7..15) to irq 72..79, and * F0..F7 (16..24) to irq 80..87. */ -static inline int gpio_to_irq(unsigned gpio) -{ - if (gpio <= EP93XX_GPIO_LINE_MAX_IRQ) - return 64 + gpio; - - return -EINVAL; -} - -static inline int irq_to_gpio(unsigned irq) -{ - return irq - gpio_to_irq(0); -} +#define gpio_to_irq(gpio) \ + (((gpio) <= EP93XX_GPIO_LINE_MAX_IRQ) ? (64 + (gpio)) : -EINVAL) + +#define irq_to_gpio(irq) ((irq) - gpio_to_irq(0)) #endif -- cgit v1.2.3 From 32cfb1b16f2b68d2296536811cadfffe26a06c1b Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 6 Oct 2009 17:57:09 +0100 Subject: ARM: 5746/1: Handle possible translation errors in ARMv6/v7 coherent_user_range This is needed because applications using the sys_cacheflush system call can pass a memory range which isn't mapped yet even though the corresponding vma is valid. The patch also adds unwinding annotations for correct backtraces from the coherent_user_range() functions. Signed-off-by: Catalin Marinas Signed-off-by: Russell King --- arch/arm/mm/cache-v6.S | 20 ++++++++++++++++++-- arch/arm/mm/cache-v7.S | 19 +++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S index 8f5c13f4c936..295e25dd6381 100644 --- a/arch/arm/mm/cache-v6.S +++ b/arch/arm/mm/cache-v6.S @@ -12,6 +12,7 @@ #include #include #include +#include #include "proc-macros.S" @@ -121,11 +122,13 @@ ENTRY(v6_coherent_kern_range) * - the Icache does not read data from the write buffer */ ENTRY(v6_coherent_user_range) - + UNWIND(.fnstart ) #ifdef HARVARD_CACHE bic r0, r0, #CACHE_LINE_SIZE - 1 -1: mcr p15, 0, r0, c7, c10, 1 @ clean D line +1: + USER( mcr p15, 0, r0, c7, c10, 1 ) @ clean D line add r0, r0, #CACHE_LINE_SIZE +2: cmp r0, r1 blo 1b #endif @@ -142,6 +145,19 @@ ENTRY(v6_coherent_user_range) #endif mov pc, lr +/* + * Fault handling for the cache operation above. If the virtual address in r0 + * isn't mapped, just try the next page. + */ +9001: + mov r0, r0, lsr #12 + mov r0, r0, lsl #12 + add r0, r0, #4096 + b 2b + UNWIND(.fnend ) +ENDPROC(v6_coherent_user_range) +ENDPROC(v6_coherent_kern_range) + /* * v6_flush_kern_dcache_page(kaddr) * diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index bda0ec31a4e2..e1bd9759617f 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -13,6 +13,7 @@ #include #include #include +#include #include "proc-macros.S" @@ -153,13 +154,16 @@ ENTRY(v7_coherent_kern_range) * - the Icache does not read data from the write buffer */ ENTRY(v7_coherent_user_range) + UNWIND(.fnstart ) dcache_line_size r2, r3 sub r3, r2, #1 bic r0, r0, r3 -1: mcr p15, 0, r0, c7, c11, 1 @ clean D line to the point of unification +1: + USER( mcr p15, 0, r0, c7, c11, 1 ) @ clean D line to the point of unification dsb - mcr p15, 0, r0, c7, c5, 1 @ invalidate I line + USER( mcr p15, 0, r0, c7, c5, 1 ) @ invalidate I line add r0, r0, r2 +2: cmp r0, r1 blo 1b mov r0, #0 @@ -167,6 +171,17 @@ ENTRY(v7_coherent_user_range) dsb isb mov pc, lr + +/* + * Fault handling for the cache operation above. If the virtual address in r0 + * isn't mapped, just try the next page. + */ +9001: + mov r0, r0, lsr #12 + mov r0, r0, lsl #12 + add r0, r0, #4096 + b 2b + UNWIND(.fnend ) ENDPROC(v7_coherent_kern_range) ENDPROC(v7_coherent_user_range) -- cgit v1.2.3 From 3257f43d9296ed7adcc84e48f6ddf5313cf29266 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 6 Oct 2009 17:57:22 +0100 Subject: ARM: 5747/1: Fix the start_pg value in free_memmap() If sparsemem is enabled, the start_pfn passed to the free_memmap() function corresponds to an area of memory not known to the kernel and pfn_to_page returns a wrong value. The (start_pfn - 1), however, is known to the kernel. Signed-off-by: Catalin Marinas Signed-off-by: Russell King --- arch/arm/mm/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 877c492f8e10..40940d7ce4ff 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -483,7 +483,7 @@ free_memmap(int node, unsigned long start_pfn, unsigned long end_pfn) /* * Convert start_pfn/end_pfn to a struct page pointer. */ - start_pg = pfn_to_page(start_pfn); + start_pg = pfn_to_page(start_pfn - 1) + 1; end_pg = pfn_to_page(end_pfn); /* -- cgit v1.2.3 From b623438341b40ee78bdab7ab509889de519af2e7 Mon Sep 17 00:00:00 2001 From: Leo Chen Date: Tue, 6 Oct 2009 19:30:40 +0100 Subject: ARM: 5748/1: bcmring: fix build warning messages Fix the warning messages during kernel build for bcmring. Signed-off-by: Leo Hao Chen Signed-off-by: Russell King --- arch/arm/mach-bcmring/core.c | 4 ++-- arch/arm/mach-bcmring/include/mach/system.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-bcmring/core.c b/arch/arm/mach-bcmring/core.c index 4b4f69251b31..e590bbe0a7b4 100644 --- a/arch/arm/mach-bcmring/core.c +++ b/arch/arm/mach-bcmring/core.c @@ -271,12 +271,12 @@ static struct irqaction bcmring_timer_irq = { .handler = bcmring_timer_interrupt, }; -static cycle_t bcmring_get_cycles_timer1(void) +static cycle_t bcmring_get_cycles_timer1(struct clocksource *cs) { return ~readl(TIMER1_VA_BASE + TIMER_VALUE); } -static cycle_t bcmring_get_cycles_timer3(void) +static cycle_t bcmring_get_cycles_timer3(struct clocksource *cs) { return ~readl(TIMER3_VA_BASE + TIMER_VALUE); } diff --git a/arch/arm/mach-bcmring/include/mach/system.h b/arch/arm/mach-bcmring/include/mach/system.h index cdbf93c694a6..38b37060d426 100644 --- a/arch/arm/mach-bcmring/include/mach/system.h +++ b/arch/arm/mach-bcmring/include/mach/system.h @@ -29,7 +29,7 @@ static inline void arch_idle(void) cpu_do_idle(); } -static inline void arch_reset(char mode, char *cmd) +static inline void arch_reset(char mode, const char *cmd) { printk("arch_reset:%c %x\n", mode, bcmring_arch_warm_reboot); -- cgit v1.2.3 From 4918a01393a38db4346ae88f38c1fd8324bb5e63 Mon Sep 17 00:00:00 2001 From: Dmitry Artamonow Date: Wed, 7 Oct 2009 10:48:26 +0100 Subject: ARM: 5752/1: SA1100: fix building of h3100 Both iPaqs h3100 and h3600 currently share the same source file - h3600.c But Makefile builds it only if CONFIG_SA1100_H3600 selected, so selecting just CONFIG_SA1100_H3100 results in "no machine record defined" message and aborted compilation. Fix it. Signed-off-by: Dmitry Artamonow Acked-by: Kristoffer Ericson Signed-off-by: Russell King --- arch/arm/mach-sa1100/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile index 8a5546e6d547..bb7b8198d0c4 100644 --- a/arch/arm/mach-sa1100/Makefile +++ b/arch/arm/mach-sa1100/Makefile @@ -25,6 +25,7 @@ led-$(CONFIG_SA1100_CERF) += leds-cerf.o obj-$(CONFIG_SA1100_COLLIE) += collie.o +obj-$(CONFIG_SA1100_H3100) += h3600.o obj-$(CONFIG_SA1100_H3600) += h3600.o obj-$(CONFIG_SA1100_HACKKIT) += hackkit.o -- cgit v1.2.3 From 941500954470e04679ff6e3cff0f82d8dbd2b6c3 Mon Sep 17 00:00:00 2001 From: Hubert Feurstein Date: Wed, 7 Oct 2009 08:36:07 +0100 Subject: ARM: 5749/1: ep93xx/micro9: Update maintainer Update Contec Micro9 maintainer and add entry in MAINTAINERS Cc: Ryan Mallon Requires: 5744/1 Signed-off-by: Hubert Feurstein Acked-by: H Hartley Sweeten Acked-by: Manfred Gruber Signed-off-by: Russell King --- MAINTAINERS | 5 +++++ arch/arm/mach-ep93xx/micro9.c | 10 ++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 09a2028bab7f..1401a85b93ae 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -577,6 +577,11 @@ M: Mike Rapoport L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained +ARM/CONTEC MICRO9 MACHINE SUPPORT +M: Hubert Feurstein +S: Maintained +F: arch/arm/mach-ep93xx/micro9.c + ARM/CORGI MACHINE SUPPORT M: Richard Purdie S: Maintained diff --git a/arch/arm/mach-ep93xx/micro9.c b/arch/arm/mach-ep93xx/micro9.c index 0a313e82fb74..72e7a7dfd7bf 100644 --- a/arch/arm/mach-ep93xx/micro9.c +++ b/arch/arm/mach-ep93xx/micro9.c @@ -2,7 +2,9 @@ * linux/arch/arm/mach-ep93xx/micro9.c * * Copyright (C) 2006 Contec Steuerungstechnik & Automation GmbH - * Manfred Gruber + * Manfred Gruber + * Copyright (C) 2009 Contec Steuerungstechnik & Automation GmbH + * Hubert Feurstein * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -66,7 +68,7 @@ static void __init micro9h_init_machine(void) } MACHINE_START(MICRO9, "Contec Hypercontrol Micro9-H") - /* Maintainer: Manfred Gruber */ + /* Maintainer: Hubert Feurstein */ .phys_io = EP93XX_APB_PHYS_BASE, .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc, .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, @@ -88,7 +90,7 @@ static void __init micro9m_init_machine(void) } MACHINE_START(MICRO9M, "Contec Hypercontrol Micro9-M") - /* Maintainer: Manfred Gruber */ + /* Maintainer: Hubert Feurstein */ .phys_io = EP93XX_APB_PHYS_BASE, .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc, .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, @@ -110,7 +112,7 @@ static void __init micro9l_init_machine(void) } MACHINE_START(MICRO9L, "Contec Hypercontrol Micro9-L") - /* Maintainer: Manfred Gruber */ + /* Maintainer: Hubert Feurstein */ .phys_io = EP93XX_APB_PHYS_BASE, .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc, .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, -- cgit v1.2.3 From 14636005fff800d4131dfe89927207a564efcc17 Mon Sep 17 00:00:00 2001 From: Hubert Feurstein Date: Wed, 7 Oct 2009 08:39:09 +0100 Subject: ARM: 5750/1: ep93xx/micro9: Update platform code Update Contec Micro9 platform code Cc: Ryan Mallon Requires: 5749/1 Signed-off-by: Hubert Feurstein Acked-by: H Hartley Sweeten Signed-off-by: Russell King --- arch/arm/mach-ep93xx/Kconfig | 14 +-- arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 11 +++ arch/arm/mach-ep93xx/micro9.c | 108 +++++++++++++----------- 3 files changed, 75 insertions(+), 58 deletions(-) diff --git a/arch/arm/mach-ep93xx/Kconfig b/arch/arm/mach-ep93xx/Kconfig index 6675c051f62f..4ad0380ff806 100644 --- a/arch/arm/mach-ep93xx/Kconfig +++ b/arch/arm/mach-ep93xx/Kconfig @@ -130,28 +130,28 @@ config MACH_MICRO9 bool config MACH_MICRO9H - bool "Support Contec Hypercontrol Micro9-H" + bool "Support Contec Micro9-High" depends on EP93XX_SDCE3_SYNC_PHYS_OFFSET select MACH_MICRO9 help Say 'Y' here if you want your kernel to support the - Contec Hypercontrol Micro9-H board. + Contec Micro9-High board. config MACH_MICRO9M - bool "Support Contec Hypercontrol Micro9-M" - depends on EP93XX_SDCE3_SYNC_PHYS_OFFSET + bool "Support Contec Micro9-Mid" + depends on EP93XX_SDCE3_ASYNC_PHYS_OFFSET select MACH_MICRO9 help Say 'Y' here if you want your kernel to support the - Contec Hypercontrol Micro9-M board. + Contec Micro9-Mid board. config MACH_MICRO9L - bool "Support Contec Hypercontrol Micro9-L" + bool "Support Contec Micro9-Lite" depends on EP93XX_SDCE3_SYNC_PHYS_OFFSET select MACH_MICRO9 help Say 'Y' here if you want your kernel to support the - Contec Hypercontrol Micro9-L board. + Contec Micro9-Lite board. config MACH_TS72XX bool "Support Technologic Systems TS-72xx SBC" diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h index 0fbf87b16338..587756dbd6f6 100644 --- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h @@ -218,6 +218,17 @@ #define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV (1<<16) #define EP93XX_SYSCON_KEYTCHCLKDIV_KEN (1<<15) #define EP93XX_SYSCON_KEYTCHCLKDIV_KDIV (1<<0) +#define EP93XX_SYSCON_SYSCFG EP93XX_SYSCON_REG(0x9c) +#define EP93XX_SYSCON_SYSCFG_REV_MASK (0xf0000000) +#define EP93XX_SYSCON_SYSCFG_REV_SHIFT (28) +#define EP93XX_SYSCON_SYSCFG_SBOOT (1<<8) +#define EP93XX_SYSCON_SYSCFG_LCSN7 (1<<7) +#define EP93XX_SYSCON_SYSCFG_LCSN6 (1<<6) +#define EP93XX_SYSCON_SYSCFG_LASDO (1<<5) +#define EP93XX_SYSCON_SYSCFG_LEEDA (1<<4) +#define EP93XX_SYSCON_SYSCFG_LEECLK (1<<3) +#define EP93XX_SYSCON_SYSCFG_LCSN2 (1<<1) +#define EP93XX_SYSCON_SYSCFG_LCSN1 (1<<0) #define EP93XX_SYSCON_SWLOCK EP93XX_SYSCON_REG(0xc0) #define EP93XX_WATCHDOG_BASE EP93XX_APB_IOMEM(0x00140000) diff --git a/arch/arm/mach-ep93xx/micro9.c b/arch/arm/mach-ep93xx/micro9.c index 72e7a7dfd7bf..49fbee5adde2 100644 --- a/arch/arm/mach-ep93xx/micro9.c +++ b/arch/arm/mach-ep93xx/micro9.c @@ -22,52 +22,77 @@ #include -static struct ep93xx_eth_data micro9_eth_data = { - .phy_id = 0x1f, -}; - -static void __init micro9_init(void) -{ - ep93xx_register_eth(µ9_eth_data, 1); -} - -/* - * Micro9-H - */ -#ifdef CONFIG_MACH_MICRO9H -static struct physmap_flash_data micro9h_flash_data = { - .width = 4, -}; +/************************************************************************* + * Micro9 NOR Flash + * + * Micro9-High has up to 64MB of 32-bit flash on CS1 + * Micro9-Mid has up to 64MB of either 32-bit or 16-bit flash on CS1 + * Micro9-Lite uses a seperate MTD map driver for flash support + *************************************************************************/ +static struct physmap_flash_data micro9_flash_data; -static struct resource micro9h_flash_resource = { +static struct resource micro9_flash_resource = { .start = EP93XX_CS1_PHYS_BASE, .end = EP93XX_CS1_PHYS_BASE + SZ_64M - 1, .flags = IORESOURCE_MEM, }; -static struct platform_device micro9h_flash = { +static struct platform_device micro9_flash = { .name = "physmap-flash", .id = 0, .dev = { - .platform_data = µ9h_flash_data, + .platform_data = µ9_flash_data, }, .num_resources = 1, - .resource = µ9h_flash_resource, + .resource = µ9_flash_resource, }; -static void __init micro9h_init(void) +static void __init __micro9_register_flash(unsigned int width) +{ + micro9_flash_data.width = width; + + platform_device_register(µ9_flash); +} + +static unsigned int __init micro9_detect_bootwidth(void) { - platform_device_register(µ9h_flash); + u32 v; + + /* Detect the bus width of the external flash memory */ + v = __raw_readl(EP93XX_SYSCON_SYSCFG); + if (v & EP93XX_SYSCON_SYSCFG_LCSN7) + return 4; /* 32-bit */ + else + return 2; /* 16-bit */ } -static void __init micro9h_init_machine(void) +static void __init micro9_register_flash(void) +{ + if (machine_is_micro9()) + __micro9_register_flash(4); + else if (machine_is_micro9m()) + __micro9_register_flash(micro9_detect_bootwidth()); +} + + +/************************************************************************* + * Micro9 Ethernet + *************************************************************************/ +static struct ep93xx_eth_data micro9_eth_data = { + .phy_id = 0x1f, +}; + + +static void __init micro9_init_machine(void) { ep93xx_init_devices(); - micro9_init(); - micro9h_init(); + ep93xx_register_eth(µ9_eth_data, 1); + micro9_register_flash(); } -MACHINE_START(MICRO9, "Contec Hypercontrol Micro9-H") + +#ifdef CONFIG_MACH_MICRO9H +MACHINE_START(MICRO9, "Contec Micro9-High") /* Maintainer: Hubert Feurstein */ .phys_io = EP93XX_APB_PHYS_BASE, .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc, @@ -75,43 +100,25 @@ MACHINE_START(MICRO9, "Contec Hypercontrol Micro9-H") .map_io = ep93xx_map_io, .init_irq = ep93xx_init_irq, .timer = &ep93xx_timer, - .init_machine = micro9h_init_machine, + .init_machine = micro9_init_machine, MACHINE_END #endif -/* - * Micro9-M - */ #ifdef CONFIG_MACH_MICRO9M -static void __init micro9m_init_machine(void) -{ - ep93xx_init_devices(); - micro9_init(); -} - -MACHINE_START(MICRO9M, "Contec Hypercontrol Micro9-M") +MACHINE_START(MICRO9M, "Contec Micro9-Mid") /* Maintainer: Hubert Feurstein */ .phys_io = EP93XX_APB_PHYS_BASE, .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc, - .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100, + .boot_params = EP93XX_SDCE3_PHYS_BASE_ASYNC + 0x100, .map_io = ep93xx_map_io, .init_irq = ep93xx_init_irq, .timer = &ep93xx_timer, - .init_machine = micro9m_init_machine, + .init_machine = micro9_init_machine, MACHINE_END #endif -/* - * Micro9-L - */ #ifdef CONFIG_MACH_MICRO9L -static void __init micro9l_init_machine(void) -{ - ep93xx_init_devices(); - micro9_init(); -} - -MACHINE_START(MICRO9L, "Contec Hypercontrol Micro9-L") +MACHINE_START(MICRO9L, "Contec Micro9-Lite") /* Maintainer: Hubert Feurstein */ .phys_io = EP93XX_APB_PHYS_BASE, .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc, @@ -119,7 +126,6 @@ MACHINE_START(MICRO9L, "Contec Hypercontrol Micro9-L") .map_io = ep93xx_map_io, .init_irq = ep93xx_init_irq, .timer = &ep93xx_timer, - .init_machine = micro9l_init_machine, + .init_machine = micro9_init_machine, MACHINE_END #endif - -- cgit v1.2.3 From 7232344d49b88e8787f1842936867cd224da47e5 Mon Sep 17 00:00:00 2001 From: Hubert Feurstein Date: Wed, 7 Oct 2009 08:41:01 +0100 Subject: ARM: 5751/1: ep93xx/micro9: Add Micro9-Slim Add Contec Micro9-Slim support Cc: Ryan Mallon Requires: 5750/1 Signed-off-by: Hubert Feurstein Acked-by: H Hartley Sweeten Signed-off-by: Russell King --- arch/arm/mach-ep93xx/Kconfig | 8 ++++++++ arch/arm/mach-ep93xx/micro9.c | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ep93xx/Kconfig b/arch/arm/mach-ep93xx/Kconfig index 4ad0380ff806..9167c3d2a5ed 100644 --- a/arch/arm/mach-ep93xx/Kconfig +++ b/arch/arm/mach-ep93xx/Kconfig @@ -153,6 +153,14 @@ config MACH_MICRO9L Say 'Y' here if you want your kernel to support the Contec Micro9-Lite board. +config MACH_MICRO9S + bool "Support Contec Micro9-Slim" + depends on EP93XX_SDCE3_ASYNC_PHYS_OFFSET + select MACH_MICRO9 + help + Say 'Y' here if you want your kernel to support the + Contec Micro9-Slim board. + config MACH_TS72XX bool "Support Technologic Systems TS-72xx SBC" depends on EP93XX_SDCE3_SYNC_PHYS_OFFSET diff --git a/arch/arm/mach-ep93xx/micro9.c b/arch/arm/mach-ep93xx/micro9.c index 49fbee5adde2..d83b80478b09 100644 --- a/arch/arm/mach-ep93xx/micro9.c +++ b/arch/arm/mach-ep93xx/micro9.c @@ -28,6 +28,7 @@ * Micro9-High has up to 64MB of 32-bit flash on CS1 * Micro9-Mid has up to 64MB of either 32-bit or 16-bit flash on CS1 * Micro9-Lite uses a seperate MTD map driver for flash support + * Micro9-Slim has up to 64MB of either 32-bit or 16-bit flash on CS1 *************************************************************************/ static struct physmap_flash_data micro9_flash_data; @@ -70,7 +71,7 @@ static void __init micro9_register_flash(void) { if (machine_is_micro9()) __micro9_register_flash(4); - else if (machine_is_micro9m()) + else if (machine_is_micro9m() || machine_is_micro9s()) __micro9_register_flash(micro9_detect_bootwidth()); } @@ -129,3 +130,16 @@ MACHINE_START(MICRO9L, "Contec Micro9-Lite") .init_machine = micro9_init_machine, MACHINE_END #endif + +#ifdef CONFIG_MACH_MICRO9S +MACHINE_START(MICRO9S, "Contec Micro9-Slim") + /* Maintainer: Hubert Feurstein */ + .phys_io = EP93XX_APB_PHYS_BASE, + .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc, + .boot_params = EP93XX_SDCE3_PHYS_BASE_ASYNC + 0x100, + .map_io = ep93xx_map_io, + .init_irq = ep93xx_init_irq, + .timer = &ep93xx_timer, + .init_machine = micro9_init_machine, +MACHINE_END +#endif -- cgit v1.2.3 From 591006f830bcc8edf2841750d7543c5c5a672f89 Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Fri, 25 Sep 2009 17:54:31 +0100 Subject: ARM: 5729/1: ep93xx: define EP93XX_*_PHYS_BASE with macros Change the #define's for the EP93XX_*_PHYS_BASE addresses to use macros for easier readability. Signed-off-by: H Hartley Sweeten Acked-by: Ryan Mallon Signed-off-by: Russell King --- arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h index 587756dbd6f6..b07338c0a01f 100644 --- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h @@ -52,25 +52,27 @@ #define EP93XX_AHB_VIRT_BASE 0xfef00000 #define EP93XX_AHB_SIZE 0x00100000 +#define EP93XX_AHB_PHYS(x) (EP93XX_AHB_PHYS_BASE + (x)) #define EP93XX_AHB_IOMEM(x) IOMEM(EP93XX_AHB_VIRT_BASE + (x)) #define EP93XX_APB_PHYS_BASE 0x80800000 #define EP93XX_APB_VIRT_BASE 0xfed00000 #define EP93XX_APB_SIZE 0x00200000 +#define EP93XX_APB_PHYS(x) (EP93XX_APB_PHYS_BASE + (x)) #define EP93XX_APB_IOMEM(x) IOMEM(EP93XX_APB_VIRT_BASE + (x)) /* AHB peripherals */ #define EP93XX_DMA_BASE EP93XX_AHB_IOMEM(0x00000000) -#define EP93XX_ETHERNET_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x00010000) +#define EP93XX_ETHERNET_PHYS_BASE EP93XX_AHB_PHYS(0x00010000) #define EP93XX_ETHERNET_BASE EP93XX_AHB_IOMEM(0x00010000) -#define EP93XX_USB_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x00020000) +#define EP93XX_USB_PHYS_BASE EP93XX_AHB_PHYS(0x00020000) #define EP93XX_USB_BASE EP93XX_AHB_IOMEM(0x00020000) -#define EP93XX_RASTER_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x00030000) +#define EP93XX_RASTER_PHYS_BASE EP93XX_AHB_PHYS(0x00030000) #define EP93XX_RASTER_BASE EP93XX_AHB_IOMEM(0x00030000) #define EP93XX_GRAPHICS_ACCEL_BASE EP93XX_AHB_IOMEM(0x00040000) @@ -134,13 +136,13 @@ #define EP93XX_IRDA_BASE EP93XX_APB_IOMEM(0x000b0000) -#define EP93XX_UART1_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x000c0000) +#define EP93XX_UART1_PHYS_BASE EP93XX_APB_PHYS(0x000c0000) #define EP93XX_UART1_BASE EP93XX_APB_IOMEM(0x000c0000) -#define EP93XX_UART2_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x000d0000) +#define EP93XX_UART2_PHYS_BASE EP93XX_APB_PHYS(0x000d0000) #define EP93XX_UART2_BASE EP93XX_APB_IOMEM(0x000d0000) -#define EP93XX_UART3_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x000e0000) +#define EP93XX_UART3_PHYS_BASE EP93XX_APB_PHYS(0x000e0000) #define EP93XX_UART3_BASE EP93XX_APB_IOMEM(0x000e0000) #define EP93XX_KEY_MATRIX_BASE EP93XX_APB_IOMEM(0x000f0000) @@ -148,10 +150,10 @@ #define EP93XX_ADC_BASE EP93XX_APB_IOMEM(0x00100000) #define EP93XX_TOUCHSCREEN_BASE EP93XX_APB_IOMEM(0x00100000) -#define EP93XX_PWM_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x00110000) +#define EP93XX_PWM_PHYS_BASE EP93XX_APB_PHYS(0x00110000) #define EP93XX_PWM_BASE EP93XX_APB_IOMEM(0x00110000) -#define EP93XX_RTC_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x00120000) +#define EP93XX_RTC_PHYS_BASE EP93XX_APB_PHYS(0x00120000) #define EP93XX_RTC_BASE EP93XX_APB_IOMEM(0x00120000) #define EP93XX_SYSCON_BASE EP93XX_APB_IOMEM(0x00130000) -- cgit v1.2.3 From 1f56f4a2b4d12c1c348cab23024024396ec7cddc Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Tue, 6 Oct 2009 09:19:45 -0500 Subject: PCI quirk: TI XIO200a erroneously reports support for fast b2b transfers This quirk will disable fast back to back transfer on the secondary bus segment of the TI Bridge. Signed-off-by: Gabe Black Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 19 +++++++++++++++++++ include/linux/pci_ids.h | 1 + 2 files changed, 20 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 6099facecd79..efa6534a6593 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -670,6 +670,25 @@ static void __devinit quirk_vt8235_acpi(struct pci_dev *dev) } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, quirk_vt8235_acpi); +/* + * TI XIO2000a PCIe-PCI Bridge erroneously reports it supports fast back-to-back: + * Disable fast back-to-back on the secondary bus segment + */ +static void __devinit quirk_xio2000a(struct pci_dev *dev) +{ + struct pci_dev *pdev; + u16 command; + + dev_warn(&dev->dev, "TI XIO2000a quirk detected; " + "secondary bus fast back-to-back transfers disabled\n"); + list_for_each_entry(pdev, &dev->subordinate->devices, bus_list) { + pci_read_config_word(pdev, PCI_COMMAND, &command); + if (command & PCI_COMMAND_FAST_BACK) + pci_write_config_word(pdev, PCI_COMMAND, command & ~PCI_COMMAND_FAST_BACK); + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XIO2000A, + quirk_xio2000a); #ifdef CONFIG_X86_IO_APIC diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index da1fda8623e0..f490e7a7307a 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -776,6 +776,7 @@ #define PCI_DEVICE_ID_TI_X515 0x8036 #define PCI_DEVICE_ID_TI_XX12 0x8039 #define PCI_DEVICE_ID_TI_XX12_FM 0x803b +#define PCI_DEVICE_ID_TI_XIO2000A 0x8231 #define PCI_DEVICE_ID_TI_1130 0xac12 #define PCI_DEVICE_ID_TI_1031 0xac13 #define PCI_DEVICE_ID_TI_1131 0xac15 -- cgit v1.2.3 From 19eea630f7c56038dd80fe2f6910c78655bf29c8 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 17 Sep 2009 15:28:22 -0700 Subject: PCI: pci.c: fix kernel-doc notation Fix kernel-doc notation (& warnings) in pci/pci.c. Signed-off-by: Randy Dunlap Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6e1da5aa7bb9..3835871f4832 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2546,10 +2546,10 @@ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) /** * pci_set_vga_state - set VGA decode state on device and parents if requested - * @dev the PCI device - * @decode - true = enable decoding, false = disable decoding - * @command_bits PCI_COMMAND_IO and/or PCI_COMMAND_MEMORY - * @change_bridge - traverse ancestors and change bridges + * @dev: the PCI device + * @decode: true = enable decoding, false = disable decoding + * @command_bits: PCI_COMMAND_IO and/or PCI_COMMAND_MEMORY + * @change_bridge: traverse ancestors and change bridges */ int pci_set_vga_state(struct pci_dev *dev, bool decode, unsigned int command_bits, bool change_bridge) -- cgit v1.2.3 From 308cf8e13f42f476dfd6552aeff58fdc0788e566 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sun, 13 Sep 2009 15:57:10 -0700 Subject: PCI: get larger bridge ranges when space is available Found one system: [ 71.120590] pci 0000:40:05.0: scanning behind bridge, config 4f4a40, pass 0 [ 71.138283] PCI: Scanning bus 0000:4a [ 71.140341] pci 0000:4a:00.0: found [15b3:6278] class 000c06 header type 00 [ 71.157173] pci 0000:4a:00.0: reg 10 64bit mmio: [0x000000-0x0fffff] [ 71.161697] pci 0000:4a:00.0: reg 18 64bit mmio pref: [0x000000-0x7fffff] [ 71.179403] pci 0000:4a:00.0: reg 20 64bit mmio pref: [0x000000-0xfffffff] [ 71.185366] pci 0000:4a:00.0: calling quirk_resource_alignment+0x0/0x1dd [ 71.200846] pci 0000:4a:00.0: disabling ASPM on pre-1.1 PCIe device. You can enable it with 'pcie_aspm=force' [ 71.219623] PCI: Fixups for bus 0000:4a [ 71.222194] pci 0000:40:05.0: bridge 32bit mmio: [0xcf000000-0xcf0fffff] [ 71.238662] pci 0000:40:05.0: bridge 64bit mmio pref: [0xcd800000-0xcdffffff] [ 71.255793] PCI: Bus scan for 0000:4a returning with max=4a Device needs a big pref mmio, but BIOS doesn't allocate mmio to it aside from a small MMIO range. Later, the kernel will not allocate resources to that to the device: [ 99.574030] pci 0000:4a:00.0: BAR 4: can't allocate mem resource [0xd0000000-0xcdffffff] [ 99.580102] pci 0000:4a:00.0: BAR 2: got res [0xcd800000-0xcdffffff] bus [0xcd800000-0xcdffffff] flags 0x12120c [ 99.602307] pci 0000:4a:00.0: BAR 2: moved to bus [0xcd800000-0xcdffffff] flags 0x12120c [ 99.615991] pci 0000:4a:00.0: BAR 0: got res [0xcf000000-0xcf0fffff] bus [0xcf000000-0xcf0fffff] flags 0x120204 [ 99.634499] pci 0000:4a:00.0: BAR 0: moved to bus [0xcf000000-0xcf0fffff] flags 0x120204 [ 99.654318] pci 0000:40:05.0: PCI bridge, secondary bus 0000:4a [ 99.658766] pci 0000:40:05.0: IO window: disabled [ 99.675478] pci 0000:40:05.0: MEM window: 0xcf000000-0xcf0fffff [ 99.681663] pci 0000:40:05.0: PREFETCH window: 0x000000cd800000-0x000000cdffffff So try to get a big range in the pci bridge if there is no child using that range. With the patch we get: [ 99.104525] pci 0000:4a:00.0: BAR 4: got res [0xfc080000000-0xfc08fffffff] bus [0xfc080000000-0xfc08fffffff] flags 0x12120c [ 99.123624] pci 0000:4a:00.0: BAR 4: moved to bus [0xfc080000000-0xfc08fffffff] flags 0x12120c [ 99.131977] pci 0000:4a:00.0: BAR 2: got res [0xfc090000000-0xfc0907fffff] bus [0xfc090000000-0xfc0907fffff] flags 0x12120c [ 99.149788] pci 0000:4a:00.0: BAR 2: moved to bus [0xfc090000000-0xfc0907fffff] flags 0x12120c [ 99.169248] pci 0000:4a:00.0: BAR 0: got res [0xc0200000-0xc02fffff] bus [0xc0200000-0xc02fffff] flags 0x120204 [ 99.189508] pci 0000:4a:00.0: BAR 0: moved to bus [0xc0200000-0xc02fffff] flags 0x120204 [ 99.206402] pci 0000:40:05.0: PCI bridge, secondary bus 0000:4a [ 99.210637] pci 0000:40:05.0: IO window: disabled [ 99.224856] pci 0000:40:05.0: MEM window: 0xc0200000-0xc03fffff [ 99.230019] pci 0000:40:05.0: PREFETCH window: 0x000fc080000000-0x000fc097ffffff Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index cb1a027eb552..0959430534b2 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -299,8 +299,17 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned lon r = bus->resource[i]; if (r == &ioport_resource || r == &iomem_resource) continue; - if (r && (r->flags & type_mask) == type && !r->parent) - return r; + if (r && (r->flags & type_mask) == type) { + if (!r->parent) + return r; + /* + * if there is no child under that, we should release + * and use it. don't need to reset it, pbus_size_* will + * set it again + */ + if (!r->child && !release_resource(r)) + return r; + } } return NULL; } -- cgit v1.2.3 From 30fc24b5cbc55f9e6c686e2710cc812419bddc0c Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 7 Oct 2009 09:28:56 -0700 Subject: PCI: Prevent AER driver from being loaded on non-root port PCIE devices A bug was seen on boards using a PLX 8518 switch device which advertises AER on each of it's transparent bridges. The AER driver was loaded for each bridge and this driver tried to access the AER source ID register whenever an interrupt occured on the shared PCI INTX lines. The source ID register does not exist on non root port PCIE device's which advertise AER and trying to access this register causes a unsupported request error on the bridge. Thus, when the next interrupt occurs, another error is found and the non existent source ID register is accessed again, and so it goes on. The result is a spammed dmesg with unsupported request PCI express errors on the bridge device that the AER driver is loaded against. Reported-by: Malcolm Crossley Signed-off-by: Kenji Kaneshige Tested-by: Malcolm Crossley Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 2ce8f9ccc66e..6bf5b15916b0 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -52,7 +52,7 @@ static struct pci_error_handlers aer_error_handlers = { static struct pcie_port_service_driver aerdriver = { .name = "aer", - .port_type = PCIE_ANY_PORT, + .port_type = PCIE_RC_PORT, .service = PCIE_PORT_SERVICE_AER, .probe = aer_probe, -- cgit v1.2.3 From a791e35e12ff672e8a0e140abeeaf900c3b2ea77 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 8 Oct 2009 11:27:10 -0400 Subject: Btrfs: cleanup extent_clear_unlock_delalloc flags extent_clear_unlock_delalloc has a growing set of ugly parameters that is very difficult to read and maintain. This switches to a flag field and well named flag defines. Signed-off-by: Chris Mason --- fs/btrfs/extent_io.c | 27 +++++++++++---------------- fs/btrfs/extent_io.h | 16 ++++++++++------ fs/btrfs/inode.c | 45 ++++++++++++++++++++++++++++++--------------- 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index de1793ba004a..f9708bd01669 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -1401,12 +1401,7 @@ out_failed: int extent_clear_unlock_delalloc(struct inode *inode, struct extent_io_tree *tree, u64 start, u64 end, struct page *locked_page, - int unlock_pages, - int clear_unlock, - int clear_delalloc, int clear_dirty, - int set_writeback, - int end_writeback, - int set_private2) + unsigned long op) { int ret; struct page *pages[16]; @@ -1416,17 +1411,17 @@ int extent_clear_unlock_delalloc(struct inode *inode, int i; int clear_bits = 0; - if (clear_unlock) + if (op & EXTENT_CLEAR_UNLOCK) clear_bits |= EXTENT_LOCKED; - if (clear_dirty) + if (op & EXTENT_CLEAR_DIRTY) clear_bits |= EXTENT_DIRTY; - if (clear_delalloc) + if (op & EXTENT_CLEAR_DELALLOC) clear_bits |= EXTENT_DELALLOC; clear_extent_bit(tree, start, end, clear_bits, 1, 0, NULL, GFP_NOFS); - if (!(unlock_pages || clear_dirty || set_writeback || end_writeback || - set_private2)) + if (!(op & (EXTENT_CLEAR_UNLOCK_PAGE | EXTENT_CLEAR_DIRTY | EXTENT_SET_WRITEBACK | + EXTENT_END_WRITEBACK | EXTENT_SET_PRIVATE2))) return 0; while (nr_pages > 0) { @@ -1435,20 +1430,20 @@ int extent_clear_unlock_delalloc(struct inode *inode, nr_pages, ARRAY_SIZE(pages)), pages); for (i = 0; i < ret; i++) { - if (set_private2) + if (op & EXTENT_SET_PRIVATE2) SetPagePrivate2(pages[i]); if (pages[i] == locked_page) { page_cache_release(pages[i]); continue; } - if (clear_dirty) + if (op & EXTENT_CLEAR_DIRTY) clear_page_dirty_for_io(pages[i]); - if (set_writeback) + if (op & EXTENT_SET_WRITEBACK) set_page_writeback(pages[i]); - if (end_writeback) + if (op & EXTENT_END_WRITEBACK) end_page_writeback(pages[i]); - if (unlock_pages) + if (op & EXTENT_CLEAR_UNLOCK_PAGE) unlock_page(pages[i]); page_cache_release(pages[i]); } diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 4794ec891fed..41d2a47ecf38 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -25,6 +25,15 @@ #define EXTENT_BUFFER_BLOCKING 1 #define EXTENT_BUFFER_DIRTY 2 +/* these are flags for extent_clear_unlock_delalloc */ +#define EXTENT_CLEAR_UNLOCK_PAGE 0x1 +#define EXTENT_CLEAR_UNLOCK 0x2 +#define EXTENT_CLEAR_DELALLOC 0x4 +#define EXTENT_CLEAR_DIRTY 0x8 +#define EXTENT_SET_WRITEBACK 0x10 +#define EXTENT_END_WRITEBACK 0x20 +#define EXTENT_SET_PRIVATE2 0x40 + /* * page->private values. Every page that is controlled by the extent * map has page->private set to one. @@ -288,10 +297,5 @@ int extent_range_uptodate(struct extent_io_tree *tree, int extent_clear_unlock_delalloc(struct inode *inode, struct extent_io_tree *tree, u64 start, u64 end, struct page *locked_page, - int unlock_page, - int clear_unlock, - int clear_delalloc, int clear_dirty, - int set_writeback, - int end_writeback, - int set_private2); + unsigned long op); #endif diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 3a6f953337b5..a7058fca3aab 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -424,9 +424,10 @@ again: * and free up our temp pages. */ extent_clear_unlock_delalloc(inode, - &BTRFS_I(inode)->io_tree, - start, end, NULL, 1, 0, - 0, 1, 1, 1, 0); + &BTRFS_I(inode)->io_tree, + start, end, NULL, + EXTENT_CLEAR_UNLOCK_PAGE | EXTENT_CLEAR_DIRTY | + EXTENT_SET_WRITEBACK | EXTENT_END_WRITEBACK); ret = 0; goto free_pages_out; } @@ -637,11 +638,13 @@ static noinline int submit_compressed_extents(struct inode *inode, * clear dirty, set writeback and unlock the pages. */ extent_clear_unlock_delalloc(inode, - &BTRFS_I(inode)->io_tree, - async_extent->start, - async_extent->start + - async_extent->ram_size - 1, - NULL, 1, 1, 0, 1, 1, 0, 0); + &BTRFS_I(inode)->io_tree, + async_extent->start, + async_extent->start + + async_extent->ram_size - 1, + NULL, EXTENT_CLEAR_UNLOCK_PAGE | + EXTENT_CLEAR_UNLOCK | + EXTENT_CLEAR_DIRTY | EXTENT_SET_WRITEBACK); ret = btrfs_submit_compressed_write(inode, async_extent->start, @@ -712,9 +715,14 @@ static noinline int cow_file_range(struct inode *inode, start, end, 0, NULL); if (ret == 0) { extent_clear_unlock_delalloc(inode, - &BTRFS_I(inode)->io_tree, - start, end, NULL, 1, 1, - 1, 1, 1, 1, 0); + &BTRFS_I(inode)->io_tree, + start, end, NULL, + EXTENT_CLEAR_UNLOCK_PAGE | + EXTENT_CLEAR_UNLOCK | + EXTENT_CLEAR_DELALLOC | + EXTENT_CLEAR_DIRTY | + EXTENT_SET_WRITEBACK | + EXTENT_END_WRITEBACK); *nr_written = *nr_written + (end - start + PAGE_CACHE_SIZE) / PAGE_CACHE_SIZE; *page_started = 1; @@ -738,6 +746,8 @@ static noinline int cow_file_range(struct inode *inode, btrfs_drop_extent_cache(inode, start, start + num_bytes - 1, 0); while (disk_num_bytes > 0) { + unsigned long op; + cur_alloc_size = min(disk_num_bytes, root->fs_info->max_extent); ret = btrfs_reserve_extent(trans, root, cur_alloc_size, root->sectorsize, 0, alloc_hint, @@ -789,10 +799,13 @@ static noinline int cow_file_range(struct inode *inode, * Do set the Private2 bit so we know this page was properly * setup for writepage */ + op = unlock ? EXTENT_CLEAR_UNLOCK_PAGE : 0; + op |= EXTENT_CLEAR_UNLOCK | EXTENT_CLEAR_DELALLOC | + EXTENT_SET_PRIVATE2; + extent_clear_unlock_delalloc(inode, &BTRFS_I(inode)->io_tree, start, start + ram_size - 1, - locked_page, unlock, 1, - 1, 0, 0, 0, 1); + locked_page, op); disk_num_bytes -= cur_alloc_size; num_bytes -= cur_alloc_size; alloc_hint = ins.objectid + ins.offset; @@ -1112,8 +1125,10 @@ out_check: BUG_ON(ret); extent_clear_unlock_delalloc(inode, &BTRFS_I(inode)->io_tree, - cur_offset, cur_offset + num_bytes - 1, - locked_page, 1, 1, 1, 0, 0, 0, 1); + cur_offset, cur_offset + num_bytes - 1, + locked_page, EXTENT_CLEAR_UNLOCK_PAGE | + EXTENT_CLEAR_UNLOCK | EXTENT_CLEAR_DELALLOC | + EXTENT_SET_PRIVATE2); cur_offset = extent_end; if (cur_offset > end) break; -- cgit v1.2.3 From a3429ab70b04363c6190964e82f04f44f3e34cf0 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 8 Oct 2009 12:30:20 -0400 Subject: Btrfs: delay clearing EXTENT_DELALLOC for compressed extents When compression is on, the cow_file_range code is farmed off to worker threads. This allows us to do significant CPU work in parallel on SMP machines. But it is a delicate balance around when we clear flags and how. In the past we cleared the delalloc flag immediately, which was safe because the pages stayed locked. But this is causing problems with the newest ENOSPC code, and with the recent extent state cleanups we can now clear the delalloc bit at the same time the uncompressed code does. Signed-off-by: Chris Mason --- fs/btrfs/inode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a7058fca3aab..401dfb2a94e8 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -427,6 +427,7 @@ again: &BTRFS_I(inode)->io_tree, start, end, NULL, EXTENT_CLEAR_UNLOCK_PAGE | EXTENT_CLEAR_DIRTY | + EXTENT_CLEAR_DELALLOC | EXTENT_SET_WRITEBACK | EXTENT_END_WRITEBACK); ret = 0; goto free_pages_out; @@ -644,6 +645,7 @@ static noinline int submit_compressed_extents(struct inode *inode, async_extent->ram_size - 1, NULL, EXTENT_CLEAR_UNLOCK_PAGE | EXTENT_CLEAR_UNLOCK | + EXTENT_CLEAR_DELALLOC | EXTENT_CLEAR_DIRTY | EXTENT_SET_WRITEBACK); ret = btrfs_submit_compressed_write(inode, @@ -877,8 +879,8 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page, u64 cur_end; int limit = 10 * 1024 * 1042; - clear_extent_bit(&BTRFS_I(inode)->io_tree, start, end, EXTENT_LOCKED | - EXTENT_DELALLOC, 1, 0, NULL, GFP_NOFS); + clear_extent_bit(&BTRFS_I(inode)->io_tree, start, end, EXTENT_LOCKED, + 1, 0, NULL, GFP_NOFS); while (start < end) { async_cow = kmalloc(sizeof(*async_cow), GFP_NOFS); async_cow->inode = inode; -- cgit v1.2.3 From 32c00aff718bb54a214b39146bdd9ac01511cd25 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 8 Oct 2009 13:34:05 -0400 Subject: Btrfs: release delalloc reservations on extent item insertion This patch fixes an issue with the delalloc metadata space reservation code. The problem is we used to free the reservation as soon as we allocated the delalloc region. The problem with this is if we are not inserting an inline extent, we don't actually insert the extent item until after the ordered extent is written out. This patch does 3 things, 1) It moves the reservation clearing stuff into the ordered code, so when we remove the ordered extent we remove the reservation. 2) It adds a EXTENT_DO_ACCOUNTING flag that gets passed when we clear delalloc bits in the cases where we want to clear the metadata reservation when we clear the delalloc extent, in the case that we do an inline extent or we invalidate the page. 3) It adds another waitqueue to the space info so that when we start a fs wide delalloc flush, anybody else who also hits that area will simply wait for the flush to finish and then try to make their allocation. This has been tested thoroughly to make sure we did not regress on performance. Signed-off-by: Josef Bacik Signed-off-by: Chris Mason --- fs/btrfs/btrfs_inode.h | 12 ++++++----- fs/btrfs/ctree.h | 3 +++ fs/btrfs/extent-tree.c | 54 +++++++++++++++++++++++++++++++++++++++---------- fs/btrfs/extent_io.c | 19 +++++++++++------ fs/btrfs/extent_io.h | 2 ++ fs/btrfs/file.c | 3 ++- fs/btrfs/inode.c | 45 ++++++++++++++++++++++++++++------------- fs/btrfs/ordered-data.c | 6 ++++++ 8 files changed, 107 insertions(+), 37 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index a54d354cefcb..c71abec0ab90 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -128,12 +128,14 @@ struct btrfs_inode { u64 last_unlink_trans; /* - * These two counters are for delalloc metadata reservations. We keep - * track of how many extents we've accounted for vs how many extents we - * have. + * Counters to keep track of the number of extent item's we may use due + * to delalloc and such. outstanding_extents is the number of extent + * items we think we'll end up using, and reserved_extents is the number + * of extent items we've reserved metadata for. */ - int delalloc_reserved_extents; - int delalloc_extents; + spinlock_t accounting_lock; + int reserved_extents; + int outstanding_extents; /* * ordered_data_close is set by truncate when a file that used diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 1b920ffc6a59..dbdada569507 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -699,6 +699,9 @@ struct btrfs_space_info { int allocating_chunk; wait_queue_head_t wait; + + int flushing; + wait_queue_head_t flush_wait; }; /* diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 2f82fabd7011..3d1be0b77f8f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2823,14 +2823,17 @@ int btrfs_unreserve_metadata_for_delalloc(struct btrfs_root *root, num_items); spin_lock(&meta_sinfo->lock); - if (BTRFS_I(inode)->delalloc_reserved_extents <= - BTRFS_I(inode)->delalloc_extents) { + spin_lock(&BTRFS_I(inode)->accounting_lock); + if (BTRFS_I(inode)->reserved_extents <= + BTRFS_I(inode)->outstanding_extents) { + spin_unlock(&BTRFS_I(inode)->accounting_lock); spin_unlock(&meta_sinfo->lock); return 0; } + spin_unlock(&BTRFS_I(inode)->accounting_lock); - BTRFS_I(inode)->delalloc_reserved_extents--; - BUG_ON(BTRFS_I(inode)->delalloc_reserved_extents < 0); + BTRFS_I(inode)->reserved_extents--; + BUG_ON(BTRFS_I(inode)->reserved_extents < 0); if (meta_sinfo->bytes_delalloc < num_bytes) { bug = true; @@ -2863,6 +2866,37 @@ static void check_force_delalloc(struct btrfs_space_info *meta_sinfo) meta_sinfo->force_delalloc = 0; } +static void flush_delalloc(struct btrfs_root *root, + struct btrfs_space_info *info) +{ + bool wait = false; + + spin_lock(&info->lock); + + if (!info->flushing) { + info->flushing = 1; + init_waitqueue_head(&info->flush_wait); + } else { + wait = true; + } + + spin_unlock(&info->lock); + + if (wait) { + wait_event(info->flush_wait, + !info->flushing); + return; + } + + btrfs_start_delalloc_inodes(root); + btrfs_wait_ordered_extents(root, 0); + + spin_lock(&info->lock); + info->flushing = 0; + spin_unlock(&info->lock); + wake_up(&info->flush_wait); +} + static int maybe_allocate_chunk(struct btrfs_root *root, struct btrfs_space_info *info) { @@ -2980,21 +3014,20 @@ again: filemap_flush(inode->i_mapping); goto again; } else if (flushed == 3) { - btrfs_start_delalloc_inodes(root); - btrfs_wait_ordered_extents(root, 0); + flush_delalloc(root, meta_sinfo); goto again; } spin_lock(&meta_sinfo->lock); meta_sinfo->bytes_delalloc -= num_bytes; spin_unlock(&meta_sinfo->lock); printk(KERN_ERR "enospc, has %d, reserved %d\n", - BTRFS_I(inode)->delalloc_extents, - BTRFS_I(inode)->delalloc_reserved_extents); + BTRFS_I(inode)->outstanding_extents, + BTRFS_I(inode)->reserved_extents); dump_space_info(meta_sinfo, 0, 0); return -ENOSPC; } - BTRFS_I(inode)->delalloc_reserved_extents++; + BTRFS_I(inode)->reserved_extents++; check_force_delalloc(meta_sinfo); spin_unlock(&meta_sinfo->lock); @@ -3093,8 +3126,7 @@ again: } if (retries == 2) { - btrfs_start_delalloc_inodes(root); - btrfs_wait_ordered_extents(root, 0); + flush_delalloc(root, meta_sinfo); goto again; } spin_lock(&meta_sinfo->lock); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index f9708bd01669..96577e8bf9fd 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -460,7 +460,8 @@ static int clear_state_bit(struct extent_io_tree *tree, struct extent_state *state, int bits, int wake, int delete) { - int ret = state->state & bits; + int bits_to_clear = bits & ~EXTENT_DO_ACCOUNTING; + int ret = state->state & bits_to_clear; if ((bits & EXTENT_DIRTY) && (state->state & EXTENT_DIRTY)) { u64 range = state->end - state->start + 1; @@ -468,7 +469,7 @@ static int clear_state_bit(struct extent_io_tree *tree, tree->dirty_bytes -= range; } clear_state_cb(tree, state, bits); - state->state &= ~bits; + state->state &= ~bits_to_clear; if (wake) wake_up(&state->wq); if (delete || state->state == 0) { @@ -956,7 +957,8 @@ int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, gfp_t mask) { return clear_extent_bit(tree, start, end, - EXTENT_DIRTY | EXTENT_DELALLOC, 0, 0, + EXTENT_DIRTY | EXTENT_DELALLOC | + EXTENT_DO_ACCOUNTING, 0, 0, NULL, mask); } @@ -1419,9 +1421,13 @@ int extent_clear_unlock_delalloc(struct inode *inode, if (op & EXTENT_CLEAR_DELALLOC) clear_bits |= EXTENT_DELALLOC; + if (op & EXTENT_CLEAR_ACCOUNTING) + clear_bits |= EXTENT_DO_ACCOUNTING; + clear_extent_bit(tree, start, end, clear_bits, 1, 0, NULL, GFP_NOFS); - if (!(op & (EXTENT_CLEAR_UNLOCK_PAGE | EXTENT_CLEAR_DIRTY | EXTENT_SET_WRITEBACK | - EXTENT_END_WRITEBACK | EXTENT_SET_PRIVATE2))) + if (!(op & (EXTENT_CLEAR_UNLOCK_PAGE | EXTENT_CLEAR_DIRTY | + EXTENT_SET_WRITEBACK | EXTENT_END_WRITEBACK | + EXTENT_SET_PRIVATE2))) return 0; while (nr_pages > 0) { @@ -2709,7 +2715,8 @@ int extent_invalidatepage(struct extent_io_tree *tree, lock_extent(tree, start, end, GFP_NOFS); wait_on_page_writeback(page); clear_extent_bit(tree, start, end, - EXTENT_LOCKED | EXTENT_DIRTY | EXTENT_DELALLOC, + EXTENT_LOCKED | EXTENT_DIRTY | EXTENT_DELALLOC | + EXTENT_DO_ACCOUNTING, 1, 1, NULL, GFP_NOFS); return 0; } diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 41d2a47ecf38..36de250a7b2b 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -15,6 +15,7 @@ #define EXTENT_BUFFER_FILLED (1 << 8) #define EXTENT_BOUNDARY (1 << 9) #define EXTENT_NODATASUM (1 << 10) +#define EXTENT_DO_ACCOUNTING (1 << 11) #define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK) /* flags for bio submission */ @@ -33,6 +34,7 @@ #define EXTENT_SET_WRITEBACK 0x10 #define EXTENT_END_WRITEBACK 0x20 #define EXTENT_SET_PRIVATE2 0x40 +#define EXTENT_CLEAR_ACCOUNTING 0x80 /* * page->private values. Every page that is controlled by the extent diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index f155179877a6..53fb1c997f0e 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -878,7 +878,8 @@ again: btrfs_put_ordered_extent(ordered); clear_extent_bits(&BTRFS_I(inode)->io_tree, start_pos, - last_pos - 1, EXTENT_DIRTY | EXTENT_DELALLOC, + last_pos - 1, EXTENT_DIRTY | EXTENT_DELALLOC | + EXTENT_DO_ACCOUNTING, GFP_NOFS); unlock_extent(&BTRFS_I(inode)->io_tree, start_pos, last_pos - 1, GFP_NOFS); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 401dfb2a94e8..ccc4f1121210 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -428,6 +428,7 @@ again: start, end, NULL, EXTENT_CLEAR_UNLOCK_PAGE | EXTENT_CLEAR_DIRTY | EXTENT_CLEAR_DELALLOC | + EXTENT_CLEAR_ACCOUNTING | EXTENT_SET_WRITEBACK | EXTENT_END_WRITEBACK); ret = 0; goto free_pages_out; @@ -722,6 +723,7 @@ static noinline int cow_file_range(struct inode *inode, EXTENT_CLEAR_UNLOCK_PAGE | EXTENT_CLEAR_UNLOCK | EXTENT_CLEAR_DELALLOC | + EXTENT_CLEAR_ACCOUNTING | EXTENT_CLEAR_DIRTY | EXTENT_SET_WRITEBACK | EXTENT_END_WRITEBACK); @@ -1195,15 +1197,17 @@ static int btrfs_split_extent_hook(struct inode *inode, root->fs_info->max_extent); /* - * if we break a large extent up then leave delalloc_extents be, - * since we've already accounted for the large extent. + * if we break a large extent up then leave oustanding_extents + * be, since we've already accounted for the large extent. */ if (div64_u64(new_size + root->fs_info->max_extent - 1, root->fs_info->max_extent) < num_extents) return 0; } - BTRFS_I(inode)->delalloc_extents++; + spin_lock(&BTRFS_I(inode)->accounting_lock); + BTRFS_I(inode)->outstanding_extents++; + spin_unlock(&BTRFS_I(inode)->accounting_lock); return 0; } @@ -1234,7 +1238,9 @@ static int btrfs_merge_extent_hook(struct inode *inode, /* we're not bigger than the max, unreserve the space and go */ if (new_size <= root->fs_info->max_extent) { - BTRFS_I(inode)->delalloc_extents--; + spin_lock(&BTRFS_I(inode)->accounting_lock); + BTRFS_I(inode)->outstanding_extents--; + spin_unlock(&BTRFS_I(inode)->accounting_lock); return 0; } @@ -1248,7 +1254,9 @@ static int btrfs_merge_extent_hook(struct inode *inode, root->fs_info->max_extent) > num_extents) return 0; - BTRFS_I(inode)->delalloc_extents--; + spin_lock(&BTRFS_I(inode)->accounting_lock); + BTRFS_I(inode)->outstanding_extents--; + spin_unlock(&BTRFS_I(inode)->accounting_lock); return 0; } @@ -1270,7 +1278,9 @@ static int btrfs_set_bit_hook(struct inode *inode, u64 start, u64 end, if (!(old & EXTENT_DELALLOC) && (bits & EXTENT_DELALLOC)) { struct btrfs_root *root = BTRFS_I(inode)->root; - BTRFS_I(inode)->delalloc_extents++; + spin_lock(&BTRFS_I(inode)->accounting_lock); + BTRFS_I(inode)->outstanding_extents++; + spin_unlock(&BTRFS_I(inode)->accounting_lock); btrfs_delalloc_reserve_space(root, inode, end - start + 1); spin_lock(&root->fs_info->delalloc_lock); BTRFS_I(inode)->delalloc_bytes += end - start + 1; @@ -1298,8 +1308,12 @@ static int btrfs_clear_bit_hook(struct inode *inode, if ((state->state & EXTENT_DELALLOC) && (bits & EXTENT_DELALLOC)) { struct btrfs_root *root = BTRFS_I(inode)->root; - BTRFS_I(inode)->delalloc_extents--; - btrfs_unreserve_metadata_for_delalloc(root, inode, 1); + if (bits & EXTENT_DO_ACCOUNTING) { + spin_lock(&BTRFS_I(inode)->accounting_lock); + BTRFS_I(inode)->outstanding_extents--; + spin_unlock(&BTRFS_I(inode)->accounting_lock); + btrfs_unreserve_metadata_for_delalloc(root, inode, 1); + } spin_lock(&root->fs_info->delalloc_lock); if (state->end - state->start + 1 > @@ -4825,7 +4839,8 @@ static void btrfs_invalidatepage(struct page *page, unsigned long offset) */ clear_extent_bit(tree, page_start, page_end, EXTENT_DIRTY | EXTENT_DELALLOC | - EXTENT_LOCKED, 1, 0, NULL, GFP_NOFS); + EXTENT_LOCKED | EXTENT_DO_ACCOUNTING, 1, 0, + NULL, GFP_NOFS); /* * whoever cleared the private bit is responsible * for the finish_ordered_io @@ -4838,8 +4853,8 @@ static void btrfs_invalidatepage(struct page *page, unsigned long offset) lock_extent(tree, page_start, page_end, GFP_NOFS); } clear_extent_bit(tree, page_start, page_end, - EXTENT_LOCKED | EXTENT_DIRTY | EXTENT_DELALLOC, - 1, 1, NULL, GFP_NOFS); + EXTENT_LOCKED | EXTENT_DIRTY | EXTENT_DELALLOC | + EXTENT_DO_ACCOUNTING, 1, 1, NULL, GFP_NOFS); __btrfs_releasepage(page, GFP_NOFS); ClearPageChecked(page); @@ -4934,7 +4949,8 @@ again: * prepare_pages in the normal write path. */ clear_extent_bits(&BTRFS_I(inode)->io_tree, page_start, page_end, - EXTENT_DIRTY | EXTENT_DELALLOC, GFP_NOFS); + EXTENT_DIRTY | EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING, + GFP_NOFS); ret = btrfs_set_extent_delalloc(inode, page_start, page_end); if (ret) { @@ -5082,8 +5098,9 @@ struct inode *btrfs_alloc_inode(struct super_block *sb) return NULL; ei->last_trans = 0; ei->logged_trans = 0; - ei->delalloc_extents = 0; - ei->delalloc_reserved_extents = 0; + ei->outstanding_extents = 0; + ei->reserved_extents = 0; + spin_lock_init(&ei->accounting_lock); btrfs_ordered_inode_tree_init(&ei->ordered_tree); INIT_LIST_HEAD(&ei->i_orphan); INIT_LIST_HEAD(&ei->ordered_operations); diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 4a9c8c4cec25..ab21c29f2247 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -306,6 +306,12 @@ int btrfs_remove_ordered_extent(struct inode *inode, tree->last = NULL; set_bit(BTRFS_ORDERED_COMPLETE, &entry->flags); + spin_lock(&BTRFS_I(inode)->accounting_lock); + BTRFS_I(inode)->outstanding_extents--; + spin_unlock(&BTRFS_I(inode)->accounting_lock); + btrfs_unreserve_metadata_for_delalloc(BTRFS_I(inode)->root, + inode, 1); + spin_lock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock); list_del_init(&entry->root_extent_list); -- cgit v1.2.3 From e3ccfa989752c083ceb23c823a84f7ce3a081e61 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 7 Oct 2009 20:44:34 -0400 Subject: Btrfs: async delalloc flushing under space pressure This patch moves the delalloc flushing that occurs when we are under space pressure off to a async thread pool. This helps since we only free up metadata space when we actually insert the extent item, which means it takes quite a while for space to be free'ed up if we wait on all ordered extents. However, if space is freed up due to inline extents being inserted, we can wake people who are waiting up early, and they can finish their work. Signed-off-by: Josef Bacik Signed-off-by: Chris Mason --- fs/btrfs/ctree.h | 13 ++++---- fs/btrfs/disk-io.c | 6 ++++ fs/btrfs/extent-tree.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index dbdada569507..a362dd617e97 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -691,17 +691,17 @@ struct btrfs_space_info { struct list_head list; + /* for controlling how we free up space for allocations */ + wait_queue_head_t allocate_wait; + wait_queue_head_t flush_wait; + int allocating_chunk; + int flushing; + /* for block groups in our same type */ struct list_head block_groups; spinlock_t lock; struct rw_semaphore groups_sem; atomic_t caching_threads; - - int allocating_chunk; - wait_queue_head_t wait; - - int flushing; - wait_queue_head_t flush_wait; }; /* @@ -918,6 +918,7 @@ struct btrfs_fs_info { struct btrfs_workers endio_meta_write_workers; struct btrfs_workers endio_write_workers; struct btrfs_workers submit_workers; + struct btrfs_workers enospc_workers; /* * fixup workers take dirty pages that didn't properly go through * the cow mechanism and make them safe to write. It happens diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 9903f042765d..ac8927bdc33d 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1762,6 +1762,9 @@ struct btrfs_root *open_ctree(struct super_block *sb, min_t(u64, fs_devices->num_devices, fs_info->thread_pool_size), &fs_info->generic_worker); + btrfs_init_workers(&fs_info->enospc_workers, "enospc", + fs_info->thread_pool_size, + &fs_info->generic_worker); /* a higher idle thresh on the submit workers makes it much more * likely that bios will be send down in a sane order to the @@ -1809,6 +1812,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, btrfs_start_workers(&fs_info->endio_meta_workers, 1); btrfs_start_workers(&fs_info->endio_meta_write_workers, 1); btrfs_start_workers(&fs_info->endio_write_workers, 1); + btrfs_start_workers(&fs_info->enospc_workers, 1); fs_info->bdi.ra_pages *= btrfs_super_num_devices(disk_super); fs_info->bdi.ra_pages = max(fs_info->bdi.ra_pages, @@ -2023,6 +2027,7 @@ fail_sb_buffer: btrfs_stop_workers(&fs_info->endio_meta_write_workers); btrfs_stop_workers(&fs_info->endio_write_workers); btrfs_stop_workers(&fs_info->submit_workers); + btrfs_stop_workers(&fs_info->enospc_workers); fail_iput: invalidate_inode_pages2(fs_info->btree_inode->i_mapping); iput(fs_info->btree_inode); @@ -2449,6 +2454,7 @@ int close_ctree(struct btrfs_root *root) btrfs_stop_workers(&fs_info->endio_meta_write_workers); btrfs_stop_workers(&fs_info->endio_write_workers); btrfs_stop_workers(&fs_info->submit_workers); + btrfs_stop_workers(&fs_info->enospc_workers); btrfs_close_devices(fs_info->fs_devices); btrfs_mapping_tree_free(&fs_info->mapping_tree); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 3d1be0b77f8f..53026806ae9e 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2866,9 +2866,66 @@ static void check_force_delalloc(struct btrfs_space_info *meta_sinfo) meta_sinfo->force_delalloc = 0; } +struct async_flush { + struct btrfs_root *root; + struct btrfs_space_info *info; + struct btrfs_work work; +}; + +static noinline void flush_delalloc_async(struct btrfs_work *work) +{ + struct async_flush *async; + struct btrfs_root *root; + struct btrfs_space_info *info; + + async = container_of(work, struct async_flush, work); + root = async->root; + info = async->info; + + btrfs_start_delalloc_inodes(root); + wake_up(&info->flush_wait); + btrfs_wait_ordered_extents(root, 0); + + spin_lock(&info->lock); + info->flushing = 0; + spin_unlock(&info->lock); + wake_up(&info->flush_wait); + + kfree(async); +} + +static void wait_on_flush(struct btrfs_space_info *info) +{ + DEFINE_WAIT(wait); + u64 used; + + while (1) { + prepare_to_wait(&info->flush_wait, &wait, + TASK_UNINTERRUPTIBLE); + spin_lock(&info->lock); + if (!info->flushing) { + spin_unlock(&info->lock); + break; + } + + used = info->bytes_used + info->bytes_reserved + + info->bytes_pinned + info->bytes_readonly + + info->bytes_super + info->bytes_root + + info->bytes_may_use + info->bytes_delalloc; + if (used < info->total_bytes) { + spin_unlock(&info->lock); + break; + } + spin_unlock(&info->lock); + schedule(); + } + finish_wait(&info->flush_wait, &wait); +} + static void flush_delalloc(struct btrfs_root *root, struct btrfs_space_info *info) { + struct async_flush *async; bool wait = false; spin_lock(&info->lock); @@ -2883,11 +2940,24 @@ static void flush_delalloc(struct btrfs_root *root, spin_unlock(&info->lock); if (wait) { - wait_event(info->flush_wait, - !info->flushing); + wait_on_flush(info); return; } + async = kzalloc(sizeof(*async), GFP_NOFS); + if (!async) + goto flush; + + async->root = root; + async->info = info; + async->work.func = flush_delalloc_async; + + btrfs_queue_worker(&root->fs_info->enospc_workers, + &async->work); + wait_on_flush(info); + return; + +flush: btrfs_start_delalloc_inodes(root); btrfs_wait_ordered_extents(root, 0); @@ -2927,7 +2997,7 @@ static int maybe_allocate_chunk(struct btrfs_root *root, if (!info->allocating_chunk) { info->force_alloc = 1; info->allocating_chunk = 1; - init_waitqueue_head(&info->wait); + init_waitqueue_head(&info->allocate_wait); } else { wait = true; } @@ -2935,7 +3005,7 @@ static int maybe_allocate_chunk(struct btrfs_root *root, spin_unlock(&info->lock); if (wait) { - wait_event(info->wait, + wait_event(info->allocate_wait, !info->allocating_chunk); return 1; } @@ -2956,7 +3026,7 @@ out: spin_lock(&info->lock); info->allocating_chunk = 0; spin_unlock(&info->lock); - wake_up(&info->wait); + wake_up(&info->allocate_wait); if (ret) return 0; -- cgit v1.2.3 From ff782e0a131c7f669445c07fe5c7ba91e043b7ed Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 8 Oct 2009 15:30:04 -0400 Subject: Btrfs: optimize fsync for the single writer case This patch optimizes the tree logging stuff so it doesn't always wait 1 jiffie for new people to join the logging transaction if there is only ever 1 writer. This helps a little bit with latency where we have something like RPM where it will fdatasync every file it writes, and so waiting the 1 jiffie for every fdatasync really starts to add up. Signed-off-by: Josef Bacik Signed-off-by: Chris Mason --- fs/btrfs/ctree.h | 2 ++ fs/btrfs/tree-log.c | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index a362dd617e97..16ddb19dda86 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1010,6 +1010,8 @@ struct btrfs_root { atomic_t log_commit[2]; unsigned long log_transid; unsigned long log_batch; + pid_t log_start_pid; + bool log_multiple_pids; u64 objectid; u64 last_trans; diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 4d7d9abef42f..78f6254ac2d9 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -137,11 +137,20 @@ static int start_log_trans(struct btrfs_trans_handle *trans, mutex_lock(&root->log_mutex); if (root->log_root) { + if (!root->log_start_pid) { + root->log_start_pid = current->pid; + root->log_multiple_pids = false; + } else if (root->log_start_pid != current->pid) { + root->log_multiple_pids = true; + } + root->log_batch++; atomic_inc(&root->log_writers); mutex_unlock(&root->log_mutex); return 0; } + root->log_multiple_pids = false; + root->log_start_pid = current->pid; mutex_lock(&root->fs_info->tree_log_mutex); if (!root->fs_info->log_root_tree) { ret = btrfs_init_log_root_tree(trans, root->fs_info); @@ -1985,7 +1994,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, if (atomic_read(&root->log_commit[(index1 + 1) % 2])) wait_log_commit(trans, root, root->log_transid - 1); - while (1) { + while (root->log_multiple_pids) { unsigned long batch = root->log_batch; mutex_unlock(&root->log_mutex); schedule_timeout_uninterruptible(1); @@ -2011,6 +2020,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, root->log_batch = 0; root->log_transid++; log->log_transid = root->log_transid; + root->log_start_pid = 0; smp_mb(); /* * log tree has been flushed to disk, new modifications of -- cgit v1.2.3 From efefb1438be269897585934fc6c05deb4dfa01ce Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 9 Oct 2009 09:25:16 -0400 Subject: Btrfs: remove negative dentry when deleting subvolumne The use of btrfs_dentry_delete is removing dentries from the dcache when deleting subvolumne. btrfs_dentry_delete ignores negative dentries. This is incorrect since if we don't remove the negative dentry, its parent dentry can't be removed. Signed-off-by: Yan Zheng Signed-off-by: Chris Mason --- fs/btrfs/inode.c | 12 +++++++----- fs/btrfs/ioctl.c | 1 + 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index ccc4f1121210..d3585cb6483c 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3629,12 +3629,14 @@ static int btrfs_dentry_delete(struct dentry *dentry) { struct btrfs_root *root; - if (!dentry->d_inode) - return 0; + if (!dentry->d_inode && !IS_ROOT(dentry)) + dentry = dentry->d_parent; - root = BTRFS_I(dentry->d_inode)->root; - if (btrfs_root_refs(&root->root_item) == 0) - return 1; + if (dentry->d_inode) { + root = BTRFS_I(dentry->d_inode)->root; + if (btrfs_root_refs(&root->root_item) == 0) + return 1; + } return 0; } diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9a780c8d0ac8..e8795becad4c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -830,6 +830,7 @@ out_up_write: out_unlock: mutex_unlock(&inode->i_mutex); if (!err) { + shrink_dcache_sb(root->fs_info->sb); btrfs_invalidate_inodes(dest); d_delete(dentry); } -- cgit v1.2.3 From 94fcca9f8999e7828d5f4dc181daa39cad2af38a Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 9 Oct 2009 09:25:16 -0400 Subject: Btrfs: optimize back reference update during btrfs_drop_snapshot This patch reading level 0 tree blocks that already use full backrefs. Signed-off-by: Yan Zheng Signed-off-by: Chris Mason --- fs/btrfs/extent-tree.c | 82 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 53026806ae9e..4aedbff36b8f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4911,6 +4911,7 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans, u64 bytenr; u64 generation; u64 refs; + u64 flags; u64 last = 0; u32 nritems; u32 blocksize; @@ -4948,15 +4949,19 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans, generation <= root->root_key.offset) continue; + /* We don't lock the tree block, it's OK to be racy here */ + ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize, + &refs, &flags); + BUG_ON(ret); + BUG_ON(refs == 0); + if (wc->stage == DROP_REFERENCE) { - ret = btrfs_lookup_extent_info(trans, root, - bytenr, blocksize, - &refs, NULL); - BUG_ON(ret); - BUG_ON(refs == 0); if (refs == 1) goto reada; + if (wc->level == 1 && + (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) + continue; if (!wc->update_ref || generation <= root->root_key.offset) continue; @@ -4965,6 +4970,10 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans, &wc->update_progress); if (ret < 0) continue; + } else { + if (wc->level == 1 && + (flags & BTRFS_BLOCK_FLAG_FULL_BACKREF)) + continue; } reada: ret = readahead_tree_block(root, bytenr, blocksize, @@ -4988,7 +4997,7 @@ reada: static noinline int walk_down_proc(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, - struct walk_control *wc) + struct walk_control *wc, int lookup_info) { int level = wc->level; struct extent_buffer *eb = path->nodes[level]; @@ -5003,8 +5012,9 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans, * when reference count of tree block is 1, it won't increase * again. once full backref flag is set, we never clear it. */ - if ((wc->stage == DROP_REFERENCE && wc->refs[level] != 1) || - (wc->stage == UPDATE_BACKREF && !(wc->flags[level] & flag))) { + if (lookup_info && + ((wc->stage == DROP_REFERENCE && wc->refs[level] != 1) || + (wc->stage == UPDATE_BACKREF && !(wc->flags[level] & flag)))) { BUG_ON(!path->locks[level]); ret = btrfs_lookup_extent_info(trans, root, eb->start, eb->len, @@ -5065,7 +5075,7 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans, static noinline int do_walk_down(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, - struct walk_control *wc) + struct walk_control *wc, int *lookup_info) { u64 bytenr; u64 generation; @@ -5085,8 +5095,10 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, * for the subtree */ if (wc->stage == UPDATE_BACKREF && - generation <= root->root_key.offset) + generation <= root->root_key.offset) { + *lookup_info = 1; return 1; + } bytenr = btrfs_node_blockptr(path->nodes[level], path->slots[level]); blocksize = btrfs_level_size(root, level - 1); @@ -5099,14 +5111,19 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, btrfs_tree_lock(next); btrfs_set_lock_blocking(next); - if (wc->stage == DROP_REFERENCE) { - ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize, - &wc->refs[level - 1], - &wc->flags[level - 1]); - BUG_ON(ret); - BUG_ON(wc->refs[level - 1] == 0); + ret = btrfs_lookup_extent_info(trans, root, bytenr, blocksize, + &wc->refs[level - 1], + &wc->flags[level - 1]); + BUG_ON(ret); + BUG_ON(wc->refs[level - 1] == 0); + *lookup_info = 0; + if (wc->stage == DROP_REFERENCE) { if (wc->refs[level - 1] > 1) { + if (level == 1 && + (wc->flags[0] & BTRFS_BLOCK_FLAG_FULL_BACKREF)) + goto skip; + if (!wc->update_ref || generation <= root->root_key.offset) goto skip; @@ -5120,12 +5137,17 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, wc->stage = UPDATE_BACKREF; wc->shared_level = level - 1; } + } else { + if (level == 1 && + (wc->flags[0] & BTRFS_BLOCK_FLAG_FULL_BACKREF)) + goto skip; } if (!btrfs_buffer_uptodate(next, generation)) { btrfs_tree_unlock(next); free_extent_buffer(next); next = NULL; + *lookup_info = 1; } if (!next) { @@ -5148,21 +5170,22 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, skip: wc->refs[level - 1] = 0; wc->flags[level - 1] = 0; + if (wc->stage == DROP_REFERENCE) { + if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) { + parent = path->nodes[level]->start; + } else { + BUG_ON(root->root_key.objectid != + btrfs_header_owner(path->nodes[level])); + parent = 0; + } - if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) { - parent = path->nodes[level]->start; - } else { - BUG_ON(root->root_key.objectid != - btrfs_header_owner(path->nodes[level])); - parent = 0; + ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent, + root->root_key.objectid, level - 1, 0); + BUG_ON(ret); } - - ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent, - root->root_key.objectid, level - 1, 0); - BUG_ON(ret); - btrfs_tree_unlock(next); free_extent_buffer(next); + *lookup_info = 1; return 1; } @@ -5276,6 +5299,7 @@ static noinline int walk_down_tree(struct btrfs_trans_handle *trans, struct walk_control *wc) { int level = wc->level; + int lookup_info = 1; int ret; while (level >= 0) { @@ -5283,14 +5307,14 @@ static noinline int walk_down_tree(struct btrfs_trans_handle *trans, btrfs_header_nritems(path->nodes[level])) break; - ret = walk_down_proc(trans, root, path, wc); + ret = walk_down_proc(trans, root, path, wc, lookup_info); if (ret > 0) break; if (level == 0) break; - ret = do_walk_down(trans, root, path, wc); + ret = do_walk_down(trans, root, path, wc, &lookup_info); if (ret > 0) { path->slots[level]++; continue; -- cgit v1.2.3 From 82d339d9b3a6395f17d3253887653250b693b74b Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 9 Oct 2009 09:54:36 -0400 Subject: Btrfs: constify dentry_operations Signed-off-by: Chris Mason --- fs/btrfs/ctree.h | 2 +- fs/btrfs/inode.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 16ddb19dda86..36a19cd43e03 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2330,7 +2330,7 @@ int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode); void btrfs_orphan_cleanup(struct btrfs_root *root); int btrfs_cont_expand(struct inode *inode, loff_t size); int btrfs_invalidate_inodes(struct btrfs_root *root); -extern struct dentry_operations btrfs_dentry_operations; +extern const struct dentry_operations btrfs_dentry_operations; /* ioctl.c */ long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d3585cb6483c..d960492d3d88 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5840,6 +5840,6 @@ static struct inode_operations btrfs_symlink_inode_operations = { .removexattr = btrfs_removexattr, }; -struct dentry_operations btrfs_dentry_operations = { +const struct dentry_operations btrfs_dentry_operations = { .d_delete = btrfs_dentry_delete, }; -- cgit v1.2.3 From e9061e214810c9534381a705a1b46533e09f2676 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 9 Oct 2009 09:57:45 -0400 Subject: Btrfs: fix uninit compiler warning in cow_file_range_nocow The extent_type variable was exposed uninit via a goto. It should be impossible to trigger because it is protected by a check on another variable, but this makes sure. Signed-off-by: Chris Mason --- fs/btrfs/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d960492d3d88..ef399a7794ff 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1023,6 +1023,7 @@ next_slot: if (found_key.offset > cur_offset) { extent_end = found_key.offset; + extent_type = 0; goto out_check; } -- cgit v1.2.3 From ac6889cbb254be1ffea376bea4a96ce9be0e0ed0 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Fri, 9 Oct 2009 11:29:53 -0400 Subject: Btrfs: fix file clone ioctl for bookend extents The file clone ioctl was incorrectly taking the offset into the extent on disk into account when calculating the length of the cloned extent. The length never changes based on the offset into the physical extent. Test case: fallocate -l 1g image mke2fs image bcp image image2 e2fsck -f image2 (errors on image2) The math bug ends up wrapping the length of the extent, and things go wrong from there. Signed-off-by: Chris Mason --- fs/btrfs/ioctl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index e8795becad4c..cdbb054102b9 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1123,8 +1123,10 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, datao += off - key.offset; datal -= off - key.offset; } - if (key.offset + datao + datal > off + len) - datal = off + len - key.offset - datao; + + if (key.offset + datal > off + len) + datal = off + len - key.offset; + /* disko == 0 means it's a hole */ if (!disko) datao = 0; -- cgit v1.2.3 From cb0f1a1e77e4280c3138047ad5c16d88320a1fde Mon Sep 17 00:00:00 2001 From: Rakib Mullick Date: Fri, 9 Oct 2009 20:35:17 +0200 Subject: hwmon: (sht15) Fix spurious section mismatch warning Fix spurious section mismatch warnings, caused due to reference from variable sht_drivers to __devinit/__devexit functions sht15_probe()/remove(). We were warned by the following warnings: LD drivers/hwmon/built-in.o WARNING: drivers/hwmon/built-in.o(.data+0x264a0): Section mismatch in reference from the variable sht_drivers to the function .devinit.text:sht15_probe() The variable sht_drivers references the function __devinit sht15_probe() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console, WARNING: drivers/hwmon/built-in.o(.data+0x264a4): Section mismatch in reference from the variable sht_drivers to the function .devexit.text:sht15_remove() The variable sht_drivers references the function __devexit sht15_remove() If the reference is valid then annotate the variable with __exit* (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console, WARNING: drivers/hwmon/built-in.o(.data+0x264f0): Section mismatch in reference from the variable sht_drivers to the function .devinit.text:sht15_probe() The variable sht_drivers references the function __devinit sht15_probe() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console, WARNING: drivers/hwmon/built-in.o(.data+0x264f4): Section mismatch in reference from the variable sht_drivers to the function .devexit.text:sht15_remove() The variable sht_drivers references the function __devexit sht15_remove() If the reference is valid then annotate the variable with __exit* (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console, WARNING: drivers/hwmon/built-in.o(.data+0x26540): Section mismatch in reference from the variable sht_drivers to the function .devinit.text:sht15_probe() The variable sht_drivers references the function __devinit sht15_probe() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console, WARNING: drivers/hwmon/built-in.o(.data+0x26544): Section mismatch in reference from the variable sht_drivers to the function .devexit.text:sht15_remove() The variable sht_drivers references the function __devexit sht15_remove() If the reference is valid then annotate the variable with __exit* (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console, WARNING: drivers/hwmon/built-in.o(.data+0x26590): Section mismatch in reference from the variable sht_drivers to the function .devinit.text:sht15_probe() The variable sht_drivers references the function __devinit sht15_probe() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console, Signed-off-by: Rakib Mullick Cc: Jonathan Cameron Signed-off-by: Jean Delvare --- drivers/hwmon/sht15.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 303c02694c3c..64bd6e5a1406 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -622,7 +622,12 @@ static int __devexit sht15_remove(struct platform_device *pdev) } -static struct platform_driver sht_drivers[] = { +/* + * sht_drivers simultaneously refers to __devinit and __devexit function + * which causes spurious section mismatch warning. So use __refdata to + * get rid from this. + */ +static struct platform_driver __refdata sht_drivers[] = { { .driver = { .name = "sht10", -- cgit v1.2.3 From 18e255558574c5663c9d72fe82c099d2115aee55 Mon Sep 17 00:00:00 2001 From: Luca Tettamanti Date: Fri, 9 Oct 2009 20:35:18 +0200 Subject: hwmon: (asus_atk0110) Refactor the code Refactor the code of the new style interface around GGRP (enumeration) and GITM (read) helpers to mimic ASL code. Also switch the read path to use dynamic buffers (handled by ACPI core) since ASUS expanded the return buffer (ASBF) in newer boards (e.g. P7P55D). Signed-off-by: Luca Tettamanti Tested-by: Robert Hancock Tested-by: Thomas Backlund Signed-off-by: Jean Delvare --- drivers/hwmon/asus_atk0110.c | 158 ++++++++++++++++++++++++++++--------------- 1 file changed, 102 insertions(+), 56 deletions(-) diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index fe4fa29c9219..19f3fd32e10a 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -129,9 +129,22 @@ struct atk_sensor_data { char const *acpi_name; }; -struct atk_acpi_buffer_u64 { - union acpi_object buf; - u64 value; +/* Return buffer format: + * [0-3] "value" is valid flag + * [4-7] value + * [8- ] unknown stuff on newer mobos + */ +struct atk_acpi_ret_buffer { + u32 flags; + u32 value; + u8 data[]; +}; + +/* Input buffer used for GITM and SITM methods */ +struct atk_acpi_input_buf { + u32 id; + u32 param1; + u32 param2; }; static int atk_add(struct acpi_device *device); @@ -439,52 +452,110 @@ static int atk_read_value_old(struct atk_sensor_data *sensor, u64 *value) return 0; } -static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value) +static union acpi_object *atk_ggrp(struct atk_data *data, u16 mux) { - struct atk_data *data = sensor->data; struct device *dev = &data->acpi_dev->dev; + struct acpi_buffer buf; + acpi_status ret; struct acpi_object_list params; - struct acpi_buffer ret; union acpi_object id; - struct atk_acpi_buffer_u64 tmp; - acpi_status status; + union acpi_object *pack; id.type = ACPI_TYPE_INTEGER; - id.integer.value = sensor->id; - + id.integer.value = mux; params.count = 1; params.pointer = &id; - tmp.buf.type = ACPI_TYPE_BUFFER; - tmp.buf.buffer.pointer = (u8 *)&tmp.value; - tmp.buf.buffer.length = sizeof(u64); - ret.length = sizeof(tmp); - ret.pointer = &tmp; + buf.length = ACPI_ALLOCATE_BUFFER; + ret = acpi_evaluate_object(data->enumerate_handle, NULL, ¶ms, &buf); + if (ret != AE_OK) { + dev_err(dev, "GGRP[%#x] ACPI exception: %s\n", mux, + acpi_format_exception(ret)); + return ERR_PTR(-EIO); + } + pack = buf.pointer; + if (pack->type != ACPI_TYPE_PACKAGE) { + /* Execution was successful, but the id was not found */ + ACPI_FREE(pack); + return ERR_PTR(-ENOENT); + } + if (pack->package.count < 1) { + dev_err(dev, "GGRP[%#x] package is too small\n", mux); + ACPI_FREE(pack); + return ERR_PTR(-EIO); + } + return pack; +} + +static union acpi_object *atk_gitm(struct atk_data *data, u64 id) +{ + struct device *dev = &data->acpi_dev->dev; + struct atk_acpi_input_buf buf; + union acpi_object tmp; + struct acpi_object_list params; + struct acpi_buffer ret; + union acpi_object *obj; + acpi_status status; + + buf.id = id; + buf.param1 = 0; + buf.param2 = 0; + + tmp.type = ACPI_TYPE_BUFFER; + tmp.buffer.pointer = (u8 *)&buf; + tmp.buffer.length = sizeof(buf); + + params.count = 1; + params.pointer = (void *)&tmp; + + ret.length = ACPI_ALLOCATE_BUFFER; status = acpi_evaluate_object_typed(data->read_handle, NULL, ¶ms, &ret, ACPI_TYPE_BUFFER); if (status != AE_OK) { - dev_warn(dev, "%s: ACPI exception: %s\n", __func__, + dev_warn(dev, "GITM[%#llx] ACPI exception: %s\n", id, acpi_format_exception(status)); - return -EIO; + return ERR_PTR(-EIO); + } + obj = ret.pointer; + + /* Sanity check */ + if (obj->buffer.length < 8) { + dev_warn(dev, "Unexpected ASBF length: %u\n", + obj->buffer.length); + ACPI_FREE(obj); + return ERR_PTR(-EIO); } + return obj; +} - /* Return buffer format: - * [0-3] "value" is valid flag - * [4-7] value - */ - if (!(tmp.value & 0xffffffff)) { +static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value) +{ + struct atk_data *data = sensor->data; + struct device *dev = &data->acpi_dev->dev; + union acpi_object *obj; + struct atk_acpi_ret_buffer *buf; + int err = 0; + + obj = atk_gitm(data, sensor->id); + if (IS_ERR(obj)) + return PTR_ERR(obj); + + buf = (struct atk_acpi_ret_buffer *)obj->buffer.pointer; + if (buf->flags == 0) { /* The reading is not valid, possible causes: * - sensor failure * - enumeration was FUBAR (and we didn't notice) */ - dev_info(dev, "Failure: %#llx\n", tmp.value); - return -EIO; + dev_warn(dev, "Read failed, sensor = %#llx\n", sensor->id); + err = -EIO; + goto out; } - *value = (tmp.value & 0xffffffff00000000ULL) >> 32; - - return 0; + *value = buf->value; +out: + ACPI_FREE(obj); + return err; } static int atk_read_value(struct atk_sensor_data *sensor, u64 *value) @@ -716,39 +787,15 @@ cleanup: static int atk_enumerate_new_hwmon(struct atk_data *data) { struct device *dev = &data->acpi_dev->dev; - struct acpi_buffer buf; - acpi_status ret; - struct acpi_object_list params; - union acpi_object id; union acpi_object *pack; int err; int i; dev_dbg(dev, "Enumerating hwmon sensors\n"); - id.type = ACPI_TYPE_INTEGER; - id.integer.value = ATK_MUX_HWMON; - params.count = 1; - params.pointer = &id; - - buf.length = ACPI_ALLOCATE_BUFFER; - ret = acpi_evaluate_object_typed(data->enumerate_handle, NULL, ¶ms, - &buf, ACPI_TYPE_PACKAGE); - if (ret != AE_OK) { - dev_warn(dev, METHOD_ENUMERATE ": ACPI exception: %s\n", - acpi_format_exception(ret)); - return -ENODEV; - } - - /* Result must be a package */ - pack = buf.pointer; - - if (pack->package.count < 1) { - dev_dbg(dev, "%s: hwmon package is too small: %d\n", __func__, - pack->package.count); - err = -EINVAL; - goto out; - } + pack = atk_ggrp(data, ATK_MUX_HWMON); + if (IS_ERR(pack)) + return PTR_ERR(pack); for (i = 0; i < pack->package.count; i++) { union acpi_object *obj = &pack->package.elements[i]; @@ -758,8 +805,7 @@ static int atk_enumerate_new_hwmon(struct atk_data *data) err = data->voltage_count + data->temperature_count + data->fan_count; -out: - ACPI_FREE(buf.pointer); + ACPI_FREE(pack); return err; } -- cgit v1.2.3 From 9e6eba610c2eb68b05841a15ece1cf929c44aea3 Mon Sep 17 00:00:00 2001 From: Luca Tettamanti Date: Fri, 9 Oct 2009 20:35:18 +0200 Subject: hwmon: (asus_atk0110) Enable the EC On newer ASUS boards (e.g. P7P55D) the EC (that - among other things - is responsible for updating the readings from the hwmon sensors) is disabled by default since ASUS detected conflict with some tools under Windows. The following patch checks the state of the EC and enable it if needed; under Linux, native drivers are locked out from ACPI owned resources so there's no risk of conflict. Signed-off-by: Luca Tettamanti Tested-by: Robert Hancock Tested-by: Thomas Backlund Signed-off-by: Jean Delvare --- drivers/hwmon/asus_atk0110.c | 185 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 184 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index 19f3fd32e10a..5a3ee00c0e7d 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -35,18 +35,22 @@ #define METHOD_OLD_ENUM_FAN "FSIF" #define ATK_MUX_HWMON 0x00000006ULL +#define ATK_MUX_MGMT 0x00000011ULL #define ATK_CLASS_MASK 0xff000000ULL #define ATK_CLASS_FREQ_CTL 0x03000000ULL #define ATK_CLASS_FAN_CTL 0x04000000ULL #define ATK_CLASS_HWMON 0x06000000ULL +#define ATK_CLASS_MGMT 0x11000000ULL #define ATK_TYPE_MASK 0x00ff0000ULL #define HWMON_TYPE_VOLT 0x00020000ULL #define HWMON_TYPE_TEMP 0x00030000ULL #define HWMON_TYPE_FAN 0x00040000ULL -#define HWMON_SENSOR_ID_MASK 0x0000ffffULL +#define ATK_ELEMENT_ID_MASK 0x0000ffffULL + +#define ATK_EC_ID 0x11060004ULL enum atk_pack_member { HWMON_PACK_FLAGS, @@ -89,6 +93,9 @@ struct atk_data { /* new inteface */ acpi_handle enumerate_handle; acpi_handle read_handle; + acpi_handle write_handle; + + bool disable_ec; int voltage_count; int temperature_count; @@ -529,6 +536,43 @@ static union acpi_object *atk_gitm(struct atk_data *data, u64 id) return obj; } +static union acpi_object *atk_sitm(struct atk_data *data, + struct atk_acpi_input_buf *buf) +{ + struct device *dev = &data->acpi_dev->dev; + struct acpi_object_list params; + union acpi_object tmp; + struct acpi_buffer ret; + union acpi_object *obj; + acpi_status status; + + tmp.type = ACPI_TYPE_BUFFER; + tmp.buffer.pointer = (u8 *)buf; + tmp.buffer.length = sizeof(*buf); + + params.count = 1; + params.pointer = &tmp; + + ret.length = ACPI_ALLOCATE_BUFFER; + status = acpi_evaluate_object_typed(data->write_handle, NULL, ¶ms, + &ret, ACPI_TYPE_BUFFER); + if (status != AE_OK) { + dev_warn(dev, "SITM[%#x] ACPI exception: %s\n", buf->id, + acpi_format_exception(status)); + return ERR_PTR(-EIO); + } + obj = ret.pointer; + + /* Sanity check */ + if (obj->buffer.length < 8) { + dev_warn(dev, "Unexpected ASBF length: %u\n", + obj->buffer.length); + ACPI_FREE(obj); + return ERR_PTR(-EIO); + } + return obj; +} + static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value) { struct atk_data *data = sensor->data; @@ -784,6 +828,111 @@ cleanup: return ret; } +static int atk_ec_present(struct atk_data *data) +{ + struct device *dev = &data->acpi_dev->dev; + union acpi_object *pack; + union acpi_object *ec; + int ret; + int i; + + pack = atk_ggrp(data, ATK_MUX_MGMT); + if (IS_ERR(pack)) { + if (PTR_ERR(pack) == -ENOENT) { + /* The MGMT class does not exists - that's ok */ + dev_dbg(dev, "Class %#llx not found\n", ATK_MUX_MGMT); + return 0; + } + return PTR_ERR(pack); + } + + /* Search the EC */ + ec = NULL; + for (i = 0; i < pack->package.count; i++) { + union acpi_object *obj = &pack->package.elements[i]; + union acpi_object *id; + + if (obj->type != ACPI_TYPE_PACKAGE) + continue; + + id = &obj->package.elements[0]; + if (id->type != ACPI_TYPE_INTEGER) + continue; + + if (id->integer.value == ATK_EC_ID) { + ec = obj; + break; + } + } + + ret = (ec != NULL); + if (!ret) + /* The system has no EC */ + dev_dbg(dev, "EC not found\n"); + + ACPI_FREE(pack); + return ret; +} + +static int atk_ec_enabled(struct atk_data *data) +{ + struct device *dev = &data->acpi_dev->dev; + union acpi_object *obj; + struct atk_acpi_ret_buffer *buf; + int err; + + obj = atk_gitm(data, ATK_EC_ID); + if (IS_ERR(obj)) { + dev_err(dev, "Unable to query EC status\n"); + return PTR_ERR(obj); + } + buf = (struct atk_acpi_ret_buffer *)obj->buffer.pointer; + + if (buf->flags == 0) { + dev_err(dev, "Unable to query EC status\n"); + err = -EIO; + } else { + err = (buf->value != 0); + dev_dbg(dev, "EC is %sabled\n", + err ? "en" : "dis"); + } + + ACPI_FREE(obj); + return err; +} + +static int atk_ec_ctl(struct atk_data *data, int enable) +{ + struct device *dev = &data->acpi_dev->dev; + union acpi_object *obj; + struct atk_acpi_input_buf sitm; + struct atk_acpi_ret_buffer *ec_ret; + int err = 0; + + sitm.id = ATK_EC_ID; + sitm.param1 = enable; + sitm.param2 = 0; + + obj = atk_sitm(data, &sitm); + if (IS_ERR(obj)) { + dev_err(dev, "Failed to %sable the EC\n", + enable ? "en" : "dis"); + return PTR_ERR(obj); + } + ec_ret = (struct atk_acpi_ret_buffer *)obj->buffer.pointer; + if (ec_ret->flags == 0) { + dev_err(dev, "Failed to %sable the EC\n", + enable ? "en" : "dis"); + err = -EIO; + } else { + dev_info(dev, "EC %sabled\n", + enable ? "en" : "dis"); + } + + ACPI_FREE(obj); + return err; +} + static int atk_enumerate_new_hwmon(struct atk_data *data) { struct device *dev = &data->acpi_dev->dev; @@ -791,6 +940,23 @@ static int atk_enumerate_new_hwmon(struct atk_data *data) int err; int i; + err = atk_ec_present(data); + if (err < 0) + return err; + if (err) { + err = atk_ec_enabled(data); + if (err < 0) + return err; + /* If the EC was disabled we will disable it again on unload */ + data->disable_ec = err; + + err = atk_ec_ctl(data, 1); + if (err) { + data->disable_ec = false; + return err; + } + } + dev_dbg(dev, "Enumerating hwmon sensors\n"); pack = atk_ggrp(data, ATK_MUX_HWMON); @@ -941,6 +1107,15 @@ static int atk_check_new_if(struct atk_data *data) } data->read_handle = ret; + /* De-multiplexer (write) */ + status = acpi_get_handle(data->atk_handle, METHOD_WRITE, &ret); + if (status != AE_OK) { + dev_dbg(dev, "method " METHOD_READ " not found: %s\n", + acpi_format_exception(status)); + return -ENODEV; + } + data->write_handle = ret; + return 0; } @@ -961,6 +1136,7 @@ static int atk_add(struct acpi_device *device) data->acpi_dev = device; data->atk_handle = device->handle; INIT_LIST_HEAD(&data->sensor_list); + data->disable_ec = false; buf.length = ACPI_ALLOCATE_BUFFER; ret = acpi_evaluate_object_typed(data->atk_handle, BOARD_ID, NULL, @@ -1019,6 +1195,8 @@ static int atk_add(struct acpi_device *device) cleanup: atk_free_sensors(data); out: + if (data->disable_ec) + atk_ec_ctl(data, 0); kfree(data); return err; } @@ -1034,6 +1212,11 @@ static int atk_remove(struct acpi_device *device, int type) atk_free_sensors(data); hwmon_device_unregister(data->hwmon_dev); + if (data->disable_ec) { + if (atk_ec_ctl(data, 0)) + dev_err(&device->dev, "Failed to disable EC\n"); + } + kfree(data); return 0; -- cgit v1.2.3 From 05576a1e38e2d06dece32974c5218528d3fbc6e2 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 9 Oct 2009 20:35:19 +0200 Subject: MAINTAINERS: Fix Riku Voipio's address Signed-off-by: Jean Delvare Acked-by: Riku Voipio --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index e1da925b38c8..16c51fc5d764 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2058,7 +2058,7 @@ S: Maintained F: fs/* FINTEK F75375S HARDWARE MONITOR AND FAN CONTROLLER DRIVER -M: Riku Voipio +M: Riku Voipio L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/f75375s.c -- cgit v1.2.3 From 384e724b6b396de32c1e9cb0b7ccdd011bcc2beb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Oct 2009 20:35:21 +0200 Subject: hwmon: (s3c-hwmon) Disable build for S3C64xx The s3c-hwmon driver depends on the arch/arm implementation of the core ADC support for the chip. Since the S3C64xx version has not yet been merged disable building of the driver on S3C64xx for now. Signed-off-by: Mark Brown Cc: Ben Dooks Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6857560144bd..700e93adeb33 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -675,7 +675,7 @@ config SENSORS_SHT15 config SENSORS_S3C tristate "S3C24XX/S3C64XX Inbuilt ADC" - depends on ARCH_S3C2410 || ARCH_S3C64XX + depends on ARCH_S3C2410 help If you say yes here you get support for the on-board ADCs of the Samsung S3C24XX or S3C64XX series of SoC -- cgit v1.2.3 From 029214841ba8a1fae48a4a108d138b302d8a1afb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 5 Oct 2009 13:23:31 -0700 Subject: Staging: comedi: serial2002: fix include build issue Now that sched.h was removed from poll.h, serial2002.c needs to include it otherwise it does not build properly. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/serial2002.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/comedi/drivers/serial2002.c b/drivers/staging/comedi/drivers/serial2002.c index a21967983942..82aa86e718b2 100644 --- a/drivers/staging/comedi/drivers/serial2002.c +++ b/drivers/staging/comedi/drivers/serial2002.c @@ -35,6 +35,7 @@ Status: in development #include #include +#include #include #include -- cgit v1.2.3 From 7d230df37a7c08f01e57e1623e0706b3949751a2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 19 Sep 2009 10:50:02 -0700 Subject: Staging: remove agnx driver The agnx driver in the staging tree is broken, does not work, and development is dead. The developers have asked for it to be removed so it now is. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/agnx/Kconfig | 5 - drivers/staging/agnx/Makefile | 8 - drivers/staging/agnx/TODO | 22 - drivers/staging/agnx/agnx.h | 156 ------- drivers/staging/agnx/debug.h | 416 ------------------ drivers/staging/agnx/pci.c | 635 ---------------------------- drivers/staging/agnx/phy.c | 960 ------------------------------------------ drivers/staging/agnx/phy.h | 409 ------------------ drivers/staging/agnx/rf.c | 893 --------------------------------------- drivers/staging/agnx/sta.c | 218 ---------- drivers/staging/agnx/sta.h | 222 ---------- drivers/staging/agnx/table.c | 168 -------- drivers/staging/agnx/table.h | 10 - drivers/staging/agnx/xmit.c | 836 ------------------------------------ drivers/staging/agnx/xmit.h | 250 ----------- 17 files changed, 5211 deletions(-) delete mode 100644 drivers/staging/agnx/Kconfig delete mode 100644 drivers/staging/agnx/Makefile delete mode 100644 drivers/staging/agnx/TODO delete mode 100644 drivers/staging/agnx/agnx.h delete mode 100644 drivers/staging/agnx/debug.h delete mode 100644 drivers/staging/agnx/pci.c delete mode 100644 drivers/staging/agnx/phy.c delete mode 100644 drivers/staging/agnx/phy.h delete mode 100644 drivers/staging/agnx/rf.c delete mode 100644 drivers/staging/agnx/sta.c delete mode 100644 drivers/staging/agnx/sta.h delete mode 100644 drivers/staging/agnx/table.c delete mode 100644 drivers/staging/agnx/table.h delete mode 100644 drivers/staging/agnx/xmit.c delete mode 100644 drivers/staging/agnx/xmit.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 9a4dd5992f65..ab64971d3fe7 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -59,8 +59,6 @@ source "drivers/staging/echo/Kconfig" source "drivers/staging/poch/Kconfig" -source "drivers/staging/agnx/Kconfig" - source "drivers/staging/otus/Kconfig" source "drivers/staging/rt2860/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 104f2f8897ec..baffd0e96474 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_W35UND) += winbond/ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_POCH) += poch/ -obj-$(CONFIG_AGNX) += agnx/ obj-$(CONFIG_OTUS) += otus/ obj-$(CONFIG_RT2860) += rt2860/ obj-$(CONFIG_RT2870) += rt2870/ diff --git a/drivers/staging/agnx/Kconfig b/drivers/staging/agnx/Kconfig deleted file mode 100644 index 7f43549e36dd..000000000000 --- a/drivers/staging/agnx/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ -config AGNX - tristate "Wireless Airgo AGNX support" - depends on WLAN_80211 && MAC80211 - ---help--- - This is an experimental driver for Airgo AGNX00 wireless chip. diff --git a/drivers/staging/agnx/Makefile b/drivers/staging/agnx/Makefile deleted file mode 100644 index 1216564a312d..000000000000 --- a/drivers/staging/agnx/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -obj-$(CONFIG_AGNX) += agnx.o - -agnx-objs := rf.o \ - pci.o \ - xmit.o \ - table.o \ - sta.o \ - phy.o diff --git a/drivers/staging/agnx/TODO b/drivers/staging/agnx/TODO deleted file mode 100644 index 89bec74318aa..000000000000 --- a/drivers/staging/agnx/TODO +++ /dev/null @@ -1,22 +0,0 @@ -2008 7/18 - -The RX has can't receive OFDM packet correctly, -Guess it need be do RX calibrate. - - -before 2008 3/1 - -1: The RX get too much "CRC failed" pakets, it make the card work very unstable, -2: After running a while, the card will get infinity "RX Frame" and "Error" -interrupt, not know the root reason so far, try to fix it -3: Using two tx queue txd and txm but not only txm. -4: Set the hdr correctly. -5: Try to do recalibrate correvtly -6: To support G mode in future -7: Fix the mac address can't be readed and set correctly in BE machine. -8: Fix include and exclude FCS in promisous mode and manage mode -9: Using sta_notify to notice sta change -10: Turn on frame reception at the end of start -11: Guess the card support HW_MULTICAST_FILTER -12: The tx process should be implment atomic? -13: Using mac80211 function to control the TX&RX LED. diff --git a/drivers/staging/agnx/agnx.h b/drivers/staging/agnx/agnx.h deleted file mode 100644 index 3963d2597a11..000000000000 --- a/drivers/staging/agnx/agnx.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef AGNX_H_ -#define AGNX_H_ - -#include - -#include "xmit.h" - -#define PFX KBUILD_MODNAME ": " - -static inline u32 agnx_read32(void __iomem *mem_region, u32 offset) -{ - return ioread32(mem_region + offset); -} - -static inline void agnx_write32(void __iomem *mem_region, u32 offset, u32 val) -{ - iowrite32(val, mem_region + offset); -} - -/* static const struct ieee80211_rate agnx_rates_80211b[] = { */ -/* { .rate = 10, */ -/* .val = 0xa, */ -/* .flags = IEEE80211_RATE_CCK }, */ -/* { .rate = 20, */ -/* .val = 0x14, */ -/* .hw_value = -0x14, */ -/* .flags = IEEE80211_RATE_CCK_2 }, */ -/* { .rate = 55, */ -/* .val = 0x37, */ -/* .val2 = -0x37, */ -/* .flags = IEEE80211_RATE_CCK_2 }, */ -/* { .rate = 110, */ -/* .val = 0x6e, */ -/* .val2 = -0x6e, */ -/* .flags = IEEE80211_RATE_CCK_2 } */ -/* }; */ - - -static const struct ieee80211_rate agnx_rates_80211g[] = { -/* { .bitrate = 10, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ -/* { .bitrate = 20, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ -/* { .bitrate = 55, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ -/* { .bitrate = 110, .hw_value = 4, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, */ - { .bitrate = 10, .hw_value = 1, }, - { .bitrate = 20, .hw_value = 2, }, - { .bitrate = 55, .hw_value = 3, }, - { .bitrate = 110, .hw_value = 4,}, - - { .bitrate = 60, .hw_value = 0xB, }, - { .bitrate = 90, .hw_value = 0xF, }, - { .bitrate = 120, .hw_value = 0xA }, - { .bitrate = 180, .hw_value = 0xE, }, -/* { .bitrate = 240, .hw_value = 0xd, }, */ - { .bitrate = 360, .hw_value = 0xD, }, - { .bitrate = 480, .hw_value = 0x8, }, - { .bitrate = 540, .hw_value = 0xC, }, -}; - -static const struct ieee80211_channel agnx_channels[] = { - { .center_freq = 2412, .hw_value = 1, }, - { .center_freq = 2417, .hw_value = 2, }, - { .center_freq = 2422, .hw_value = 3, }, - { .center_freq = 2427, .hw_value = 4, }, - { .center_freq = 2432, .hw_value = 5, }, - { .center_freq = 2437, .hw_value = 6, }, - { .center_freq = 2442, .hw_value = 7, }, - { .center_freq = 2447, .hw_value = 8, }, - { .center_freq = 2452, .hw_value = 9, }, - { .center_freq = 2457, .hw_value = 10, }, - { .center_freq = 2462, .hw_value = 11, }, - { .center_freq = 2467, .hw_value = 12, }, - { .center_freq = 2472, .hw_value = 13, }, - { .center_freq = 2484, .hw_value = 14, }, -}; - -#define NUM_DRIVE_MODES 2 -/* Agnx operate mode */ -enum { - AGNX_MODE_80211A, - AGNX_MODE_80211A_OOB, - AGNX_MODE_80211A_MIMO, - AGNX_MODE_80211B_SHORT, - AGNX_MODE_80211B_LONG, - AGNX_MODE_80211G, - AGNX_MODE_80211G_OOB, - AGNX_MODE_80211G_MIMO, -}; - -enum { - AGNX_UNINIT, - AGNX_START, - AGNX_STOP, -}; - -struct agnx_priv { - struct pci_dev *pdev; - struct ieee80211_hw *hw; - - spinlock_t lock; - struct mutex mutex; - unsigned int init_status; - - void __iomem *ctl; /* pointer to base ram address */ - void __iomem *data; /* pointer to mem region #2 */ - - struct agnx_ring rx; - struct agnx_ring txm; - struct agnx_ring txd; - - /* Need volatile? */ - u32 irq_status; - - struct delayed_work periodic_work; /* Periodic tasks like recalibrate */ - struct ieee80211_low_level_stats stats; - - /* unsigned int phymode; */ - int mode; - int channel; - u8 bssid[ETH_ALEN]; - - u8 mac_addr[ETH_ALEN]; - u8 revid; - - struct ieee80211_supported_band band; -}; - - -#define AGNX_CHAINS_MAX 6 -#define AGNX_PERIODIC_DELAY 60000 /* unit: ms */ -#define LOCAL_STAID 0 /* the station entry for the card itself */ -#define BSSID_STAID 1 /* the station entry for the bsssid AP */ -#define spi_delay() udelay(40) -#define eeprom_delay() udelay(40) -#define routing_table_delay() udelay(50) - -/* PDU pool MEM region #2 */ -#define AGNX_PDUPOOL 0x40000 /* PDU pool */ -#define AGNX_PDUPOOL_SIZE 0x8000 /* PDU pool size*/ -#define AGNX_PDU_TX_WQ 0x41000 /* PDU list TX workqueue */ -#define AGNX_PDU_FREE 0x41800 /* Free Pool */ -#define PDU_SIZE 0x80 /* Free Pool node size */ -#define PDU_FREE_CNT 0xd0 /* Free pool node count */ - - -/* RF stuffs */ -extern void rf_chips_init(struct agnx_priv *priv); -extern void spi_rc_write(void __iomem *mem_region, u32 chip_ids, u32 sw); -extern void calibrate_oscillator(struct agnx_priv *priv); -extern void do_calibration(struct agnx_priv *priv); -extern void antenna_calibrate(struct agnx_priv *priv); -extern void __antenna_calibrate(struct agnx_priv *priv); -extern void print_offsets(struct agnx_priv *priv); -extern int agnx_set_channel(struct agnx_priv *priv, unsigned int channel); - - -#endif /* AGNX_H_ */ diff --git a/drivers/staging/agnx/debug.h b/drivers/staging/agnx/debug.h deleted file mode 100644 index 7947f327a214..000000000000 --- a/drivers/staging/agnx/debug.h +++ /dev/null @@ -1,416 +0,0 @@ -#ifndef AGNX_DEBUG_H_ -#define AGNX_DEBUG_H_ - -#include "agnx.h" -#include "phy.h" -#include "sta.h" -#include "xmit.h" - -#define AGNX_TRACE printk(KERN_ERR PFX "function:%s line:%d\n", __func__, __LINE__) - -#define PRINTK_LE16(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.4x\n", le16_to_cpu(var)) -#define PRINTK_LE32(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.8x\n", le32_to_cpu(var)) -#define PRINTK_U8(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.2x\n", var) -#define PRINTK_BE16(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.4x\n", be16_to_cpu(var)) -#define PRINTK_BE32(prefix, var) printk(KERN_DEBUG PFX #prefix ": " #var " 0x%.8x\n", be32_to_cpu(var)) -#define PRINTK_BITS(prefix, field) printk(KERN_DEBUG PFX #prefix ": " #field ": 0x%x\n", (reg & field) >> field##_SHIFT) - -static inline void agnx_bug(char *reason) -{ - printk(KERN_ERR PFX "%s\n", reason); - BUG(); -} - -static inline void agnx_print_desc(struct agnx_desc *desc) -{ - u32 reg = be32_to_cpu(desc->frag); - - PRINTK_BITS(DESC, PACKET_LEN); - - if (reg & FIRST_FRAG) { - PRINTK_BITS(DESC, FIRST_PACKET_MASK); - PRINTK_BITS(DESC, FIRST_RESERV2); - PRINTK_BITS(DESC, FIRST_TKIP_ERROR); - PRINTK_BITS(DESC, FIRST_TKIP_PACKET); - PRINTK_BITS(DESC, FIRST_RESERV1); - PRINTK_BITS(DESC, FIRST_FRAG_LEN); - } else { - PRINTK_BITS(DESC, SUB_RESERV2); - PRINTK_BITS(DESC, SUB_TKIP_ERROR); - PRINTK_BITS(DESC, SUB_TKIP_PACKET); - PRINTK_BITS(DESC, SUB_RESERV1); - PRINTK_BITS(DESC, SUB_FRAG_LEN); - } - - PRINTK_BITS(DESC, FIRST_FRAG); - PRINTK_BITS(DESC, LAST_FRAG); - PRINTK_BITS(DESC, OWNER); -} - - -static inline void dump_ieee80211b_phy_hdr(__be32 _11b0, __be32 _11b1) -{ - -} - -static inline void agnx_print_hdr(struct agnx_hdr *hdr) -{ - u32 reg; - int i; - - reg = be32_to_cpu(hdr->reg0); - PRINTK_BITS(HDR, RTS); - PRINTK_BITS(HDR, MULTICAST); - PRINTK_BITS(HDR, ACK); - PRINTK_BITS(HDR, TM); - PRINTK_BITS(HDR, RELAY); - PRINTK_BITS(HDR, REVISED_FCS); - PRINTK_BITS(HDR, NEXT_BUFFER_ADDR); - - reg = be32_to_cpu(hdr->reg1); - PRINTK_BITS(HDR, MAC_HDR_LEN); - PRINTK_BITS(HDR, DURATION_OVERIDE); - PRINTK_BITS(HDR, PHY_HDR_OVERIDE); - PRINTK_BITS(HDR, CRC_FAIL); - PRINTK_BITS(HDR, SEQUENCE_NUMBER); - PRINTK_BITS(HDR, BUFF_HEAD_ADDR); - - reg = be32_to_cpu(hdr->reg2); - PRINTK_BITS(HDR, PDU_COUNT); - PRINTK_BITS(HDR, WEP_KEY); - PRINTK_BITS(HDR, USES_WEP_KEY); - PRINTK_BITS(HDR, KEEP_ALIVE); - PRINTK_BITS(HDR, BUFF_TAIL_ADDR); - - reg = be32_to_cpu(hdr->reg3); - PRINTK_BITS(HDR, CTS_11G); - PRINTK_BITS(HDR, RTS_11G); - PRINTK_BITS(HDR, FRAG_SIZE); - PRINTK_BITS(HDR, PAYLOAD_LEN); - PRINTK_BITS(HDR, FRAG_NUM); - - reg = be32_to_cpu(hdr->reg4); - PRINTK_BITS(HDR, RELAY_STAID); - PRINTK_BITS(HDR, STATION_ID); - PRINTK_BITS(HDR, WORKQUEUE_ID); - - reg = be32_to_cpu(hdr->reg5); - /* printf the route flag */ - PRINTK_BITS(HDR, ROUTE_HOST); - PRINTK_BITS(HDR, ROUTE_CARD_CPU); - PRINTK_BITS(HDR, ROUTE_ENCRYPTION); - PRINTK_BITS(HDR, ROUTE_TX); - PRINTK_BITS(HDR, ROUTE_RX1); - PRINTK_BITS(HDR, ROUTE_RX2); - PRINTK_BITS(HDR, ROUTE_COMPRESSION); - - PRINTK_BE32(HDR, hdr->_11g0); - PRINTK_BE32(HDR, hdr->_11g1); - PRINTK_BE32(HDR, hdr->_11b0); - PRINTK_BE32(HDR, hdr->_11b1); - - dump_ieee80211b_phy_hdr(hdr->_11b0, hdr->_11b1); - - /* Fixme */ - for (i = 0; i < ARRAY_SIZE(hdr->mac_hdr); i++) { - if (i == 0) - printk(KERN_DEBUG PFX "IEEE80211 HDR: "); - printk("%.2x ", hdr->mac_hdr[i]); - if (i + 1 == ARRAY_SIZE(hdr->mac_hdr)) - printk("\n"); - } - - PRINTK_BE16(HDR, hdr->rts_duration); - PRINTK_BE16(HDR, hdr->last_duration); - PRINTK_BE16(HDR, hdr->sec_last_duration); - PRINTK_BE16(HDR, hdr->other_duration); - PRINTK_BE16(HDR, hdr->tx_other_duration); - PRINTK_BE16(HDR, hdr->last_11g_len); - PRINTK_BE16(HDR, hdr->other_11g_len); - PRINTK_BE16(HDR, hdr->last_11b_len); - PRINTK_BE16(HDR, hdr->other_11b_len); - - /* FIXME */ - reg = be16_to_cpu(hdr->reg6); - PRINTK_BITS(HDR, MBF); - PRINTK_BITS(HDR, RSVD4); - - PRINTK_BE16(HDR, hdr->rx_frag_stat); - - PRINTK_BE32(HDR, hdr->time_stamp); - PRINTK_BE32(HDR, hdr->phy_stats_hi); - PRINTK_BE32(HDR, hdr->phy_stats_lo); - PRINTK_BE32(HDR, hdr->mic_key0); - PRINTK_BE32(HDR, hdr->mic_key1); -} /* agnx_print_hdr */ - - -static inline void agnx_print_rx_hdr(struct agnx_hdr *hdr) -{ - agnx_print_hdr(hdr); - - PRINTK_BE16(HDR, hdr->rx.rx_packet_duration); - PRINTK_BE16(HDR, hdr->rx.replay_cnt); - - PRINTK_U8(HDR, hdr->rx_channel); -} - -static inline void agnx_print_tx_hdr(struct agnx_hdr *hdr) -{ - agnx_print_hdr(hdr); - - PRINTK_U8(HDR, hdr->tx.long_retry_limit); - PRINTK_U8(HDR, hdr->tx.short_retry_limit); - PRINTK_U8(HDR, hdr->tx.long_retry_cnt); - PRINTK_U8(HDR, hdr->tx.short_retry_cnt); - - PRINTK_U8(HDR, hdr->rx_channel); -} - -static inline void -agnx_print_sta_power(struct agnx_priv *priv, unsigned int sta_idx) -{ - struct agnx_sta_power power; - u32 reg; - - get_sta_power(priv, &power, sta_idx); - - reg = le32_to_cpu(power.reg); - PRINTK_BITS(STA_POWER, SIGNAL); - PRINTK_BITS(STA_POWER, RATE); - PRINTK_BITS(STA_POWER, TIFS); - PRINTK_BITS(STA_POWER, EDCF); - PRINTK_BITS(STA_POWER, CHANNEL_BOND); - PRINTK_BITS(STA_POWER, PHY_MODE); - PRINTK_BITS(STA_POWER, POWER_LEVEL); - PRINTK_BITS(STA_POWER, NUM_TRANSMITTERS); -} - -static inline void -agnx_print_sta_tx_wq(struct agnx_priv *priv, unsigned int sta_idx, unsigned int wq_idx) -{ - struct agnx_sta_tx_wq tx_wq; - u32 reg; - - get_sta_tx_wq(priv, &tx_wq, sta_idx, wq_idx); - - reg = le32_to_cpu(tx_wq.reg0); - PRINTK_BITS(STA_TX_WQ, TAIL_POINTER); - PRINTK_BITS(STA_TX_WQ, HEAD_POINTER_LOW); - - reg = le32_to_cpu(tx_wq.reg3); - PRINTK_BITS(STA_TX_WQ, HEAD_POINTER_HIGH); - PRINTK_BITS(STA_TX_WQ, ACK_POINTER_LOW); - - reg = le32_to_cpu(tx_wq.reg1); - PRINTK_BITS(STA_TX_WQ, ACK_POINTER_HIGH); - PRINTK_BITS(STA_TX_WQ, HEAD_TIMOUT_TAIL_PACK_CNT); - PRINTK_BITS(STA_TX_WQ, ACK_TIMOUT_TAIL_PACK_CNT); - - reg = le32_to_cpu(tx_wq.reg2); - PRINTK_BITS(STA_TX_WQ, HEAD_TIMOUT_WIN_LIM_BYTE_CNT); - PRINTK_BITS(STA_TX_WQ, HEAD_TIMOUT_WIN_LIM_FRAG_CNT); - PRINTK_BITS(STA_TX_WQ, WORK_QUEUE_ACK_TYPE); - PRINTK_BITS(STA_TX_WQ, WORK_QUEUE_VALID); -} - -static inline void agnx_print_sta_traffic(struct agnx_sta_traffic *traffic) -{ - u32 reg; - - reg = le32_to_cpu(traffic->reg0); - PRINTK_BITS(STA_TRAFFIC, ACK_TIMOUT_CNT); - PRINTK_BITS(STA_TRAFFIC, TRAFFIC_ACK_TYPE); - PRINTK_BITS(STA_TRAFFIC, NEW_PACKET); - PRINTK_BITS(STA_TRAFFIC, TRAFFIC_VALID); - PRINTK_BITS(STA_TRAFFIC, RX_HDR_DESC_POINTER); - - reg = le32_to_cpu(traffic->reg1); - PRINTK_BITS(STA_TRAFFIC, RX_PACKET_TIMESTAMP); - PRINTK_BITS(STA_TRAFFIC, TRAFFIC_RESERVED); - PRINTK_BITS(STA_TRAFFIC, SV); - PRINTK_BITS(STA_TRAFFIC, RX_SEQUENCE_NUM); - - PRINTK_LE32(STA_TRAFFIC, traffic->tx_replay_cnt_low); - - PRINTK_LE16(STA_TRAFFIC, traffic->tx_replay_cnt_high); - PRINTK_LE16(STA_TRAFFIC, traffic->rx_replay_cnt_high); - - PRINTK_LE32(STA_TRAFFIC, traffic->rx_replay_cnt_low); -} - -static inline void agnx_print_sta(struct agnx_priv *priv, unsigned int sta_idx) -{ - struct agnx_sta station; - struct agnx_sta *sta = &station; - u32 reg; - unsigned int i; - - get_sta(priv, sta, sta_idx); - - for (i = 0; i < 4; i++) - PRINTK_LE32(STA, sta->tx_session_keys[i]); - for (i = 0; i < 4; i++) - PRINTK_LE32(STA, sta->rx_session_keys[i]); - - reg = le32_to_cpu(sta->reg); - PRINTK_BITS(STA, ID_1); - PRINTK_BITS(STA, ID_0); - PRINTK_BITS(STA, ENABLE_CONCATENATION); - PRINTK_BITS(STA, ENABLE_DECOMPRESSION); - PRINTK_BITS(STA, STA_RESERVED); - PRINTK_BITS(STA, EAP); - PRINTK_BITS(STA, ED_NULL); - PRINTK_BITS(STA, ENCRYPTION_POLICY); - PRINTK_BITS(STA, DEFINED_KEY_ID); - PRINTK_BITS(STA, FIXED_KEY); - PRINTK_BITS(STA, KEY_VALID); - PRINTK_BITS(STA, STATION_VALID); - - PRINTK_LE32(STA, sta->tx_aes_blks_unicast); - PRINTK_LE32(STA, sta->rx_aes_blks_unicast); - - PRINTK_LE16(STA, sta->aes_format_err_unicast_cnt); - PRINTK_LE16(STA, sta->aes_replay_unicast); - - PRINTK_LE16(STA, sta->aes_decrypt_err_unicast); - PRINTK_LE16(STA, sta->aes_decrypt_err_default); - - PRINTK_LE16(STA, sta->single_retry_packets); - PRINTK_LE16(STA, sta->failed_tx_packets); - - PRINTK_LE16(STA, sta->muti_retry_packets); - PRINTK_LE16(STA, sta->ack_timeouts); - - PRINTK_LE16(STA, sta->frag_tx_cnt); - PRINTK_LE16(STA, sta->rts_brq_sent); - - PRINTK_LE16(STA, sta->tx_packets); - PRINTK_LE16(STA, sta->cts_back_timeout); - - PRINTK_LE32(STA, sta->phy_stats_high); - PRINTK_LE32(STA, sta->phy_stats_low); - - /* for (i = 0; i < 8; i++) */ - agnx_print_sta_traffic(sta->traffic + 0); - - PRINTK_LE16(STA, sta->traffic_class0_frag_success); - PRINTK_LE16(STA, sta->traffic_class1_frag_success); - PRINTK_LE16(STA, sta->traffic_class2_frag_success); - PRINTK_LE16(STA, sta->traffic_class3_frag_success); - PRINTK_LE16(STA, sta->traffic_class4_frag_success); - PRINTK_LE16(STA, sta->traffic_class5_frag_success); - PRINTK_LE16(STA, sta->traffic_class6_frag_success); - PRINTK_LE16(STA, sta->traffic_class7_frag_success); - - PRINTK_LE16(STA, sta->num_frag_non_prime_rates); - PRINTK_LE16(STA, sta->ack_timeout_non_prime_rates); -} - - -static inline void dump_ieee80211_hdr(struct ieee80211_hdr *hdr, char *tag) -{ - u16 fctl; - int hdrlen; - - fctl = le16_to_cpu(hdr->frame_control); - switch (fctl & IEEE80211_FCTL_FTYPE) { - case IEEE80211_FTYPE_DATA: - printk(PFX "%s DATA ", tag); - break; - case IEEE80211_FTYPE_CTL: - printk(PFX "%s CTL ", tag); - break; - case IEEE80211_FTYPE_MGMT: - printk(PFX "%s MGMT ", tag); - switch (fctl & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_ASSOC_REQ: - printk("SubType: ASSOC_REQ "); - break; - case IEEE80211_STYPE_ASSOC_RESP: - printk("SubType: ASSOC_RESP "); - break; - case IEEE80211_STYPE_REASSOC_REQ: - printk("SubType: REASSOC_REQ "); - break; - case IEEE80211_STYPE_REASSOC_RESP: - printk("SubType: REASSOC_RESP "); - break; - case IEEE80211_STYPE_PROBE_REQ: - printk("SubType: PROBE_REQ "); - break; - case IEEE80211_STYPE_PROBE_RESP: - printk("SubType: PROBE_RESP "); - break; - case IEEE80211_STYPE_BEACON: - printk("SubType: BEACON "); - break; - case IEEE80211_STYPE_ATIM: - printk("SubType: ATIM "); - break; - case IEEE80211_STYPE_DISASSOC: - printk("SubType: DISASSOC "); - break; - case IEEE80211_STYPE_AUTH: - printk("SubType: AUTH "); - break; - case IEEE80211_STYPE_DEAUTH: - printk("SubType: DEAUTH "); - break; - case IEEE80211_STYPE_ACTION: - printk("SubType: ACTION "); - break; - default: - printk("SubType: Unknow\n"); - } - break; - default: - printk(PFX "%s Packet type: Unknow\n", tag); - } - - hdrlen = ieee80211_hdrlen(fctl); - - if (hdrlen >= 4) - printk("FC=0x%04x DUR=0x%04x", - fctl, le16_to_cpu(hdr->duration_id)); - if (hdrlen >= 10) - printk(" A1=%pM", hdr->addr1); - if (hdrlen >= 16) - printk(" A2=%pM", hdr->addr2); - if (hdrlen >= 24) - printk(" A3=%pM", hdr->addr3); - if (hdrlen >= 30) - printk(" A4=%pM", hdr->addr4); - printk("\n"); -} - -static inline void dump_txm_registers(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - for (i = 0; i <= 0x1e8; i += 4) - printk(KERN_DEBUG PFX "TXM: %x---> 0x%.8x\n", i, ioread32(ctl + i)); -} -static inline void dump_rxm_registers(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - for (i = 0; i <= 0x108; i += 4) - printk(KERN_DEBUG PFX "RXM: %x---> 0x%.8x\n", i, ioread32(ctl + 0x2000 + i)); -} -static inline void dump_bm_registers(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - for (i = 0; i <= 0x90; i += 4) - printk(KERN_DEBUG PFX "BM: %x---> 0x%.8x\n", i, ioread32(ctl + 0x2c00 + i)); -} -static inline void dump_cir_registers(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - for (i = 0; i <= 0xb8; i += 4) - printk(KERN_DEBUG PFX "CIR: %x---> 0x%.8x\n", i, ioread32(ctl + 0x3000 + i)); -} - -#endif /* AGNX_DEBUG_H_ */ diff --git a/drivers/staging/agnx/pci.c b/drivers/staging/agnx/pci.c deleted file mode 100644 index 32b5489456a8..000000000000 --- a/drivers/staging/agnx/pci.c +++ /dev/null @@ -1,635 +0,0 @@ -/** - * Airgo MIMO wireless driver - * - * Copyright (c) 2007 Li YanBo - - * Thanks for Jeff Williams do reverse engineer - * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include "agnx.h" -#include "debug.h" -#include "xmit.h" -#include "phy.h" - -MODULE_AUTHOR("Li YanBo "); -MODULE_DESCRIPTION("Airgo MIMO PCI wireless driver"); -MODULE_LICENSE("GPL"); - -static struct pci_device_id agnx_pci_id_tbl[] __devinitdata = { - { PCI_DEVICE(0x17cb, 0x0001) }, /* Beklin F5d8010, Netgear WGM511 etc */ - { PCI_DEVICE(0x17cb, 0x0002) }, /* Netgear Wpnt511 */ - { 0 } -}; - -MODULE_DEVICE_TABLE(pci, agnx_pci_id_tbl); - - -static inline void agnx_interrupt_ack(struct agnx_priv *priv, u32 *reason) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - if (*reason & AGNX_STAT_RX) { - /* Mark complete RX */ - reg = ioread32(ctl + AGNX_CIR_RXCTL); - reg |= 0x4; - iowrite32(reg, ctl + AGNX_CIR_RXCTL); - /* disable Rx interrupt */ - } - if (*reason & AGNX_STAT_TX) { - reg = ioread32(ctl + AGNX_CIR_TXDCTL); - if (reg & 0x4) { - iowrite32(reg, ctl + AGNX_CIR_TXDCTL); - *reason |= AGNX_STAT_TXD; - } - reg = ioread32(ctl + AGNX_CIR_TXMCTL); - if (reg & 0x4) { - iowrite32(reg, ctl + AGNX_CIR_TXMCTL); - *reason |= AGNX_STAT_TXM; - } - } -#if 0 - if (*reason & AGNX_STAT_X) { - reg = ioread32(ctl + AGNX_INT_STAT); - iowrite32(reg, ctl + AGNX_INT_STAT); - /* FIXME reinit interrupt mask */ - reg = 0xc390bf9 & ~IRQ_TX_BEACON; - reg &= ~IRQ_TX_DISABLE; - iowrite32(reg, ctl + AGNX_INT_MASK); - iowrite32(0x800, ctl + AGNX_CIR_BLKCTL); - } -#endif -} /* agnx_interrupt_ack */ - -static irqreturn_t agnx_interrupt_handler(int irq, void *dev_id) -{ - struct ieee80211_hw *dev = dev_id; - struct agnx_priv *priv = dev->priv; - void __iomem *ctl = priv->ctl; - irqreturn_t ret = IRQ_NONE; - u32 irq_reason; - - spin_lock(&priv->lock); - -/* printk(KERN_ERR PFX "Get a interrupt %s\n", __func__); */ - - if (priv->init_status != AGNX_START) - goto out; - - /* FiXME Here has no lock, Is this will lead to race? */ - irq_reason = ioread32(ctl + AGNX_CIR_BLKCTL); - if (!(irq_reason & 0x7)) - goto out; - - ret = IRQ_HANDLED; - priv->irq_status = ioread32(ctl + AGNX_INT_STAT); - -/* printk(PFX "Interrupt reason is 0x%x\n", irq_reason); */ - /* Make sure the txm and txd flags don't conflict with other unknown - interrupt flag, maybe is not necessary */ - irq_reason &= 0xF; - - disable_rx_interrupt(priv); - /* TODO Make sure the card finished initialized */ - agnx_interrupt_ack(priv, &irq_reason); - - if (irq_reason & AGNX_STAT_RX) - handle_rx_irq(priv); - if (irq_reason & AGNX_STAT_TXD) - handle_txd_irq(priv); - if (irq_reason & AGNX_STAT_TXM) - handle_txm_irq(priv); - if (irq_reason & AGNX_STAT_X) - handle_other_irq(priv); - - enable_rx_interrupt(priv); -out: - spin_unlock(&priv->lock); - return ret; -} /* agnx_interrupt_handler */ - - -/* FIXME */ -static int agnx_tx(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - AGNX_TRACE; - return _agnx_tx(dev->priv, skb); -} /* agnx_tx */ - - -static int agnx_get_mac_address(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - /* Attention! directly read the MAC or other date from EEPROM will - lead to cardbus(WGM511) lock up when write to PM PLL register */ - reg = agnx_read32(ctl, 0x3544); - udelay(40); - reg = agnx_read32(ctl, 0x354c); - udelay(50); - /* Get the mac address */ - reg = agnx_read32(ctl, 0x3544); - udelay(40); - - /* HACK */ - reg = cpu_to_le32(reg); - priv->mac_addr[0] = ((u8 *)®)[2]; - priv->mac_addr[1] = ((u8 *)®)[3]; - reg = agnx_read32(ctl, 0x3548); - udelay(50); - *((u32 *)(priv->mac_addr + 2)) = cpu_to_le32(reg); - - if (!is_valid_ether_addr(priv->mac_addr)) { - printk(KERN_WARNING PFX "read mac %pM\n", priv->mac_addr); - printk(KERN_WARNING PFX "Invalid hwaddr! Using random hwaddr\n"); - random_ether_addr(priv->mac_addr); - } - - return 0; -} /* agnx_get_mac_address */ - -static int agnx_alloc_rings(struct agnx_priv *priv) -{ - unsigned int len; - AGNX_TRACE; - - /* Allocate RX/TXM/TXD rings info */ - priv->rx.size = AGNX_RX_RING_SIZE; - priv->txm.size = AGNX_TXM_RING_SIZE; - priv->txd.size = AGNX_TXD_RING_SIZE; - - len = priv->rx.size + priv->txm.size + priv->txd.size; - -/* priv->rx.info = kzalloc(sizeof(struct agnx_info) * len, GFP_KERNEL); */ - priv->rx.info = kzalloc(sizeof(struct agnx_info) * len, GFP_ATOMIC); - if (!priv->rx.info) - return -ENOMEM; - priv->txm.info = priv->rx.info + priv->rx.size; - priv->txd.info = priv->txm.info + priv->txm.size; - - /* Allocate RX/TXM/TXD descriptors */ - priv->rx.desc = pci_alloc_consistent(priv->pdev, sizeof(struct agnx_desc) * len, - &priv->rx.dma); - if (!priv->rx.desc) { - kfree(priv->rx.info); - return -ENOMEM; - } - - priv->txm.desc = priv->rx.desc + priv->rx.size; - priv->txm.dma = priv->rx.dma + sizeof(struct agnx_desc) * priv->rx.size; - priv->txd.desc = priv->txm.desc + priv->txm.size; - priv->txd.dma = priv->txm.dma + sizeof(struct agnx_desc) * priv->txm.size; - - return 0; -} /* agnx_alloc_rings */ - -static void rings_free(struct agnx_priv *priv) -{ - unsigned int len = priv->rx.size + priv->txm.size + priv->txd.size; - unsigned long flags; - AGNX_TRACE; - - spin_lock_irqsave(&priv->lock, flags); - kfree(priv->rx.info); - pci_free_consistent(priv->pdev, sizeof(struct agnx_desc) * len, - priv->rx.desc, priv->rx.dma); - spin_unlock_irqrestore(&priv->lock, flags); -} - -#if 0 -static void agnx_periodic_work_handler(struct work_struct *work) -{ - struct agnx_priv *priv = container_of(work, struct agnx_priv, periodic_work.work); -/* unsigned long flags; */ - unsigned long delay; - - /* fixme: using mutex?? */ -/* spin_lock_irqsave(&priv->lock, flags); */ - - /* TODO Recalibrate*/ -/* calibrate_oscillator(priv); */ -/* antenna_calibrate(priv); */ -/* agnx_send_packet(priv, 997); */ - /* FIXME */ -/* if (debug == 3) */ -/* delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY); */ -/* else */ - delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY); -/* delay = round_jiffies(HZ * 15); */ - - queue_delayed_work(priv->hw->workqueue, &priv->periodic_work, delay); - -/* spin_unlock_irqrestore(&priv->lock, flags); */ -} -#endif - -static int agnx_start(struct ieee80211_hw *dev) -{ - struct agnx_priv *priv = dev->priv; - /* unsigned long delay; */ - int err = 0; - AGNX_TRACE; - - err = agnx_alloc_rings(priv); - if (err) { - printk(KERN_ERR PFX "Can't alloc RX/TXM/TXD rings\n"); - goto out; - } - err = request_irq(priv->pdev->irq, &agnx_interrupt_handler, - IRQF_SHARED, "agnx_pci", dev); - if (err) { - printk(KERN_ERR PFX "Failed to register IRQ handler\n"); - rings_free(priv); - goto out; - } - -/* mdelay(500); */ - - might_sleep(); - agnx_hw_init(priv); - -/* mdelay(500); */ - might_sleep(); - - priv->init_status = AGNX_START; -/* INIT_DELAYED_WORK(&priv->periodic_work, agnx_periodic_work_handler); */ -/* delay = msecs_to_jiffies(AGNX_PERIODIC_DELAY); */ -/* queue_delayed_work(priv->hw->workqueue, &priv->periodic_work, delay); */ -out: - return err; -} /* agnx_start */ - -static void agnx_stop(struct ieee80211_hw *dev) -{ - struct agnx_priv *priv = dev->priv; - AGNX_TRACE; - - priv->init_status = AGNX_STOP; - /* make sure hardware will not generate irq */ - agnx_hw_reset(priv); - free_irq(priv->pdev->irq, dev); -/* flush_workqueue(priv->hw->workqueue); */ -/* cancel_delayed_work_sync(&priv->periodic_work); */ - unfill_rings(priv); - rings_free(priv); -} - -static int agnx_config(struct ieee80211_hw *dev, u32 changed) -{ - struct agnx_priv *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; - int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - AGNX_TRACE; - - spin_lock(&priv->lock); - /* FIXME need priv lock? */ - if (channel != priv->channel) { - priv->channel = channel; - agnx_set_channel(priv, priv->channel); - } - - spin_unlock(&priv->lock); - return 0; -} - -static void agnx_bss_info_changed(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf, - u32 changed) -{ - struct agnx_priv *priv = dev->priv; - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - - if (!(changed & BSS_CHANGED_BSSID)) - return; - - spin_lock(&priv->lock); - - if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) { - agnx_set_bssid(priv, conf->bssid); - memcpy(priv->bssid, conf->bssid, ETH_ALEN); - hash_write(priv, conf->bssid, BSSID_STAID); - sta_init(priv, BSSID_STAID); - /* FIXME needed? */ - sta_power_init(priv, BSSID_STAID); - agnx_write32(ctl, AGNX_BM_MTSM, 0xff & ~0x1); - } - spin_unlock(&priv->lock); -} /* agnx_bss_info_changed */ - - -static void agnx_configure_filter(struct ieee80211_hw *dev, - unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, struct dev_mc_list *mclist) -{ - unsigned int new_flags = 0; - - *total_flags = new_flags; - /* TODO */ -} - -static int agnx_add_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) -{ - struct agnx_priv *priv = dev->priv; - AGNX_TRACE; - - spin_lock(&priv->lock); - /* FIXME */ - if (priv->mode != NL80211_IFTYPE_MONITOR) - return -EOPNOTSUPP; - - switch (conf->type) { - case NL80211_IFTYPE_STATION: - priv->mode = conf->type; - break; - default: - return -EOPNOTSUPP; - } - - spin_unlock(&priv->lock); - - return 0; -} - -static void agnx_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) -{ - struct agnx_priv *priv = dev->priv; - AGNX_TRACE; - - /* TODO */ - priv->mode = NL80211_IFTYPE_MONITOR; -} - -static int agnx_get_stats(struct ieee80211_hw *dev, - struct ieee80211_low_level_stats *stats) -{ - struct agnx_priv *priv = dev->priv; - AGNX_TRACE; - spin_lock(&priv->lock); - /* TODO !! */ - memcpy(stats, &priv->stats, sizeof(*stats)); - spin_unlock(&priv->lock); - - return 0; -} - -static u64 agnx_get_tsft(struct ieee80211_hw *dev) -{ - void __iomem *ctl = ((struct agnx_priv *)dev->priv)->ctl; - u32 tsftl; - u64 tsft; - AGNX_TRACE; - - /* FIXME */ - tsftl = ioread32(ctl + AGNX_TXM_TIMESTAMPLO); - tsft = ioread32(ctl + AGNX_TXM_TIMESTAMPHI); - tsft <<= 32; - tsft |= tsftl; - - return tsft; -} - -static int agnx_get_tx_stats(struct ieee80211_hw *dev, - struct ieee80211_tx_queue_stats *stats) -{ - struct agnx_priv *priv = dev->priv; - AGNX_TRACE; - - /* FIXME now we just using txd queue, but should using txm queue too */ - stats[0].len = (priv->txd.idx - priv->txd.idx_sent) / 2; - stats[0].limit = priv->txd.size - 2; - stats[0].count = priv->txd.idx / 2; - - return 0; -} - -static struct ieee80211_ops agnx_ops = { - .tx = agnx_tx, - .start = agnx_start, - .stop = agnx_stop, - .add_interface = agnx_add_interface, - .remove_interface = agnx_remove_interface, - .config = agnx_config, - .bss_info_changed = agnx_bss_info_changed, - .configure_filter = agnx_configure_filter, - .get_stats = agnx_get_stats, - .get_tx_stats = agnx_get_tx_stats, - .get_tsf = agnx_get_tsft -}; - -static void __devexit agnx_pci_remove(struct pci_dev *pdev) -{ - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - struct agnx_priv *priv; - AGNX_TRACE; - - if (!dev) - return; - priv = dev->priv; - ieee80211_unregister_hw(dev); - pci_iounmap(pdev, priv->ctl); - pci_iounmap(pdev, priv->data); - pci_release_regions(pdev); - pci_disable_device(pdev); - - ieee80211_free_hw(dev); -} - -static int __devinit agnx_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct ieee80211_hw *dev; - struct agnx_priv *priv; - int err; - - err = pci_enable_device(pdev); - if (err) { - dev_err(&pdev->dev, "can't enable pci device\n"); - return err; - } - - err = pci_request_regions(pdev, "agnx-pci"); - if (err) { - dev_err(&pdev->dev, "can't reserve PCI resources\n"); - return err; - } - - if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) || - pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { - dev_err(&pdev->dev, "no suitable DMA available\n"); - err = -EIO; - goto err_free_reg; - } - - pci_set_master(pdev); - - dev = ieee80211_alloc_hw(sizeof(*priv), &agnx_ops); - if (!dev) { - dev_err(&pdev->dev, "ieee80211 alloc failed\n"); - err = -ENOMEM; - goto err_free_reg; - } - priv = dev->priv; - memset(priv, 0, sizeof(*priv)); - priv->mode = NL80211_IFTYPE_MONITOR; - priv->pdev = pdev; - priv->hw = dev; - spin_lock_init(&priv->lock); - priv->init_status = AGNX_UNINIT; - - priv->ctl = pci_iomap(pdev, 0, 0); -/* dev_dbg(&pdev->dev, "MEM1 mapped address is 0x%p\n", priv->ctl); */ - if (!priv->ctl) { - dev_err(&pdev->dev, "can't map device memory\n"); - err = -ENOMEM; - goto err_free_dev; - } - priv->data = pci_iomap(pdev, 1, 0); - if (!priv->data) { - dev_err(&pdev->dev, "can't map device memory\n"); - err = -ENOMEM; - goto err_iounmap2; - } - - pci_read_config_byte(pdev, PCI_REVISION_ID, &priv->revid); - - priv->band.channels = (struct ieee80211_channel *)agnx_channels; - priv->band.n_channels = ARRAY_SIZE(agnx_channels); - priv->band.bitrates = (struct ieee80211_rate *)agnx_rates_80211g; - priv->band.n_bitrates = ARRAY_SIZE(agnx_rates_80211g); - - /* Init ieee802.11 dev */ - SET_IEEE80211_DEV(dev, &pdev->dev); - pci_set_drvdata(pdev, dev); - dev->extra_tx_headroom = sizeof(struct agnx_hdr); - - /* FIXME It only include FCS in promious mode but not manage mode */ -/* dev->flags = IEEE80211_HW_RX_INCLUDES_FCS; */ - dev->channel_change_time = 5000; - dev->max_signal = 100; - /* FIXME */ - dev->queues = 1; - - agnx_get_mac_address(priv); - - SET_IEEE80211_PERM_ADDR(dev, priv->mac_addr); - -/* /\* FIXME *\/ */ -/* for (i = 1; i < NUM_DRIVE_MODES; i++) { */ -/* err = ieee80211_register_hwmode(dev, &priv->modes[i]); */ -/* if (err) { */ -/* printk(KERN_ERR PFX "Can't register hwmode\n"); */ -/* goto err_iounmap; */ -/* } */ -/* } */ - - priv->channel = 1; - dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - - err = ieee80211_register_hw(dev); - if (err) { - dev_err(&pdev->dev, "can't register hardware\n"); - goto err_iounmap; - } - - agnx_hw_reset(priv); - - dev_info(&pdev->dev, "%s: hwaddr %pM, Rev 0x%02x\n", - wiphy_name(dev->wiphy), - dev->wiphy->perm_addr, priv->revid); - return 0; - - err_iounmap: - pci_iounmap(pdev, priv->data); - - err_iounmap2: - pci_iounmap(pdev, priv->ctl); - - err_free_dev: - pci_set_drvdata(pdev, NULL); - ieee80211_free_hw(dev); - - err_free_reg: - pci_release_regions(pdev); - - pci_disable_device(pdev); - return err; -} /* agnx_pci_probe*/ - -#ifdef CONFIG_PM - -static int agnx_pci_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - AGNX_TRACE; - - ieee80211_stop_queues(dev); - agnx_stop(dev); - - pci_save_state(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; -} - -static int agnx_pci_resume(struct pci_dev *pdev) -{ - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - AGNX_TRACE; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - - agnx_start(dev); - ieee80211_wake_queues(dev); - - return 0; -} - -#else - -#define agnx_pci_suspend NULL -#define agnx_pci_resume NULL - -#endif /* CONFIG_PM */ - - -static struct pci_driver agnx_pci_driver = { - .name = "agnx-pci", - .id_table = agnx_pci_id_tbl, - .probe = agnx_pci_probe, - .remove = __devexit_p(agnx_pci_remove), - .suspend = agnx_pci_suspend, - .resume = agnx_pci_resume, -}; - -static int __init agnx_pci_init(void) -{ - AGNX_TRACE; - return pci_register_driver(&agnx_pci_driver); -} - -static void __exit agnx_pci_exit(void) -{ - AGNX_TRACE; - pci_unregister_driver(&agnx_pci_driver); -} - - -module_init(agnx_pci_init); -module_exit(agnx_pci_exit); diff --git a/drivers/staging/agnx/phy.c b/drivers/staging/agnx/phy.c deleted file mode 100644 index ec1ca86fa0c4..000000000000 --- a/drivers/staging/agnx/phy.c +++ /dev/null @@ -1,960 +0,0 @@ -/** - * Airgo MIMO wireless driver - * - * Copyright (c) 2007 Li YanBo - - * Thanks for Jeff Williams do reverse engineer - * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include "agnx.h" -#include "debug.h" -#include "phy.h" -#include "table.h" -#include "sta.h" -#include "xmit.h" - -u8 read_from_eeprom(struct agnx_priv *priv, u16 address) -{ - void __iomem *ctl = priv->ctl; - struct agnx_eeprom cmd; - u32 reg; - - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd = EEPROM_CMD_READ << AGNX_EEPROM_COMMAND_SHIFT; - cmd.address = address; - /* Verify that the Status bit is clear */ - /* Read Command and Address are written to the Serial Interface */ - iowrite32(*(__le32 *)&cmd, ctl + AGNX_CIR_SERIALITF); - /* Wait for the Status bit to clear again */ - eeprom_delay(); - /* Read from Data */ - reg = ioread32(ctl + AGNX_CIR_SERIALITF); - - cmd = *(struct agnx_eeprom *)® - - return cmd.data; -} - -static int card_full_reset(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); - agnx_write32(ctl, AGNX_CIR_BLKCTL, 0x80); - reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); - return 0; -} - -inline void enable_power_saving(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg &= ~0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); -} - -inline void disable_power_saving(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); -} - - -void disable_receiver(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - - /* FIXME Disable the receiver */ - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x0); - /* Set gain control reset */ - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); - /* Reset gain control reset */ - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); -} - - -/* Fixme this shoule be disable RX, above is enable RX */ -void enable_receiver(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - - /* Set adaptive gain control discovery mode */ - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - /* Set gain control reset */ - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); - /* Clear gain control reset */ - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); -} - -static void mac_address_set(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u8 *mac_addr = priv->mac_addr; - u32 reg; - - /* FIXME */ - reg = (mac_addr[0] << 24) | (mac_addr[1] << 16) | mac_addr[2] << 8 | mac_addr[3]; - iowrite32(reg, ctl + AGNX_RXM_MACHI); - reg = (mac_addr[4] << 8) | mac_addr[5]; - iowrite32(reg, ctl + AGNX_RXM_MACLO); -} - -static void receiver_bssid_set(struct agnx_priv *priv, const u8 *bssid) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - disable_receiver(priv); - /* FIXME */ - reg = bssid[0] << 24 | (bssid[1] << 16) | (bssid[2] << 8) | bssid[3]; - iowrite32(reg, ctl + AGNX_RXM_BSSIDHI); - reg = (bssid[4] << 8) | bssid[5]; - iowrite32(reg, ctl + AGNX_RXM_BSSIDLO); - - /* Enable the receiver */ - enable_receiver(priv); - - /* Clear the TSF */ -/* agnx_write32(ctl, AGNX_TXM_TSFLO, 0x0); */ -/* agnx_write32(ctl, AGNX_TXM_TSFHI, 0x0); */ - /* Clear the TBTT */ - agnx_write32(ctl, AGNX_TXM_TBTTLO, 0x0); - agnx_write32(ctl, AGNX_TXM_TBTTHI, 0x0); - disable_receiver(priv); -} /* receiver_bssid_set */ - -static void band_management_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - void __iomem *data = priv->data; - u32 reg; - int i; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_BM_TXWADDR, AGNX_PDU_TX_WQ); - agnx_write32(ctl, AGNX_CIR_ADDRWIN, 0x0); - memset_io(data + AGNX_PDUPOOL, 0x0, AGNX_PDUPOOL_SIZE); - agnx_write32(ctl, AGNX_BM_BMCTL, 0x200); - - agnx_write32(ctl, AGNX_BM_CIPDUWCNT, 0x40); - agnx_write32(ctl, AGNX_BM_SPPDUWCNT, 0x2); - agnx_write32(ctl, AGNX_BM_RFPPDUWCNT, 0x0); - agnx_write32(ctl, AGNX_BM_RHPPDUWCNT, 0x22); - - /* FIXME Initialize the Free Pool Linked List */ - /* 1. Write the Address of the Next Node ((0x41800 + node*size)/size) - to the first word of each node. */ - for (i = 0; i < PDU_FREE_CNT; i++) { - iowrite32((AGNX_PDU_FREE + (i+1)*PDU_SIZE)/PDU_SIZE, - data + AGNX_PDU_FREE + (PDU_SIZE * i)); - /* The last node should be set to 0x0 */ - if ((i + 1) == PDU_FREE_CNT) - memset_io(data + AGNX_PDU_FREE + (PDU_SIZE * i), - 0x0, PDU_SIZE); - } - - /* Head is First Pool address (0x41800) / size (0x80) */ - agnx_write32(ctl, AGNX_BM_FPLHP, AGNX_PDU_FREE/PDU_SIZE); - /* Tail is Last Pool Address (0x47f80) / size (0x80) */ - agnx_write32(ctl, AGNX_BM_FPLTP, 0x47f80/PDU_SIZE); - /* Count is Number of Nodes in the Pool (0xd0) */ - agnx_write32(ctl, AGNX_BM_FPCNT, PDU_FREE_CNT); - - /* Start all workqueue */ - agnx_write32(ctl, AGNX_BM_CIWQCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_CPULWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_CPUHWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_CPUTXWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_CPURXWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_SPRXWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_SPTXWCTL, 0x80000); - agnx_write32(ctl, AGNX_BM_RFPWCTL, 0x80000); - - /* Enable the Band Management */ - reg = agnx_read32(ctl, AGNX_BM_BMCTL); - reg |= 0x1; - agnx_write32(ctl, AGNX_BM_BMCTL, reg); -} /* band_managment_init */ - - -static void system_itf_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x0); - agnx_write32(ctl, AGNX_PM_TESTPHY, 0x11e143a); - - if (priv->revid == 0) { - reg = agnx_read32(ctl, AGNX_SYSITF_SYSMODE); - reg |= 0x11; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, reg); - } - /* ??? What is that means? it should difference for differice type - of cards */ - agnx_write32(ctl, AGNX_CIR_SERIALITF, 0xfff81006); - - agnx_write32(ctl, AGNX_SYSITF_GPIOIN, 0x1f0000); - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x5); - reg = agnx_read32(ctl, AGNX_SYSITF_GPIOIN); -} - -static void encryption_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_ENCRY_WEPKEY0, 0x0); - agnx_write32(ctl, AGNX_ENCRY_WEPKEY1, 0x0); - agnx_write32(ctl, AGNX_ENCRY_WEPKEY2, 0x0); - agnx_write32(ctl, AGNX_ENCRY_WEPKEY3, 0x0); - agnx_write32(ctl, AGNX_ENCRY_CCMRECTL, 0x8); -} - -static void tx_management_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - void __iomem *data = priv->data; - u32 reg; - AGNX_TRACE; - - /* Fill out the ComputationalEngineLookupTable - * starting at memory #2 offset 0x800 - */ - tx_engine_lookup_tbl_init(priv); - memset_io(data + 0x1000, 0, 0xfe0); - /* Enable Transmission Management Functions */ - agnx_write32(ctl, AGNX_TXM_ETMF, 0x3ff); - /* Write 0x3f to Transmission Template */ - agnx_write32(ctl, AGNX_TXM_TXTEMP, 0x3f); - - if (priv->revid >= 2) - agnx_write32(ctl, AGNX_TXM_SIFSPIFS, 0x1e140a0b); - else - agnx_write32(ctl, AGNX_TXM_SIFSPIFS, 0x1e190a0b); - - reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); - reg &= 0xff00; - reg |= 0xb; - agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); - reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); - reg &= 0xffff00ff; - reg |= 0xa00; - agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); - /* Enable TIFS */ - agnx_write32(ctl, AGNX_TXM_CTL, 0x40000); - - reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); - reg &= 0xff00ffff; - reg |= 0x510000; - agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); - reg = agnx_read32(ctl, AGNX_TXM_PROBDELAY); - reg &= 0xff00ffff; - agnx_write32(ctl, AGNX_TXM_PROBDELAY, reg); - reg = agnx_read32(ctl, AGNX_TXM_TIFSEIFS); - reg &= 0x00ffffff; - reg |= 0x1c000000; - agnx_write32(ctl, AGNX_TXM_TIFSEIFS, reg); - reg = agnx_read32(ctl, AGNX_TXM_PROBDELAY); - reg &= 0x00ffffff; - reg |= 0x01000000; - agnx_write32(ctl, AGNX_TXM_PROBDELAY, reg); - - /* # Set DIF 0-1,2-3,4-5,6-7 to defaults */ - agnx_write32(ctl, AGNX_TXM_DIF01, 0x321d321d); - agnx_write32(ctl, AGNX_TXM_DIF23, 0x321d321d); - agnx_write32(ctl, AGNX_TXM_DIF45, 0x321d321d); - agnx_write32(ctl, AGNX_TXM_DIF67, 0x321d321d); - - /* Max Ack timeout limit */ - agnx_write32(ctl, AGNX_TXM_MAXACKTIM, 0x1e19); - /* Max RX Data Timeout count, */ - reg = agnx_read32(ctl, AGNX_TXM_MAXRXTIME); - reg &= 0xffff0000; - reg |= 0xff; - agnx_write32(ctl, AGNX_TXM_MAXRXTIME, reg); - - /* CF poll RX Timeout count */ - reg = agnx_read32(ctl, AGNX_TXM_CFPOLLRXTIM); - reg &= 0xffff; - reg |= 0xff0000; - agnx_write32(ctl, AGNX_TXM_CFPOLLRXTIM, reg); - - /* Max Timeout Exceeded count, */ - reg = agnx_read32(ctl, AGNX_TXM_MAXTIMOUT); - reg &= 0xff00ffff; - reg |= 0x190000; - agnx_write32(ctl, AGNX_TXM_MAXTIMOUT, reg); - - /* CF ack timeout limit for 11b */ - reg = agnx_read32(ctl, AGNX_TXM_CFACKT11B); - reg &= 0xff00; - reg |= 0x1e; - agnx_write32(ctl, AGNX_TXM_CFACKT11B, reg); - - /* Max CF Poll Timeout Count */ - reg = agnx_read32(ctl, AGNX_TXM_CFPOLLRXTIM); - reg &= 0xffff0000; - reg |= 0x19; - agnx_write32(ctl, AGNX_TXM_CFPOLLRXTIM, reg); - /* CF Poll RX Timeout Count */ - reg = agnx_read32(ctl, AGNX_TXM_CFPOLLRXTIM); - reg &= 0xffff0000; - reg |= 0x1e; - agnx_write32(ctl, AGNX_TXM_CFPOLLRXTIM, reg); - - /* # write default to */ - /* 1. Schedule Empty Count */ - agnx_write32(ctl, AGNX_TXM_SCHEMPCNT, 0x5); - /* 2. CFP Period Count */ - agnx_write32(ctl, AGNX_TXM_CFPERCNT, 0x1); - /* 3. CFP MDV */ - agnx_write32(ctl, AGNX_TXM_CFPMDV, 0x10000); - - /* Probe Delay */ - reg = agnx_read32(ctl, AGNX_TXM_PROBDELAY); - reg &= 0xffff0000; - reg |= 0x400; - agnx_write32(ctl, AGNX_TXM_PROBDELAY, reg); - - /* Max CCA count Slot */ - reg = agnx_read32(ctl, AGNX_TXM_MAXCCACNTSLOT); - reg &= 0xffff00ff; - reg |= 0x900; - agnx_write32(ctl, AGNX_TXM_MAXCCACNTSLOT, reg); - - /* Slot limit/1 msec Limit */ - reg = agnx_read32(ctl, AGNX_TXM_SLOTLIMIT); - reg &= 0xff00ffff; - reg |= 0x140077; - agnx_write32(ctl, AGNX_TXM_SLOTLIMIT, reg); - - /* # Set CW #(0-7) to default */ - agnx_write32(ctl, AGNX_TXM_CW0, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW1, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW2, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW3, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW4, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW5, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW6, 0xff0007); - agnx_write32(ctl, AGNX_TXM_CW7, 0xff0007); - - /* # Set Short/Long limit #(0-7) to default */ - agnx_write32(ctl, AGNX_TXM_SLBEALIM0, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM1, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM2, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM3, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM4, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM5, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM6, 0xa000a); - agnx_write32(ctl, AGNX_TXM_SLBEALIM7, 0xa000a); - - reg = agnx_read32(ctl, AGNX_TXM_CTL); - reg |= 0x1400; - agnx_write32(ctl, AGNX_TXM_CTL, reg); - /* Wait for bit 0 in Control Reg to clear */ - udelay(80); - reg = agnx_read32(ctl, AGNX_TXM_CTL); - /* Or 0x18000 to Control reg */ - reg = agnx_read32(ctl, AGNX_TXM_CTL); - reg |= 0x18000; - agnx_write32(ctl, AGNX_TXM_CTL, reg); - /* Wait for bit 0 in Control Reg to clear */ - udelay(80); - reg = agnx_read32(ctl, AGNX_TXM_CTL); - - /* Set Listen Interval Count to default */ - agnx_write32(ctl, AGNX_TXM_LISINTERCNT, 0x1); - /* Set DTIM period count to default */ - agnx_write32(ctl, AGNX_TXM_DTIMPERICNT, 0x2000); -} /* tx_management_init */ - -static void rx_management_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - - /* Initialize the Routing Table */ - routing_table_init(priv); - - if (priv->revid >= 3) { - agnx_write32(ctl, 0x2074, 0x1f171710); - agnx_write32(ctl, 0x2078, 0x10100d0d); - agnx_write32(ctl, 0x207c, 0x11111010); - } else { - agnx_write32(ctl, AGNX_RXM_DELAY11, 0x0); - } - agnx_write32(ctl, AGNX_RXM_REQRATE, 0x8195e00); -} - - -static void agnx_timer_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - AGNX_TRACE; - -/* /\* Write 0x249f00 (tick duration?) to Timer 1 *\/ */ -/* agnx_write32(ctl, AGNX_TIMCTL_TIMER1, 0x249f00); */ -/* /\* Write 0xe2 to Timer 1 Control *\/ */ -/* agnx_write32(ctl, AGNX_TIMCTL_TIM1CTL, 0xe2); */ - - /* Write 0x249f00 (tick duration?) to Timer 1 */ - agnx_write32(ctl, AGNX_TIMCTL_TIMER1, 0x0); - /* Write 0xe2 to Timer 1 Control */ - agnx_write32(ctl, AGNX_TIMCTL_TIM1CTL, 0x0); - - iowrite32(0xFFFFFFFF, priv->ctl + AGNX_TXM_BEACON_CTL); -} - -static void power_manage_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_PM_MACMSW, 0x1f); - agnx_write32(ctl, AGNX_PM_RFCTL, 0x1f); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg &= 0xf00f; - reg |= 0xa0; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - - if (priv->revid >= 3) { - reg = agnx_read32(ctl, AGNX_PM_SOFTRST); - reg |= 0x18; - agnx_write32(ctl, AGNX_PM_SOFTRST, reg); - } -} /* power_manage_init */ - - -static void gain_ctlcnt_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_GCR_TRACNT5, 0x119); - agnx_write32(ctl, AGNX_GCR_TRACNT6, 0x118); - agnx_write32(ctl, AGNX_GCR_TRACNT7, 0x117); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg &= ~0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - - agnx_write32(ctl, AGNX_CIR_ADDRWIN, 0x0); - - /* FIXME Write the initial Station Descriptor for the card */ - sta_init(priv, LOCAL_STAID); - sta_init(priv, BSSID_STAID); - - /* Enable staion 0 and 1 can do TX */ - /* It seemed if we set other bit to 1 the bit 0 will - be auto change to 0 */ - agnx_write32(ctl, AGNX_BM_TXTOPEER, 0x2 | 0x1); -/* agnx_write32(ctl, AGNX_BM_TXTOPEER, 0x1); */ -} /* gain_ctlcnt_init */ - - -static void phy_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - void __iomem *data = priv->data; - u32 reg; - AGNX_TRACE; - - /* Load InitialGainTable */ - gain_table_init(priv); - - agnx_write32(ctl, AGNX_CIR_ADDRWIN, 0x2000000); - - /* Clear the following offsets in Memory Range #2: */ - memset_io(data + 0x5040, 0, 0xa * 4); - memset_io(data + 0x5080, 0, 0xa * 4); - memset_io(data + 0x50c0, 0, 0xa * 4); - memset_io(data + 0x5400, 0, 0x80 * 4); - memset_io(data + 0x6000, 0, 0x280 * 4); - memset_io(data + 0x7000, 0, 0x280 * 4); - memset_io(data + 0x8000, 0, 0x280 * 4); - - /* Initialize the Following Registers According to PCI Revision ID */ - if (priv->revid == 0) { - /* fixme the part hasn't been update but below has been update - based on WGM511 */ - agnx_write32(ctl, AGNX_ACI_LEN, 0xf); - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x1d); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x3); - agnx_write32(ctl, AGNX_ACI_AICCHA0OVE, 0x11); - agnx_write32(ctl, AGNX_ACI_AICCHA1OVE, 0x0); - agnx_write32(ctl, AGNX_GCR_THD0A, 0x64); - agnx_write32(ctl, AGNX_GCR_THD0AL, 0x4b); - agnx_write32(ctl, AGNX_GCR_THD0B, 0x4b); - agnx_write32(ctl, AGNX_GCR_DUNSAT, 0x14); - agnx_write32(ctl, AGNX_GCR_DSAT, 0x24); - agnx_write32(ctl, AGNX_GCR_DFIRCAL, 0x8); - agnx_write32(ctl, AGNX_GCR_DGCTL11A, 0x1a); - agnx_write32(ctl, AGNX_GCR_DGCTL11B, 0x3); - agnx_write32(ctl, AGNX_GCR_GAININIT, 0xd); - agnx_write32(ctl, AGNX_GCR_THNOSIG, 0x1); - agnx_write32(ctl, AGNX_GCR_COARSTEP, 0x7); - agnx_write32(ctl, AGNX_GCR_SIFST11A, 0x28); - agnx_write32(ctl, AGNX_GCR_SIFST11B, 0x28); - reg = agnx_read32(ctl, AGNX_GCR_CWDETEC); - reg |= 0x1; - agnx_write32(ctl, AGNX_GCR_CWDETEC, reg); - agnx_write32(ctl, AGNX_GCR_0X38, 0x1e); - agnx_write32(ctl, AGNX_GCR_BOACT, 0x26); - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - agnx_write32(ctl, AGNX_GCR_NLISTANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NACTIANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NMEASANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NCAPTANT, 0x3); - agnx_write32(ctl, AGNX_GCR_THCAP11A, 0x0); - agnx_write32(ctl, AGNX_GCR_THCAP11B, 0x0); - agnx_write32(ctl, AGNX_GCR_THCAPRX11A, 0x0); - agnx_write32(ctl, AGNX_GCR_THCAPRX11B, 0x0); - agnx_write32(ctl, AGNX_GCR_THLEVDRO, 0x10); - agnx_write32(ctl, AGNX_GCR_MAXRXTIME11A, 0x1); - agnx_write32(ctl, AGNX_GCR_MAXRXTIME11B, 0x1); - agnx_write32(ctl, AGNX_GCR_CORRTIME, 0x190); - agnx_write32(ctl, AGNX_GCR_SIGHTH, 0x78); - agnx_write32(ctl, AGNX_GCR_SIGLTH, 0x1c); - agnx_write32(ctl, AGNX_GCR_CORRDROP, 0x0); - agnx_write32(ctl, AGNX_GCR_THCD, 0x0); - agnx_write32(ctl, AGNX_GCR_MAXPOWDIFF, 0x1); - agnx_write32(ctl, AGNX_GCR_TESTBUS, 0x0); - agnx_write32(ctl, AGNX_GCR_ANTCFG, 0x1f); - agnx_write32(ctl, AGNX_GCR_THJUMP, 0x14); - agnx_write32(ctl, AGNX_GCR_THPOWER, 0x0); - agnx_write32(ctl, AGNX_GCR_THPOWCLIP, 0x30); - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 0x32); - agnx_write32(ctl, AGNX_GCR_THRX11BPOWMIN, 0x19); - agnx_write32(ctl, AGNX_GCR_0X14c, 0x0); - agnx_write32(ctl, AGNX_GCR_0X150, 0x0); - agnx_write32(ctl, 0x9400, 0x0); - agnx_write32(ctl, 0x940c, 0x6ff); - agnx_write32(ctl, 0x9428, 0xa0); - agnx_write32(ctl, 0x9434, 0x0); - agnx_write32(ctl, 0x9c04, 0x15); - agnx_write32(ctl, 0x9c0c, 0x7f); - agnx_write32(ctl, 0x9c34, 0x0); - agnx_write32(ctl, 0xc000, 0x38d); - agnx_write32(ctl, 0x14018, 0x0); - agnx_write32(ctl, 0x16000, 0x1); - agnx_write32(ctl, 0x11004, 0x0); - agnx_write32(ctl, 0xec54, 0xa); - agnx_write32(ctl, 0xec1c, 0x5); - } else if (priv->revid > 0) { - agnx_write32(ctl, AGNX_ACI_LEN, 0xf); - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x21); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - agnx_write32(ctl, AGNX_ACI_AICCHA0OVE, 0x11); - agnx_write32(ctl, AGNX_ACI_AICCHA1OVE, 0x0); - agnx_write32(ctl, AGNX_GCR_DUNSAT, 0x14); - agnx_write32(ctl, AGNX_GCR_DSAT, 0x24); - agnx_write32(ctl, AGNX_GCR_DFIRCAL, 0x8); - agnx_write32(ctl, AGNX_GCR_DGCTL11A, 0x1a); - agnx_write32(ctl, AGNX_GCR_DGCTL11B, 0x3); - agnx_write32(ctl, AGNX_GCR_GAININIT, 0xd); - agnx_write32(ctl, AGNX_GCR_THNOSIG, 0x1); - agnx_write32(ctl, AGNX_GCR_COARSTEP, 0x7); - agnx_write32(ctl, AGNX_GCR_SIFST11A, 0x28); - agnx_write32(ctl, AGNX_GCR_SIFST11B, 0x28); - agnx_write32(ctl, AGNX_GCR_CWDETEC, 0x0); - agnx_write32(ctl, AGNX_GCR_0X38, 0x1e); -/* agnx_write32(ctl, AGNX_GCR_BOACT, 0x26);*/ - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - - agnx_write32(ctl, AGNX_GCR_THCAP11A, 0x32); - agnx_write32(ctl, AGNX_GCR_THCAP11B, 0x32); - agnx_write32(ctl, AGNX_GCR_THCAPRX11A, 0x32); - agnx_write32(ctl, AGNX_GCR_THCAPRX11B, 0x32); - agnx_write32(ctl, AGNX_GCR_THLEVDRO, 0x10); - agnx_write32(ctl, AGNX_GCR_MAXRXTIME11A, 0x1ad); - agnx_write32(ctl, AGNX_GCR_MAXRXTIME11B, 0xa10); - agnx_write32(ctl, AGNX_GCR_CORRTIME, 0x190); - agnx_write32(ctl, AGNX_GCR_CORRDROP, 0x0); - agnx_write32(ctl, AGNX_GCR_THCD, 0x0); - agnx_write32(ctl, AGNX_GCR_THCS, 0x0); - agnx_write32(ctl, AGNX_GCR_MAXPOWDIFF, 0x4); - agnx_write32(ctl, AGNX_GCR_TESTBUS, 0x0); - agnx_write32(ctl, AGNX_GCR_THJUMP, 0x1e); - agnx_write32(ctl, AGNX_GCR_THPOWER, 0x0); - agnx_write32(ctl, AGNX_GCR_THPOWCLIP, 0x2a); - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 0x3c); - agnx_write32(ctl, AGNX_GCR_THRX11BPOWMIN, 0x19); - agnx_write32(ctl, AGNX_GCR_0X14c, 0x0); - agnx_write32(ctl, AGNX_GCR_0X150, 0x0); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - agnx_write32(ctl, AGNX_GCR_WATCHDOG, 0x37); - agnx_write32(ctl, 0x9400, 0x0); - agnx_write32(ctl, 0x940c, 0x6ff); - agnx_write32(ctl, 0x9428, 0xa0); - agnx_write32(ctl, 0x9434, 0x0); - agnx_write32(ctl, 0x9c04, 0x15); - agnx_write32(ctl, 0x9c0c, 0x7f); - agnx_write32(ctl, 0x9c34, 0x0); - agnx_write32(ctl, 0xc000, 0x38d); - agnx_write32(ctl, 0x14014, 0x1000); - agnx_write32(ctl, 0x14018, 0x0); - agnx_write32(ctl, 0x16000, 0x1); - agnx_write32(ctl, 0x11004, 0x0); - agnx_write32(ctl, 0xec54, 0xa); - agnx_write32(ctl, 0xec1c, 0x50); - } else if (priv->revid > 1) { - reg = agnx_read32(ctl, 0xec18); - reg |= 0x8; - agnx_write32(ctl, 0xec18, reg); - } - - /* Write the TX Fir Coefficient Table */ - tx_fir_table_init(priv); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg &= ~0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - reg = agnx_read32(ctl, AGNX_PM_PLLCTL); - reg |= 0x1; - agnx_write32(ctl, AGNX_PM_PLLCTL, reg); - -/* reg = agnx_read32(ctl, 0x1a030); */ -/* reg &= ~0x4; */ -/* agnx_write32(ctl, 0x1a030, reg); */ - - agnx_write32(ctl, AGNX_GCR_TRACNT4, 0x113); -} /* phy_init */ - -static void chip_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - band_management_init(priv); - - rf_chips_init(priv); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - - /* Initialize the PHY */ - phy_init(priv); - - encryption_init(priv); - - tx_management_init(priv); - - rx_management_init(priv); - - power_manage_init(priv); - - /* Initialize the Timers */ - agnx_timer_init(priv); - - /* Write 0xc390bf9 to Interrupt Mask (Disable TX) */ - reg = 0xc390bf9 & ~IRQ_TX_BEACON; - reg &= ~IRQ_TX_DISABLE; - agnx_write32(ctl, AGNX_INT_MASK, reg); - - reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); - reg |= 0x800; - agnx_write32(ctl, AGNX_CIR_BLKCTL, reg); - - /* set it when need get multicast enable? */ - agnx_write32(ctl, AGNX_BM_MTSM, 0xff); -} /* chip_init */ - - -static inline void set_promis_and_managed(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x10 | 0x2); - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x10 | 0x2); -} -static inline void set_learn_mode(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x8); -} -static inline void set_scan_mode(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x20); -} -static inline void set_promiscuous_mode(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - /* agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x210);*/ - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x10); -} -static inline void set_managed_mode(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x2); -} -static inline void set_adhoc_mode(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, 0x0); -} - -#if 0 -static void unknow_register_write(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x0, 0x3e); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x4, 0xb2); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x8, 0x140); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0xc, 0x1C0); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x10, 0x1FF); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x14, 0x1DD); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x18, 0x15F); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x1c, 0xA1); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x20, 0x3E7); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x24, 0x36B); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x28, 0x348); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x2c, 0x37D); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x30, 0x3DE); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x34, 0x36); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x38, 0x64); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x3c, 0x57); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x40, 0x23); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x44, 0x3ED); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x48, 0x3C9); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x4c, 0x3CA); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x50, 0x3E7); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x54, 0x8); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x58, 0x1F); - agnx_write32(ctl, AGNX_UNKNOWN_BASE + 0x5c, 0x1a); -} -#endif - -static void card_interface_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u8 bssid[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - u32 reg; - unsigned int i; - AGNX_TRACE; - - might_sleep(); - /* Clear RX Control and Enable RX queues */ - agnx_write32(ctl, AGNX_CIR_RXCTL, 0x8); - - might_sleep(); - /* Do a full reset of the card */ - card_full_reset(priv); - might_sleep(); - - /* Check and set Card Endianness */ - reg = ioread32(priv->ctl + AGNX_CIR_ENDIAN); - /* TODO If not 0xB3B2B1B0 set to 0xB3B2B1B0 */ - printk(KERN_INFO PFX "CIR_ENDIAN is %x\n", reg); - - - /* Config the eeprom */ - agnx_write32(ctl, AGNX_CIR_SERIALITF, 0x7000086); - udelay(10); - reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); - - - agnx_write32(ctl, AGNX_PM_SOFTRST, 0x80000033); - reg = agnx_read32(ctl, 0xec50); - reg |= 0xf; - agnx_write32(ctl, 0xec50, reg); - agnx_write32(ctl, AGNX_PM_SOFTRST, 0x0); - - - reg = agnx_read32(ctl, AGNX_SYSITF_GPIOIN); - udelay(10); - reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); - - /* Dump the eeprom */ - do { - char eeprom[0x100000/0x100]; - - for (i = 0; i < 0x100000; i += 0x100) { - agnx_write32(ctl, AGNX_CIR_SERIALITF, 0x3000000 + i); - udelay(13); - reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); - udelay(70); - reg = agnx_read32(ctl, AGNX_CIR_SERIALITF); - eeprom[i/0x100] = reg & 0xFF; - udelay(10); - } - print_hex_dump_bytes(PFX "EEPROM: ", DUMP_PREFIX_NONE, eeprom, - ARRAY_SIZE(eeprom)); - } while (0); - - spi_rc_write(ctl, RF_CHIP0, 0x26); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - - /* Initialize the system interface */ - system_itf_init(priv); - - might_sleep(); - /* Chip Initialization (Polaris) */ - chip_init(priv); - might_sleep(); - - /* Calibrate the antennae */ - antenna_calibrate(priv); - - reg = agnx_read32(ctl, 0xec50); - reg &= ~0x40; - agnx_write32(ctl, 0xec50, reg); - agnx_write32(ctl, AGNX_PM_SOFTRST, 0x0); - agnx_write32(ctl, AGNX_PM_PLLCTL, 0x1); - - reg = agnx_read32(ctl, AGNX_BM_BMCTL); - reg |= 0x8000; - agnx_write32(ctl, AGNX_BM_BMCTL, reg); - enable_receiver(priv); - reg = agnx_read32(ctl, AGNX_SYSITF_SYSMODE); - reg |= 0x200; - agnx_write32(ctl, AGNX_SYSITF_SYSMODE, reg); - enable_receiver(priv); - - might_sleep(); - /* Initialize Gain Control Counts */ - gain_ctlcnt_init(priv); - - /* Write Initial Station Power Template for this station(#0) */ - sta_power_init(priv, LOCAL_STAID); - - might_sleep(); - /* Initialize the rx,td,tm rings, for each node in the ring */ - fill_rings(priv); - - might_sleep(); - - - agnx_write32(ctl, AGNX_PM_SOFTRST, 0x80000033); - agnx_write32(ctl, 0xec50, 0xc); - agnx_write32(ctl, AGNX_PM_SOFTRST, 0x0); - - /* FIXME Initialize the transmit control register */ - agnx_write32(ctl, AGNX_TXM_CTL, 0x194c1); - - enable_receiver(priv); - - might_sleep(); - /* FIXME Set the Receive Control Mac Address to card address */ - mac_address_set(priv); - enable_receiver(priv); - might_sleep(); - - /* Set the recieve request rate */ - /* FIXME Enable the request */ - /* Check packet length */ - /* Set maximum packet length */ -/* agnx_write32(ctl, AGNX_RXM_REQRATE, 0x88195e00); */ -/* enable_receiver(priv); */ - - /* Set the Receiver BSSID */ - receiver_bssid_set(priv, bssid); - - /* FIXME Set to managed mode */ - set_managed_mode(priv); -/* set_promiscuous_mode(priv); */ -/* set_scan_mode(priv); */ -/* set_learn_mode(priv); */ -/* set_promis_and_managed(priv); */ -/* set_adhoc_mode(priv); */ - - /* Set the recieve request rate */ - /* Check packet length */ - agnx_write32(ctl, AGNX_RXM_REQRATE, 0x08000000); - reg = agnx_read32(ctl, AGNX_RXM_REQRATE); - /* Set maximum packet length */ - reg |= 0x00195e00; - agnx_write32(ctl, AGNX_RXM_REQRATE, reg); - - /* Configure the RX and TX interrupt */ - reg = ENABLE_RX_INTERRUPT | RX_CACHE_LINE | FRAG_LEN_2048 | FRAG_BE; - agnx_write32(ctl, AGNX_CIR_RXCFG, reg); - /* FIXME */ - reg = ENABLE_TX_INTERRUPT | TX_CACHE_LINE | FRAG_LEN_2048 | FRAG_BE; - agnx_write32(ctl, AGNX_CIR_TXCFG, reg); - - /* Enable RX TX Interrupts */ - agnx_write32(ctl, AGNX_CIR_RXCTL, 0x80); - agnx_write32(ctl, AGNX_CIR_TXMCTL, 0x80); - agnx_write32(ctl, AGNX_CIR_TXDCTL, 0x80); - - /* FIXME Set the master control interrupt in block control */ - agnx_write32(ctl, AGNX_CIR_BLKCTL, 0x800); - - /* Enable RX and TX queues */ - reg = agnx_read32(ctl, AGNX_CIR_RXCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_CIR_RXCTL, reg); - reg = agnx_read32(ctl, AGNX_CIR_TXMCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_CIR_TXMCTL, reg); - reg = agnx_read32(ctl, AGNX_CIR_TXDCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_CIR_TXDCTL, reg); - - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x5); - /* FIXME */ - /* unknow_register_write(priv); */ - /* Update local card hash entry */ - hash_write(priv, priv->mac_addr, LOCAL_STAID); - - might_sleep(); - - /* FIXME */ - agnx_set_channel(priv, 1); - might_sleep(); -} /* agnx_card_interface_init */ - - -void agnx_hw_init(struct agnx_priv *priv) -{ - AGNX_TRACE; - might_sleep(); - card_interface_init(priv); -} - -int agnx_hw_reset(struct agnx_priv *priv) -{ - return card_full_reset(priv); -} - -int agnx_set_ssid(struct agnx_priv *priv, u8 *ssid, size_t ssid_len) -{ - AGNX_TRACE; - return 0; -} - -void agnx_set_bssid(struct agnx_priv *priv, const u8 *bssid) -{ - receiver_bssid_set(priv, bssid); -} diff --git a/drivers/staging/agnx/phy.h b/drivers/staging/agnx/phy.h deleted file mode 100644 index a955f05361e7..000000000000 --- a/drivers/staging/agnx/phy.h +++ /dev/null @@ -1,409 +0,0 @@ -#ifndef AGNX_PHY_H_ -#define AGNX_PHY_H_ - -#include "agnx.h" - -/* Transmission Managment Registers */ -#define AGNX_TXM_BASE 0x0000 -#define AGNX_TXM_CTL 0x0000 /* control register */ -#define AGNX_TXM_ETMF 0x0004 /* enable transmission management functions */ -#define AGNX_TXM_TXTEMP 0x0008 /* transmission template */ -#define AGNX_TXM_RETRYSTAID 0x000c /* Retry Station ID */ -#define AGNX_TXM_TIMESTAMPLO 0x0010 /* Timestamp Lo */ -#define AGNX_TXM_TIMESTAMPHI 0x0014 /* Timestamp Hi */ -#define AGNX_TXM_TXDELAY 0x0018 /* tx delay */ -#define AGNX_TXM_TBTTLO 0x0020 /* tbtt Lo */ -#define AGNX_TXM_TBTTHI 0x0024 /* tbtt Hi */ -#define AGNX_TXM_BEAINTER 0x0028 /* Beacon Interval */ -#define AGNX_TXM_NAV 0x0030 /* NAV */ -#define AGNX_TXM_CFPMDV 0x0034 /* CFP MDV */ -#define AGNX_TXM_CFPERCNT 0x0038 /* CFP period count */ -#define AGNX_TXM_PROBDELAY 0x003c /* probe delay */ -#define AGNX_TXM_LISINTERCNT 0x0040 /* listen interval count */ -#define AGNX_TXM_DTIMPERICNT 0x004c /* DTIM period count */ - -#define AGNX_TXM_BEACON_CTL 0x005c /* beacon control */ - -#define AGNX_TXM_SCHEMPCNT 0x007c /* schedule empty count */ -#define AGNX_TXM_MAXTIMOUT 0x0084 /* max timeout exceed count */ -#define AGNX_TXM_MAXCFPTIM 0x0088 /* max CF poll timeout count */ -#define AGNX_TXM_MAXRXTIME 0x008c /* max RX timeout count */ -#define AGNX_TXM_MAXACKTIM 0x0090 /* max ACK timeout count */ -#define AGNX_TXM_DIF01 0x00a0 /* DIF 0-1 */ -#define AGNX_TXM_DIF23 0x00a4 /* DIF 2-3 */ -#define AGNX_TXM_DIF45 0x00a8 /* DIF 4-5 */ -#define AGNX_TXM_DIF67 0x00ac /* DIF 6-7 */ -#define AGNX_TXM_SIFSPIFS 0x00b0 /* SIFS/PIFS */ -#define AGNX_TXM_TIFSEIFS 0x00b4 /* TIFS/EIFS */ -#define AGNX_TXM_MAXCCACNTSLOT 0x00b8 /* max CCA count slot */ -#define AGNX_TXM_SLOTLIMIT 0x00bc /* slot limit/1 msec limit */ -#define AGNX_TXM_CFPOLLRXTIM 0x00f0 /* CF poll RX timeout count */ -#define AGNX_TXM_CFACKT11B 0x00f4 /* CF ack timeout limit for 11b */ -#define AGNX_TXM_CW0 0x0100 /* CW 0 */ -#define AGNX_TXM_SLBEALIM0 0x0108 /* short/long beacon limit 0 */ -#define AGNX_TXM_CW1 0x0120 /* CW 1 */ -#define AGNX_TXM_SLBEALIM1 0x0128 /* short/long beacon limit 1 */ -#define AGNX_TXM_CW2 0x0140 /* CW 2 */ -#define AGNX_TXM_SLBEALIM2 0x0148 /* short/long beacon limit 2 */ -#define AGNX_TXM_CW3 0x0160 /* CW 3 */ -#define AGNX_TXM_SLBEALIM3 0x0168 /* short/long beacon limit 3 */ -#define AGNX_TXM_CW4 0x0180 /* CW 4 */ -#define AGNX_TXM_SLBEALIM4 0x0188 /* short/long beacon limit 4 */ -#define AGNX_TXM_CW5 0x01a0 /* CW 5 */ -#define AGNX_TXM_SLBEALIM5 0x01a8 /* short/long beacon limit 5 */ -#define AGNX_TXM_CW6 0x01c0 /* CW 6 */ -#define AGNX_TXM_SLBEALIM6 0x01c8 /* short/long beacon limit 6 */ -#define AGNX_TXM_CW7 0x01e0 /* CW 7 */ -#define AGNX_TXM_SLBEALIM7 0x01e8 /* short/long beacon limit 7 */ -#define AGNX_TXM_BEACONTEMP 0x1000 /* beacon template */ -#define AGNX_TXM_STAPOWTEMP 0x1a00 /* Station Power Template */ - -/* Receive Management Control Registers */ -#define AGNX_RXM_BASE 0x2000 -#define AGNX_RXM_REQRATE 0x2000 /* requested rate */ -#define AGNX_RXM_MACHI 0x2004 /* first 4 bytes of mac address */ -#define AGNX_RXM_MACLO 0x2008 /* last 2 bytes of mac address */ -#define AGNX_RXM_BSSIDHI 0x200c /* bssid hi */ -#define AGNX_RXM_BSSIDLO 0x2010 /* bssid lo */ -#define AGNX_RXM_HASH_CMD_FLAG 0x2014 /* Flags for the RX Hash Command Default:0 */ -#define AGNX_RXM_HASH_CMD_HIGH 0x2018 /* The High half of the Hash Command */ -#define AGNX_RXM_HASH_CMD_LOW 0x201c /* The Low half of the Hash Command */ -#define AGNX_RXM_ROUTAB 0x2020 /* routing table */ -#define ROUTAB_SUBTYPE_SHIFT 24 -#define ROUTAB_TYPE_SHIFT 28 -#define ROUTAB_STATUS_SHIFT 30 -#define ROUTAB_RW_SHIFT 31 -#define ROUTAB_ROUTE_DROP 0xf00000 /* Drop */ -#define ROUTAB_ROUTE_CPU 0x400000 /* CPU */ -#define ROUTAB_ROUTE_ENCRY 0x500800 /* Encryption */ -#define ROUTAB_ROUTE_RFP 0x800000 /* RFP */ - -#define ROUTAB_TYPE_MANAG 0x0 /* Management */ -#define ROUTAB_TYPE_CTL 0x1 /* Control */ -#define ROUTAB_TYPE_DATA 0x2 /* Data */ - -#define ROUTAB_SUBTYPE_DATA 0x0 -#define ROUTAB_SUBTYPE_DATAACK 0x1 -#define ROUTAB_SUBTYPE_DATAPOLL 0x2 -#define ROUTAB_SUBTYPE_DATAPOLLACK 0x3 -#define ROUTAB_SUBTYPE_NULL 0x4 /* NULL */ -#define ROUTAB_SUBTYPE_NULLACK 0x5 -#define ROUTAB_SUBTYPE_NULLPOLL 0x6 -#define ROUTAB_SUBTYPE_NULLPOLLACK 0x7 -#define ROUTAB_SUBTYPE_QOSDATA 0x8 /* QOS DATA */ -#define ROUTAB_SUBTYPE_QOSDATAACK 0x9 -#define ROUTAB_SUBTYPE_QOSDATAPOLL 0xa -#define ROUTAB_SUBTYPE_QOSDATAACKPOLL 0xb -#define ROUTAB_SUBTYPE_QOSNULL 0xc -#define ROUTAB_SUBTYPE_QOSNULLACK 0xd -#define ROUTAB_SUBTYPE_QOSNULLPOLL 0xe -#define ROUTAB_SUBTYPE_QOSNULLPOLLACK 0xf -#define AGNX_RXM_DELAY11 0x2024 /* delay 11(AB) */ -#define AGNX_RXM_SOF_CNT 0x2028 /* SOF Count */ -#define AGNX_RXM_FRAG_CNT 0x202c /* Fragment Count*/ -#define AGNX_RXM_FCS_CNT 0x2030 /* FCS Count */ -#define AGNX_RXM_BSSID_MISS_CNT 0x2034 /* BSSID Miss Count */ -#define AGNX_RXM_PDU_ERR_CNT 0x2038 /* PDU Error Count */ -#define AGNX_RXM_DEST_MISS_CNT 0x203C /* Destination Miss Count */ -#define AGNX_RXM_DROP_CNT 0x2040 /* Drop Count */ -#define AGNX_RXM_ABORT_CNT 0x2044 /* Abort Count */ -#define AGNX_RXM_RELAY_CNT 0x2048 /* Relay Count */ -#define AGNX_RXM_HASH_MISS_CNT 0x204c /* Hash Miss Count */ -#define AGNX_RXM_SA_HI 0x2050 /* Address of received packet Hi */ -#define AGNX_RXM_SA_LO 0x2054 /* Address of received packet Lo */ -#define AGNX_RXM_HASH_DUMP_LST 0x2100 /* Contains Hash Data */ -#define AGNX_RXM_HASH_DUMP_MST 0x2104 /* Contains Hash Data */ -#define AGNX_RXM_HASH_DUMP_DATA 0x2108 /* The Station ID to dump */ - - -/* Encryption Managment */ -#define AGNX_ENCRY_BASE 0x2400 -#define AGNX_ENCRY_WEPKEY0 0x2440 /* wep key #0 */ -#define AGNX_ENCRY_WEPKEY1 0x2444 /* wep key #1 */ -#define AGNX_ENCRY_WEPKEY2 0x2448 /* wep key #2 */ -#define AGNX_ENCRY_WEPKEY3 0x244c /* wep key #3 */ -#define AGNX_ENCRY_CCMRECTL 0x2460 /* ccm replay control */ - - -/* Band Management Registers */ -#define AGNX_BM_BASE 0x2c00 -#define AGNX_BM_BMCTL 0x2c00 /* band management control */ -#define AGNX_BM_TXWADDR 0x2c18 /* tx workqueue address start */ -#define AGNX_BM_TXTOPEER 0x2c24 /* transmit to peers */ -#define AGNX_BM_FPLHP 0x2c2c /* free pool list head pointer */ -#define AGNX_BM_FPLTP 0x2c30 /* free pool list tail pointer */ -#define AGNX_BM_FPCNT 0x2c34 /* free pool count */ -#define AGNX_BM_CIPDUWCNT 0x2c38 /* card interface pdu workqueue count */ -#define AGNX_BM_SPPDUWCNT 0x2c3c /* sp pdu workqueue count */ -#define AGNX_BM_RFPPDUWCNT 0x2c40 /* rfp pdu workqueue count */ -#define AGNX_BM_RHPPDUWCNT 0x2c44 /* rhp pdu workqueue count */ -#define AGNX_BM_CIWQCTL 0x2c48 /* Card Interface WorkQueue Control */ -#define AGNX_BM_CPUTXWCTL 0x2c50 /* cpu tx workqueue control */ -#define AGNX_BM_CPURXWCTL 0x2c58 /* cpu rx workqueue control */ -#define AGNX_BM_CPULWCTL 0x2c60 /* cpu low workqueue control */ -#define AGNX_BM_CPUHWCTL 0x2c68 /* cpu high workqueue control */ -#define AGNX_BM_SPTXWCTL 0x2c70 /* sp tx workqueue control */ -#define AGNX_BM_SPRXWCTL 0x2c78 /* sp rx workqueue control */ -#define AGNX_BM_RFPWCTL 0x2c80 /* RFP workqueue control */ -#define AGNX_BM_MTSM 0x2c90 /* Multicast Transmit Station Mask */ - -/* Card Interface Registers (32bits) */ -#define AGNX_CIR_BASE 0x3000 -#define AGNX_CIR_BLKCTL 0x3000 /* block control*/ -#define AGNX_STAT_TX 0x1 -#define AGNX_STAT_RX 0x2 -#define AGNX_STAT_X 0x4 -/* Below two interrupt flags will be set by our but not CPU or the card */ -#define AGNX_STAT_TXD 0x10 -#define AGNX_STAT_TXM 0x20 - -#define AGNX_CIR_ADDRWIN 0x3004 /* Addressable Windows*/ -#define AGNX_CIR_ENDIAN 0x3008 /* card endianness */ -#define AGNX_CIR_SERIALITF 0x3020 /* serial interface */ -#define AGNX_CIR_RXCFG 0x3040 /* receive config */ -#define ENABLE_RX_INTERRUPT 0x20 -#define RX_CACHE_LINE 0x8 -/* the RX fragment length */ -#define FRAG_LEN_256 0x0 /* 256B */ -#define FRAG_LEN_512 0x1 -#define FRAG_LEN_1024 0x2 -#define FRAG_LEN_2048 0x3 -#define FRAG_BE 0x10 -#define AGNX_CIR_RXCTL 0x3050 /* receive control */ -/* memory address, chipside */ -#define AGNX_CIR_RXCMSTART 0x3054 /* receive client memory start */ -#define AGNX_CIR_RXCMEND 0x3058 /* receive client memory end */ -/* memory address, pci */ -#define AGNX_CIR_RXHOSTADDR 0x3060 /* receive hostside address */ -/* memory address, chipside */ -#define AGNX_CIR_RXCLIADDR 0x3064 /* receive clientside address */ -#define AGNX_CIR_RXDMACTL 0x3068 /* receive dma control */ -#define AGNX_CIR_TXCFG 0x3080 /* transmit config */ -#define AGNX_CIR_TXMCTL 0x3090 /* Transmit Management Control */ -#define ENABLE_TX_INTERRUPT 0x20 -#define TX_CACHE_LINE 0x8 -#define AGNX_CIR_TXMSTART 0x3094 /* Transmit Management Start */ -#define AGNX_CIR_TXMEND 0x3098 /* Transmit Management End */ -#define AGNX_CIR_TXDCTL 0x30a0 /* transmit data control */ -/* memeory address, chipset */ -#define AGNX_CIR_TXDSTART 0x30a4 /* transmit data start */ -#define AGNX_CIR_TXDEND 0x30a8 /* transmit data end */ -#define AGNX_CIR_TXMHADDR 0x30b0 /* Transmit Management Hostside Address */ -#define AGNX_CIR_TXMCADDR 0x30b4 /* Transmit Management Clientside Address */ -#define AGNX_CIR_TXDMACTL 0x30b8 /* transmit dma control */ - - -/* Power Managment Unit */ -#define AGNX_PM_BASE 0x3c00 -#define AGNX_PM_PMCTL 0x3c00 /* PM Control*/ -#define AGNX_PM_MACMSW 0x3c08 /* MAC Manual Slow Work Enable */ -#define AGNX_PM_RFCTL 0x3c0c /* RF Control */ -#define AGNX_PM_PHYMW 0x3c14 /* Phy Mannal Work */ -#define AGNX_PM_SOFTRST 0x3c18 /* PMU Soft Reset */ -#define AGNX_PM_PLLCTL 0x3c1c /* PMU PLL control*/ -#define AGNX_PM_TESTPHY 0x3c24 /* PMU Test Phy */ - - -/* Interrupt Control interface */ -#define AGNX_INT_BASE 0x4000 -#define AGNX_INT_STAT 0x4000 /* interrupt status */ -#define AGNX_INT_MASK 0x400c /* interrupt mask */ -/* FIXME */ -#define IRQ_TX_BEACON 0x1 /* TX Beacon */ -#define IRQ_TX_RETRY 0x8 /* TX Retry Interrupt */ -#define IRQ_TX_ACTIVITY 0x10 /* TX Activity */ -#define IRQ_RX_ACTIVITY 0x20 /* RX Activity */ -/* FIXME I guess that instead RX a none exist staion's packet or - the station hasn't been init */ -#define IRQ_RX_X 0x40 -#define IRQ_RX_Y 0x80 /* RX ? */ -#define IRQ_RX_HASHHIT 0x100 /* RX Hash Hit */ -#define IRQ_RX_FRAME 0x200 /* RX Frame */ -#define IRQ_ERR_INT 0x400 /* Error Interrupt */ -#define IRQ_TX_QUE_FULL 0x800 /* TX Workqueue Full */ -#define IRQ_BANDMAN_ERR 0x10000 /* Bandwidth Management Error */ -#define IRQ_TX_DISABLE 0x20000 /* TX Disable */ -#define IRQ_RX_IVASESKEY 0x80000 /* RX Invalid Session Key */ -#define IRQ_RX_KEYIDMIS 0x100000 /* RX key ID Mismatch */ -#define IRQ_REP_THHIT 0x200000 /* Replay Threshold Hit */ -#define IRQ_TIMER1 0x4000000 /* Timer1 */ -#define IRQ_TIMER_CNT 0x10000000 /* Timer Count */ -#define IRQ_PHY_FASTINT 0x20000000 /* Phy Fast Interrupt */ -#define IRQ_PHY_SLOWINT 0x40000000 /* Phy Slow Interrupt */ -#define IRQ_OTHER 0x80000000 /* Unknow interrupt */ -#define AGNX_IRQ_ALL 0xffffffff - -/* System Interface */ -#define AGNX_SYSITF_BASE 0x4400 -#define AGNX_SYSITF_SYSMODE 0x4400 /* system mode */ -#define AGNX_SYSITF_GPIOIN 0x4410 /* GPIO In */ -/* PIN lines for leds? */ -#define AGNX_SYSITF_GPIOUT 0x4414 /* GPIO Out */ - -/* Timer Control */ -#define AGNX_TIMCTL_TIMER1 0x4800 /* Timer 1 */ -#define AGNX_TIMCTL_TIM1CTL 0x4808 /* Timer 1 Control */ - - -/* Antenna Calibration Interface */ -#define AGNX_ACI_BASE 0x5000 -#define AGNX_ACI_MODE 0x5000 /* Mode */ -#define AGNX_ACI_MEASURE 0x5004 /* Measure */ -#define AGNX_ACI_SELCHAIN 0x5008 /* Select Chain */ -#define AGNX_ACI_LEN 0x500c /* Length */ -#define AGNX_ACI_TIMER1 0x5018 /* Timer 1 */ -#define AGNX_ACI_TIMER2 0x501c /* Timer 2 */ -#define AGNX_ACI_OFFSET 0x5020 /* Offset */ -#define AGNX_ACI_STATUS 0x5030 /* Status */ -#define CALI_IDLE 0x0 -#define CALI_DONE 0x1 -#define CALI_BUSY 0x2 -#define CALI_ERR 0x3 -#define AGNX_ACI_AICCHA0OVE 0x5034 /* AIC Channel 0 Override */ -#define AGNX_ACI_AICCHA1OVE 0x5038 /* AIC Channel 1 Override */ - -/* Gain Control Registers */ -#define AGNX_GCR_BASE 0x9000 -/* threshold of primary antenna */ -#define AGNX_GCR_THD0A 0x9000 /* threshold? D0 A */ -/* low threshold of primary antenna */ -#define AGNX_GCR_THD0AL 0x9004 /* threshold? D0 A low */ -/* threshold of secondary antenna */ -#define AGNX_GCR_THD0B 0x9008 /* threshold? D0_B */ -#define AGNX_GCR_DUNSAT 0x900c /* d unsaturated */ -#define AGNX_GCR_DSAT 0x9010 /* d saturated */ -#define AGNX_GCR_DFIRCAL 0x9014 /* D Fir/Cal */ -#define AGNX_GCR_DGCTL11A 0x9018 /* d gain control 11a */ -#define AGNX_GCR_DGCTL11B 0x901c /* d gain control 11b */ -/* strength of gain */ -#define AGNX_GCR_GAININIT 0x9020 /* gain initialization */ -#define AGNX_GCR_THNOSIG 0x9024 /* threhold no signal */ -#define AGNX_GCR_COARSTEP 0x9028 /* coarse stepping */ -#define AGNX_GCR_SIFST11A 0x902c /* sifx time 11a */ -#define AGNX_GCR_SIFST11B 0x9030 /* sifx time 11b */ -#define AGNX_GCR_CWDETEC 0x9034 /* cw detection */ -#define AGNX_GCR_0X38 0x9038 /* ???? */ -#define AGNX_GCR_BOACT 0x903c /* BO Active */ -#define AGNX_GCR_BOINACT 0x9040 /* BO Inactive */ -#define AGNX_GCR_BODYNA 0x9044 /* BO dynamic */ -/* 802.11 mode(a,b,g) */ -#define AGNX_GCR_DISCOVMOD 0x9048 /* discovery mode */ -#define AGNX_GCR_NLISTANT 0x904c /* number of listening antenna */ -#define AGNX_GCR_NACTIANT 0x9050 /* number of active antenna */ -#define AGNX_GCR_NMEASANT 0x9054 /* number of measuring antenna */ -#define AGNX_GCR_NCAPTANT 0x9058 /* number of capture antenna */ -#define AGNX_GCR_THCAP11A 0x905c /* threshold capture 11a */ -#define AGNX_GCR_THCAP11B 0x9060 /* threshold capture 11b */ -#define AGNX_GCR_THCAPRX11A 0x9064 /* threshold capture rx 11a */ -#define AGNX_GCR_THCAPRX11B 0x9068 /* threshold capture rx 11b */ -#define AGNX_GCR_THLEVDRO 0x906c /* threshold level drop */ -#define AGNX_GCR_GAINSET0 0x9070 /* Gainset 0 */ -#define AGNX_GCR_GAINSET1 0x9074 /* Gainset 1 */ -#define AGNX_GCR_GAINSET2 0x9078 /* Gainset 2 */ -#define AGNX_GCR_MAXRXTIME11A 0x907c /* maximum rx time 11a */ -#define AGNX_GCR_MAXRXTIME11B 0x9080 /* maximum rx time 11b */ -#define AGNX_GCR_CORRTIME 0x9084 /* correction time */ -/* reset the subsystem, 0 = disable, 1 = enable */ -#define AGNX_GCR_RSTGCTL 0x9088 /* reset gain control */ -/* channel receiving */ -#define AGNX_GCR_RXCHANEL 0x908c /* receive channel */ -#define AGNX_GCR_NOISE0 0x9090 /* Noise 0 */ -#define AGNX_GCR_NOISE1 0x9094 /* Noise 1 */ -#define AGNX_GCR_NOISE2 0x9098 /* Noise 2 */ -#define AGNX_GCR_SIGHTH 0x909c /* Signal High Threshold */ -#define AGNX_GCR_SIGLTH 0x90a0 /* Signal Low Threshold */ -#define AGNX_GCR_CORRDROP 0x90a4 /* correction drop */ -/* threshold of tertiay antenna */ -#define AGNX_GCR_THCD 0x90a8 /* threshold? CD */ -#define AGNX_GCR_THCS 0x90ac /* threshold? CS */ -#define AGNX_GCR_MAXPOWDIFF 0x90b8 /* maximum power difference */ -#define AGNX_GCR_TRACNT4 0x90ec /* Transition Count 4 */ -#define AGNX_GCR_TRACNT5 0x90f0 /* transition count 5 */ -#define AGNX_GCR_TRACNT6 0x90f4 /* transition count 6 */ -#define AGNX_GCR_TRACNT7 0x90f8 /* transition coutn 7 */ -#define AGNX_GCR_TESTBUS 0x911c /* test bus */ -#define AGNX_GCR_CHAINNUM 0x9120 /* Number of Chains */ -#define AGNX_GCR_ANTCFG 0x9124 /* Antenna Config */ -#define AGNX_GCR_THJUMP 0x912c /* threhold jump */ -#define AGNX_GCR_THPOWER 0x9130 /* threshold power */ -#define AGNX_GCR_THPOWCLIP 0x9134 /* threshold power clip*/ -#define AGNX_GCR_FORCECTLCLK 0x9138 /* Force Gain Control Clock */ -#define AGNX_GCR_GAINSETWRITE 0x913c /* Gainset Write */ -#define AGNX_GCR_THD0BTFEST 0x9140 /* threshold d0 b tf estimate */ -#define AGNX_GCR_THRX11BPOWMIN 0x9144 /* threshold rx 11b power minimum */ -#define AGNX_GCR_0X14c 0x914c /* ?? */ -#define AGNX_GCR_0X150 0x9150 /* ?? */ -#define AGNX_GCR_RXOVERIDE 0x9194 /* recieve override */ -#define AGNX_GCR_WATCHDOG 0x91b0 /* watchdog timeout */ - - -/* Spi Interface */ -#define AGNX_SPI_BASE 0xdc00 -#define AGNX_SPI_CFG 0xdc00 /* spi configuration */ -/* Only accept 16 bits */ -#define AGNX_SPI_WMSW 0xdc04 /* write most significant word */ -/* Only accept 16 bits */ -#define AGNX_SPI_WLSW 0xdc08 /* write least significant word */ -#define AGNX_SPI_CTL 0xdc0c /* spi control */ -#define AGNX_SPI_RMSW 0xdc10 /* read most significant word */ -#define AGNX_SPI_RLSW 0xdc14 /* read least significant word */ -/* SPI Control Mask */ -#define SPI_READ_CTL 0x4000 /* read control */ -#define SPI_BUSY_CTL 0x8000 /* busy control */ -/* RF and synth chips in spi */ -#define RF_CHIP0 0x400 -#define RF_CHIP1 0x800 -#define RF_CHIP2 0x1000 -#define SYNTH_CHIP 0x2000 - -/* Unknown register */ -#define AGNX_UNKNOWN_BASE 0x7800 - -/* FIXME MonitorGain */ -#define AGNX_MONGCR_BASE 0x12000 - -/* Gain Table */ -#define AGNX_GAIN_TABLE 0x12400 - -/* The initial FIR coefficient table */ -#define AGNX_FIR_BASE 0x19804 - -#define AGNX_ENGINE_LOOKUP_TBL 0x800 - -/* eeprom commands */ -#define EEPROM_CMD_NULL 0x0 /* NULL */ -#define EEPROM_CMD_WRITE 0x2 /* write */ -#define EEPROM_CMD_READ 0x3 /* read */ -#define EEPROM_CMD_STATUSREAD 0x5 /* status register read */ -#define EEPROM_CMD_WRITEENABLE 0x6 /* write enable */ -#define EEPROM_CMD_CONFIGURE 0x7 /* configure */ - -#define EEPROM_DATAFORCOFIGURE 0x6 /* ??? */ - -/* eeprom address */ -#define EEPROM_ADDR_SUBVID 0x0 /* Sub Vendor ID */ -#define EEPROM_ADDR_SUBSID 0x2 /* Sub System ID */ -#define EEPROM_ADDR_MACADDR 0x146 /* MAC Address */ -#define EEPROM_ADDR_LOTYPE 0x14f /* LO type */ - -struct agnx_eeprom { - u8 data; /* date */ - u16 address; /* address in EEPROM */ - u8 cmd; /* command, unknown, status */ -} __attribute__((__packed__)); - -#define AGNX_EEPROM_COMMAND_SHIFT 5 -#define AGNX_EEPROM_COMMAND_STAT 0x01 - -void disable_receiver(struct agnx_priv *priv); -void enable_receiver(struct agnx_priv *priv); -u8 read_from_eeprom(struct agnx_priv *priv, u16 address); -void agnx_hw_init(struct agnx_priv *priv); -int agnx_hw_reset(struct agnx_priv *priv); -int agnx_set_ssid(struct agnx_priv *priv, u8 *ssid, size_t ssid_len); -void agnx_set_bssid(struct agnx_priv *priv, const u8 *bssid); -void enable_power_saving(struct agnx_priv *priv); -void disable_power_saving(struct agnx_priv *priv); -void calibrate_antenna_period(unsigned long data); - -#endif /* AGNX_PHY_H_ */ diff --git a/drivers/staging/agnx/rf.c b/drivers/staging/agnx/rf.c deleted file mode 100644 index 42e457a1844f..000000000000 --- a/drivers/staging/agnx/rf.c +++ /dev/null @@ -1,893 +0,0 @@ -/** - * Airgo MIMO wireless driver - * - * Copyright (c) 2007 Li YanBo - - * Thanks for Jeff Williams do reverse engineer - * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation; - */ - -#include -#include -#include "agnx.h" -#include "debug.h" -#include "phy.h" -#include "table.h" - -/* FIXME! */ -static inline void spi_write(void __iomem *region, u32 chip_ids, u32 sw, - u16 size, u32 control) -{ - u32 reg; - u32 lsw = sw & 0xffff; /* lower 16 bits of sw*/ - u32 msw = sw >> 16; /* high 16 bits of sw */ - - /* FIXME Write Most Significant Word of the 32bit data to MSW */ - /* FIXME And Least Significant Word to LSW */ - iowrite32((lsw), region + AGNX_SPI_WLSW); - iowrite32((msw), region + AGNX_SPI_WMSW); - reg = chip_ids | size | control; - /* Write chip id(s), write size and busy control to Control Register */ - iowrite32((reg), region + AGNX_SPI_CTL); - /* Wait for Busy control to clear */ - spi_delay(); -} - -/* - * Write to SPI Synth register - */ -static inline void spi_sy_write(void __iomem *region, u32 chip_ids, u32 sw) -{ - /* FIXME the size 0x15 is a magic value*/ - spi_write(region, chip_ids, sw, 0x15, SPI_BUSY_CTL); -} - -/* - * Write to SPI RF register - */ -static inline void spi_rf_write(void __iomem *region, u32 chip_ids, u32 sw) -{ - /* FIXME the size 0xd is a magic value*/ - spi_write(region, chip_ids, sw, 0xd, SPI_BUSY_CTL); -} /* spi_rf_write */ - -/* - * Write to SPI with Read Control bit set - */ -inline void spi_rc_write(void __iomem *region, u32 chip_ids, u32 sw) -{ - /* FIXME the size 0xe5 is a magic value */ - spi_write(region, chip_ids, sw, 0xe5, SPI_BUSY_CTL|SPI_READ_CTL); -} - -/* Get the active chains's count */ -static int get_active_chains(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int num = 0; - u32 reg; - AGNX_TRACE; - - spi_rc_write(ctl, RF_CHIP0, 0x21); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (reg == 1) - num++; - - spi_rc_write(ctl, RF_CHIP1, 0x21); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (reg == 1) - num++; - - spi_rc_write(ctl, RF_CHIP2, 0x21); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (reg == 1) - num++; - - spi_rc_write(ctl, RF_CHIP0, 0x26); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (0x33 != reg) - printk(KERN_WARNING PFX "Unmatched rf chips result\n"); - - return num; -} /* get_active_chains */ - -void rf_chips_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - int num; - AGNX_TRACE; - - if (priv->revid == 1) { - reg = agnx_read32(ctl, AGNX_SYSITF_GPIOUT); - reg |= 0x8; - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, reg); - } - - /* Set SPI clock speed to 200NS */ - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xF; - reg |= 0x3; - agnx_write32(ctl, AGNX_SPI_CFG, reg); - - /* Set SPI clock speed to 50NS */ - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xF; - reg |= 0x1; - agnx_write32(ctl, AGNX_SPI_CFG, reg); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1101); - - num = get_active_chains(priv); - printk(KERN_INFO PFX "Active chains are %d\n", num); - - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xF; - agnx_write32(ctl, AGNX_SPI_CFG, reg); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1908); -} /* rf_chips_init */ - - -static u32 channel_tbl[15][9] = { - {0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {1, 0x00, 0x00, 0x624, 0x00, 0x1a4, 0x28, 0x00, 0x1e}, - {2, 0x00, 0x00, 0x615, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, - {3, 0x00, 0x00, 0x61a, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, - {4, 0x00, 0x00, 0x61f, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, - {5, 0x00, 0x00, 0x624, 0x00, 0x1ae, 0x28, 0x00, 0x1e}, - {6, 0x00, 0x00, 0x61f, 0x00, 0x1b3, 0x28, 0x00, 0x1e}, - {7, 0x00, 0x00, 0x624, 0x00, 0x1b3, 0x28, 0x00, 0x1e}, - {8, 0x00, 0x00, 0x629, 0x00, 0x1b3, 0x28, 0x00, 0x1e}, - {9, 0x00, 0x00, 0x624, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, - {10, 0x00, 0x00, 0x629, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, - {11, 0x00, 0x00, 0x62e, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, - {12, 0x00, 0x00, 0x633, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, - {13, 0x00, 0x00, 0x628, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, - {14, 0x00, 0x00, 0x644, 0x00, 0x1b8, 0x28, 0x00, 0x1e}, -}; - - -static inline void -channel_tbl_write(struct agnx_priv *priv, unsigned int channel, unsigned int reg_num) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - reg = channel_tbl[channel][reg_num]; - reg <<= 4; - reg |= reg_num; - spi_sy_write(ctl, SYNTH_CHIP, reg); -} - -static void synth_freq_set(struct agnx_priv *priv, unsigned int channel) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - - /* Set the Clock bits to 50NS */ - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xF; - reg |= 0x1; - agnx_write32(ctl, AGNX_SPI_CFG, reg); - - /* Write 0x00c0 to LSW and 0x3 to MSW of Synth Chip */ - spi_sy_write(ctl, SYNTH_CHIP, 0x300c0); - - spi_sy_write(ctl, SYNTH_CHIP, 0x32); - - /* # Write to Register 1 on the Synth Chip */ - channel_tbl_write(priv, channel, 1); - /* # Write to Register 3 on the Synth Chip */ - channel_tbl_write(priv, channel, 3); - /* # Write to Register 6 on the Synth Chip */ - channel_tbl_write(priv, channel, 6); - /* # Write to Register 5 on the Synth Chip */ - channel_tbl_write(priv, channel, 5); - /* # Write to register 8 on the Synth Chip */ - channel_tbl_write(priv, channel, 8); - - /* FIXME Clear the clock bits */ - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xf; - agnx_write32(ctl, AGNX_SPI_CFG, reg); -} /* synth_chip_init */ - - -static void antenna_init(struct agnx_priv *priv, int num_antenna) -{ - void __iomem *ctl = priv->ctl; - - switch (num_antenna) { - case 1: - agnx_write32(ctl, AGNX_GCR_NLISTANT, 1); - agnx_write32(ctl, AGNX_GCR_NMEASANT, 1); - agnx_write32(ctl, AGNX_GCR_NACTIANT, 1); - agnx_write32(ctl, AGNX_GCR_NCAPTANT, 1); - - agnx_write32(ctl, AGNX_GCR_ANTCFG, 7); - agnx_write32(ctl, AGNX_GCR_BOACT, 34); - agnx_write32(ctl, AGNX_GCR_BOINACT, 34); - agnx_write32(ctl, AGNX_GCR_BODYNA, 30); - - agnx_write32(ctl, AGNX_GCR_THD0A, 125); - agnx_write32(ctl, AGNX_GCR_THD0AL, 100); - agnx_write32(ctl, AGNX_GCR_THD0B, 90); - - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 80); - agnx_write32(ctl, AGNX_GCR_SIGHTH, 100); - agnx_write32(ctl, AGNX_GCR_SIGLTH, 16); - break; - case 2: - agnx_write32(ctl, AGNX_GCR_NLISTANT, 2); - agnx_write32(ctl, AGNX_GCR_NMEASANT, 2); - agnx_write32(ctl, AGNX_GCR_NACTIANT, 2); - agnx_write32(ctl, AGNX_GCR_NCAPTANT, 2); - agnx_write32(ctl, AGNX_GCR_ANTCFG, 15); - agnx_write32(ctl, AGNX_GCR_BOACT, 36); - agnx_write32(ctl, AGNX_GCR_BOINACT, 36); - agnx_write32(ctl, AGNX_GCR_BODYNA, 32); - agnx_write32(ctl, AGNX_GCR_THD0A, 120); - agnx_write32(ctl, AGNX_GCR_THD0AL, 100); - agnx_write32(ctl, AGNX_GCR_THD0B, 80); - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 70); - agnx_write32(ctl, AGNX_GCR_SIGHTH, 100); - agnx_write32(ctl, AGNX_GCR_SIGLTH, 32); - break; - case 3: - agnx_write32(ctl, AGNX_GCR_NLISTANT, 3); - agnx_write32(ctl, AGNX_GCR_NMEASANT, 3); - agnx_write32(ctl, AGNX_GCR_NACTIANT, 3); - agnx_write32(ctl, AGNX_GCR_NCAPTANT, 3); - agnx_write32(ctl, AGNX_GCR_ANTCFG, 31); - agnx_write32(ctl, AGNX_GCR_BOACT, 36); - agnx_write32(ctl, AGNX_GCR_BOINACT, 36); - agnx_write32(ctl, AGNX_GCR_BODYNA, 32); - agnx_write32(ctl, AGNX_GCR_THD0A, 100); - agnx_write32(ctl, AGNX_GCR_THD0AL, 100); - agnx_write32(ctl, AGNX_GCR_THD0B, 70); - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 70); - agnx_write32(ctl, AGNX_GCR_SIGHTH, 100); - agnx_write32(ctl, AGNX_GCR_SIGLTH, 48); -/* agnx_write32(ctl, AGNX_GCR_SIGLTH, 16); */ - break; - default: - printk(KERN_WARNING PFX "Unknow antenna number\n"); - } -} /* antenna_init */ - -static void chain_update(struct agnx_priv *priv, u32 chain) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - spi_rc_write(ctl, RF_CHIP0, 0x20); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - - if (reg == 0x4) - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, reg|0x1000); - else if (reg != 0x0) - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, reg|0x1000); - else { - if (chain == 3 || chain == 6) { - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, reg|0x1000); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - } else if (chain == 2 || chain == 4) { - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, reg|0x1000); - spi_rf_write(ctl, RF_CHIP2, 0x1005); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x824); - } else if (chain == 1) { - spi_rf_write(ctl, RF_CHIP0, reg|0x1000); - spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1004); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0xc36); - } - } - - spi_rc_write(ctl, RF_CHIP0, 0x22); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - - switch (reg) { - case 0: - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1005); - break; - case 1: - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - break; - case 2: - if (chain == 6 || chain == 4) { - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1202); - spi_rf_write(ctl, RF_CHIP2, 0x1005); - } else if (chain < 3) { - spi_rf_write(ctl, RF_CHIP0, 0x1202); - spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1005); - } - break; - default: - if (chain == 3) { - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1203); - spi_rf_write(ctl, RF_CHIP2, 0x1201); - } else if (chain == 2) { - spi_rf_write(ctl, RF_CHIP0, 0x1203); - spi_rf_write(ctl, RF_CHIP2, 0x1200); - spi_rf_write(ctl, RF_CHIP1, 0x1201); - } else if (chain == 1) { - spi_rf_write(ctl, RF_CHIP0, 0x1203); - spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1200); - } else if (chain == 4) { - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1203); - spi_rf_write(ctl, RF_CHIP2, 0x1201); - } else { - spi_rf_write(ctl, RF_CHIP0, 0x1203); - spi_rf_write(ctl, RF_CHIP1|RF_CHIP2, 0x1201); - } - } -} /* chain_update */ - -static void antenna_config(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - /* Write 0x0 to the TX Management Control Register Enable bit */ - reg = agnx_read32(ctl, AGNX_TXM_CTL); - reg &= ~0x1; - agnx_write32(ctl, AGNX_TXM_CTL, reg); - - /* FIXME */ - /* Set initial value based on number of Antennae */ - antenna_init(priv, 3); - - /* FIXME Update Power Templates for current valid Stations */ - /* sta_power_init(priv, 0);*/ - - /* FIXME the number of chains should get from eeprom*/ - chain_update(priv, AGNX_CHAINS_MAX); -} /* antenna_config */ - -void calibrate_oscillator(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - spi_rc_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - reg = agnx_read32(ctl, AGNX_GCR_GAINSET1); - reg |= 0x10; - agnx_write32(ctl, AGNX_GCR_GAINSET1, reg); - - agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 1); - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 1); - - agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); - - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x27); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - /* (Residual DC Calibration) to Calibration Mode */ - agnx_write32(ctl, AGNX_ACI_MODE, 0x2); - - spi_rc_write(ctl, RF_CHIP0|RF_CHIP1, 0x1004); - agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); - /* (TX LO Calibration) to Calibration Mode */ - agnx_write32(ctl, AGNX_ACI_MODE, 0x4); - - do { - u32 reg1, reg2, reg3; - /* Enable Power Saving Control */ - enable_power_saving(priv); - /* Save the following registers to restore */ - reg1 = ioread32(ctl + 0x11000); - reg2 = ioread32(ctl + 0xec50); - reg3 = ioread32(ctl + 0xec54); - wmb(); - - agnx_write32(ctl, 0x11000, 0xcfdf); - agnx_write32(ctl, 0xec50, 0x70); - /* Restore the registers */ - agnx_write32(ctl, 0x11000, reg1); - agnx_write32(ctl, 0xec50, reg2); - agnx_write32(ctl, 0xec54, reg3); - /* Disable Power Saving Control */ - disable_power_saving(priv); - } while (0); - - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0); -} /* calibrate_oscillator */ - - -static void radio_channel_set(struct agnx_priv *priv, unsigned int channel) -{ - void __iomem *ctl = priv->ctl; - unsigned int freq = priv->band.channels[channel - 1].center_freq; - u32 reg; - AGNX_TRACE; - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - /* Set SPI Clock to 50 Ns */ - reg = agnx_read32(ctl, AGNX_SPI_CFG); - reg &= ~0xF; - reg |= 0x1; - agnx_write32(ctl, AGNX_SPI_CFG, reg); - - /* Clear the Disable Tx interrupt bit in Interrupt Mask */ -/* reg = agnx_read32(ctl, AGNX_INT_MASK); */ -/* reg &= ~IRQ_TX_DISABLE; */ -/* agnx_write32(ctl, AGNX_INT_MASK, reg); */ - - /* Band Selection */ - reg = agnx_read32(ctl, AGNX_SYSITF_GPIOUT); - reg |= 0x8; - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, reg); - - /* FIXME Set the SiLabs Chip Frequency */ - synth_freq_set(priv, channel); - - reg = agnx_read32(ctl, AGNX_PM_SOFTRST); - reg |= 0x80100030; - agnx_write32(ctl, AGNX_PM_SOFTRST, reg); - reg = agnx_read32(ctl, AGNX_PM_PLLCTL); - reg |= 0x20009; - agnx_write32(ctl, AGNX_PM_PLLCTL, reg); - - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, 0x5); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1100); - - /* Load the MonitorGain Table */ - monitor_gain_table_init(priv); - - /* Load the TX Fir table */ - tx_fir_table_init(priv); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg |= 0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - - spi_rc_write(ctl, RF_CHIP0|RF_CHIP1, 0x22); - udelay(80); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - - - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0xff); - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - - reg = agnx_read32(ctl, 0xec50); - reg |= 0x4f; - agnx_write32(ctl, 0xec50, reg); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - agnx_write32(ctl, 0x11008, 0x1); - agnx_write32(ctl, 0x1100c, 0x0); - agnx_write32(ctl, 0x11008, 0x0); - agnx_write32(ctl, 0xec50, 0xc); - - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - agnx_write32(ctl, 0x11010, 0x6e); - agnx_write32(ctl, 0x11014, 0x6c); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1201); - - /* Calibrate the Antenna */ - /* antenna_calibrate(priv); */ - /* Calibrate the TxLocalOscillator */ - calibrate_oscillator(priv); - - reg = agnx_read32(ctl, AGNX_PM_PMCTL); - reg &= ~0x8; - agnx_write32(ctl, AGNX_PM_PMCTL, reg); - agnx_write32(ctl, AGNX_GCR_GAININIT, 0xa); - agnx_write32(ctl, AGNX_GCR_THCD, 0x0); - - agnx_write32(ctl, 0x11018, 0xb); - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x0); - - /* Write Frequency to Gain Control Channel */ - agnx_write32(ctl, AGNX_GCR_RXCHANEL, freq); - /* Write 0x140000/Freq to 0x9c08 */ - reg = 0x140000/freq; - agnx_write32(ctl, 0x9c08, reg); - - reg = agnx_read32(ctl, AGNX_PM_SOFTRST); - reg &= ~0x80100030; - agnx_write32(ctl, AGNX_PM_SOFTRST, reg); - - reg = agnx_read32(ctl, AGNX_PM_PLLCTL); - reg &= ~0x20009; - reg |= 0x1; - agnx_write32(ctl, AGNX_PM_PLLCTL, reg); - - agnx_write32(ctl, AGNX_ACI_MODE, 0x0); - -/* FIXME According to Number of Chains: */ - -/* 1. 1: */ -/* 1. Write 0x1203 to RF Chip 0 */ -/* 2. Write 0x1200 to RF Chips 1 +2 */ -/* 2. 2: */ -/* 1. Write 0x1203 to RF Chip 0 */ -/* 2. Write 0x1200 to RF Chip 2 */ -/* 3. Write 0x1201 to RF Chip 1 */ -/* 3. 3: */ -/* 1. Write 0x1203 to RF Chip 0 */ -/* 2. Write 0x1201 to RF Chip 1 + 2 */ -/* 4. 4: */ -/* 1. Write 0x1203 to RF Chip 0 + 1 */ -/* 2. Write 0x1200 to RF Chip 2 */ - -/* 5. 6: */ - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1, 0x1203); - spi_rf_write(ctl, RF_CHIP2, 0x1201); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1000); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - - /* FIXME Set the Disable Tx interrupt bit in Interrupt Mask - (Or 0x20000 to Interrupt Mask) */ -/* reg = agnx_read32(ctl, AGNX_INT_MASK); */ -/* reg |= IRQ_TX_DISABLE; */ -/* agnx_write32(ctl, AGNX_INT_MASK, reg); */ - - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); - - /* Configure the Antenna */ - antenna_config(priv); - - /* Write 0x0 to Discovery Mode Enable detect G, B, A packet? */ - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0); - - reg = agnx_read32(ctl, AGNX_RXM_REQRATE); - reg |= 0x80000000; - agnx_write32(ctl, AGNX_RXM_REQRATE, reg); - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); - - /* enable radio on and the power LED */ - reg = agnx_read32(ctl, AGNX_SYSITF_GPIOUT); - reg &= ~0x1; - reg |= 0x2; - agnx_write32(ctl, AGNX_SYSITF_GPIOUT, reg); - - reg = agnx_read32(ctl, AGNX_TXM_CTL); - reg |= 0x1; - agnx_write32(ctl, AGNX_TXM_CTL, reg); -} /* radio_channel_set */ - -static void base_band_filter_calibrate(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1700); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1001); - agnx_write32(ctl, AGNX_GCR_FORCECTLCLK, 0x0); - spi_rc_write(ctl, RF_CHIP0, 0x27); - spi_rc_write(ctl, RF_CHIP1, 0x27); - spi_rc_write(ctl, RF_CHIP2, 0x27); - agnx_write32(ctl, AGNX_GCR_FORCECTLCLK, 0x1); -} - -static void print_offset(struct agnx_priv *priv, u32 chain) -{ - void __iomem *ctl = priv->ctl; - u32 offset; - - iowrite32((chain), ctl + AGNX_ACI_SELCHAIN); - udelay(10); - offset = (ioread32(ctl + AGNX_ACI_OFFSET)); - printk(PFX "Chain is 0x%x, Offset is 0x%x\n", chain, offset); -} - -void print_offsets(struct agnx_priv *priv) -{ - print_offset(priv, 0); - print_offset(priv, 4); - print_offset(priv, 1); - print_offset(priv, 5); - print_offset(priv, 2); - print_offset(priv, 6); -} - - -struct chains { - u32 cali; /* calibrate value*/ - -#define NEED_CALIBRATE 0 -#define SUCCESS_CALIBRATE 1 - int status; -}; - -static void chain_calibrate(struct agnx_priv *priv, struct chains *chains, - unsigned int num) -{ - void __iomem *ctl = priv->ctl; - u32 calibra = chains[num].cali; - - if (num < 3) - calibra |= 0x1400; - else - calibra |= 0x1500; - - switch (num) { - case 0: - case 4: - spi_rf_write(ctl, RF_CHIP0, calibra); - break; - case 1: - case 5: - spi_rf_write(ctl, RF_CHIP1, calibra); - break; - case 2: - case 6: - spi_rf_write(ctl, RF_CHIP2, calibra); - break; - default: - BUG(); - } -} /* chain_calibrate */ - -static inline void get_calibrete_value(struct agnx_priv *priv, struct chains *chains, - unsigned int num) -{ - void __iomem *ctl = priv->ctl; - u32 offset; - - iowrite32((num), ctl + AGNX_ACI_SELCHAIN); - /* FIXME */ - udelay(10); - offset = (ioread32(ctl + AGNX_ACI_OFFSET)); - - if (offset < 0xf) { - chains[num].status = SUCCESS_CALIBRATE; - return; - } - - if (num == 0 || num == 1 || num == 2) { - if (0 == chains[num].cali) - chains[num].cali = 0xff; - else - chains[num].cali--; - } else - chains[num].cali++; - - chains[num].status = NEED_CALIBRATE; -} - -static inline void calibra_delay(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - unsigned int i = 100; - - wmb(); - while (--i) { - reg = (ioread32(ctl + AGNX_ACI_STATUS)); - if (reg == 0x4000) - break; - udelay(10); - } - if (!i) - printk(PFX "calibration failed\n"); -} - -void do_calibration(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - struct chains chains[7]; - unsigned int i, j; - AGNX_TRACE; - - for (i = 0; i < 7; i++) { - if (i == 3) - continue; - - chains[i].cali = 0x7f; - chains[i].status = NEED_CALIBRATE; - } - - /* FIXME 0x300 is a magic number */ - for (j = 0; j < 0x300; j++) { - if (chains[0].status == SUCCESS_CALIBRATE && - chains[1].status == SUCCESS_CALIBRATE && - chains[2].status == SUCCESS_CALIBRATE && - chains[4].status == SUCCESS_CALIBRATE && - chains[5].status == SUCCESS_CALIBRATE && - chains[6].status == SUCCESS_CALIBRATE) - break; - - /* Attention, there is no chain 3 */ - for (i = 0; i < 7; i++) { - if (i == 3) - continue; - if (chains[i].status == NEED_CALIBRATE) - chain_calibrate(priv, chains, i); - } - /* Write 0x1 to Calibration Measure */ - iowrite32((0x1), ctl + AGNX_ACI_MEASURE); - calibra_delay(priv); - - for (i = 0; i < 7; i++) { - if (i == 3) - continue; - - get_calibrete_value(priv, chains, i); - } - } - printk(PFX "Clibrate times is %d\n", j); - print_offsets(priv); -} /* do_calibration */ - -void antenna_calibrate(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - AGNX_TRACE; - - agnx_write32(ctl, AGNX_GCR_NLISTANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NMEASANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NACTIANT, 0x3); - agnx_write32(ctl, AGNX_GCR_NCAPTANT, 0x3); - - agnx_write32(ctl, AGNX_GCR_ANTCFG, 0x1f); - agnx_write32(ctl, AGNX_GCR_BOACT, 0x24); - agnx_write32(ctl, AGNX_GCR_BOINACT, 0x24); - agnx_write32(ctl, AGNX_GCR_BODYNA, 0x20); - agnx_write32(ctl, AGNX_GCR_THD0A, 0x64); - agnx_write32(ctl, AGNX_GCR_THD0AL, 0x64); - agnx_write32(ctl, AGNX_GCR_THD0B, 0x46); - agnx_write32(ctl, AGNX_GCR_THD0BTFEST, 0x3c); - agnx_write32(ctl, AGNX_GCR_SIGHTH, 0x64); - agnx_write32(ctl, AGNX_GCR_SIGLTH, 0x30); - - spi_rc_write(ctl, RF_CHIP0, 0x20); - /* Fixme */ - udelay(80); - /* 1. Should read 0x0 */ - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (0x0 != reg) - printk(KERN_WARNING PFX "Unmatched rf chips result\n"); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1000); - - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - - spi_rc_write(ctl, RF_CHIP0, 0x22); - udelay(80); - reg = agnx_read32(ctl, AGNX_SPI_RLSW); - if (0x0 != reg) - printk(KERN_WARNING PFX "Unmatched rf chips result\n"); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1005); - - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x1); - agnx_write32(ctl, AGNX_GCR_RSTGCTL, 0x0); - - reg = agnx_read32(ctl, AGNX_PM_SOFTRST); - reg |= 0x1c000032; - agnx_write32(ctl, AGNX_PM_SOFTRST, reg); - reg = agnx_read32(ctl, AGNX_PM_PLLCTL); - reg |= 0x0003f07; - agnx_write32(ctl, AGNX_PM_PLLCTL, reg); - - reg = agnx_read32(ctl, 0xec50); - reg |= 0x40; - agnx_write32(ctl, 0xec50, reg); - - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0xff8); - agnx_write32(ctl, AGNX_GCR_DISCOVMOD, 0x3); - - agnx_write32(ctl, AGNX_GCR_CHAINNUM, 0x6); - agnx_write32(ctl, 0x19874, 0x0); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1700); - - /* Calibrate the BaseBandFilter */ - base_band_filter_calibrate(priv); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1002); - - agnx_write32(ctl, AGNX_GCR_GAINSET0, 0x1d); - agnx_write32(ctl, AGNX_GCR_GAINSET1, 0x1d); - agnx_write32(ctl, AGNX_GCR_GAINSET2, 0x1d); - agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x1); - - agnx_write32(ctl, AGNX_ACI_MODE, 0x1); - agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); - - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x27); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1400); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1500); - - /* Measure Calibration */ - agnx_write32(ctl, AGNX_ACI_MEASURE, 0x1); - calibra_delay(priv); - - /* do calibration */ - do_calibration(priv); - - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x21); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - agnx_write32(ctl, AGNX_ACI_LEN, 0xf); - - reg = agnx_read32(ctl, AGNX_GCR_GAINSET0); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET0, reg); - reg = agnx_read32(ctl, AGNX_GCR_GAINSET1); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET1, reg); - reg = agnx_read32(ctl, AGNX_GCR_GAINSET2); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET2, reg); - - agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x0); - disable_receiver(priv); -} /* antenna_calibrate */ - -void __antenna_calibrate(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - /* Calibrate the BaseBandFilter */ - /* base_band_filter_calibrate(priv); */ - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1002); - - - agnx_write32(ctl, AGNX_GCR_GAINSET0, 0x1d); - agnx_write32(ctl, AGNX_GCR_GAINSET1, 0x1d); - agnx_write32(ctl, AGNX_GCR_GAINSET2, 0x1d); - - agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x1); - - agnx_write32(ctl, AGNX_ACI_MODE, 0x1); - agnx_write32(ctl, AGNX_ACI_LEN, 0x3ff); - - - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x27); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1400); - spi_rf_write(ctl, RF_CHIP0|RF_CHIP1|RF_CHIP2, 0x1500); - /* Measure Calibration */ - agnx_write32(ctl, AGNX_ACI_MEASURE, 0x1); - calibra_delay(priv); - do_calibration(priv); - agnx_write32(ctl, AGNX_GCR_RXOVERIDE, 0x0); - - agnx_write32(ctl, AGNX_ACI_TIMER1, 0x21); - agnx_write32(ctl, AGNX_ACI_TIMER2, 0x27); - - agnx_write32(ctl, AGNX_ACI_LEN, 0xf); - - reg = agnx_read32(ctl, AGNX_GCR_GAINSET0); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET0, reg); - reg = agnx_read32(ctl, AGNX_GCR_GAINSET1); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET1, reg); - reg = agnx_read32(ctl, AGNX_GCR_GAINSET2); - reg &= 0xf; - agnx_write32(ctl, AGNX_GCR_GAINSET2, reg); - - - agnx_write32(ctl, AGNX_GCR_GAINSETWRITE, 0x0); - - /* Write 0x3 Gain Control Discovery Mode */ - enable_receiver(priv); -} - -int agnx_set_channel(struct agnx_priv *priv, unsigned int channel) -{ - AGNX_TRACE; - - printk(KERN_ERR PFX "Channel is %d %s\n", channel, __func__); - radio_channel_set(priv, channel); - return 0; -} diff --git a/drivers/staging/agnx/sta.c b/drivers/staging/agnx/sta.c deleted file mode 100644 index 3e7db5e2811a..000000000000 --- a/drivers/staging/agnx/sta.c +++ /dev/null @@ -1,218 +0,0 @@ -#include -#include -#include "phy.h" -#include "sta.h" -#include "debug.h" - -void hash_read(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id) -{ - void __iomem *ctl = priv->ctl; - - reglo &= 0xFFFF; - reglo |= 0x30000000; - reglo |= 0x40000000; /* Set status busy */ - reglo |= sta_id << 16; - - iowrite32(0, ctl + AGNX_RXM_HASH_CMD_FLAG); - iowrite32(reghi, ctl + AGNX_RXM_HASH_CMD_HIGH); - iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); - - reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_HIGH); - reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); - printk(PFX "RX hash cmd are : %.8x%.8x\n", reghi, reglo); -} - -void hash_write(struct agnx_priv *priv, const u8 *mac_addr, u8 sta_id) -{ - void __iomem *ctl = priv->ctl; - u32 reghi, reglo; - - if (!is_valid_ether_addr(mac_addr)) - printk(KERN_WARNING PFX "Update hash table: Invalid hwaddr!\n"); - - reghi = mac_addr[0] << 24 | mac_addr[1] << 16 | mac_addr[2] << 8 | mac_addr[3]; - reglo = mac_addr[4] << 8 | mac_addr[5]; - reglo |= 0x10000000; /* Set hash commmand */ - reglo |= 0x40000000; /* Set status busy */ - reglo |= sta_id << 16; - - iowrite32(0, ctl + AGNX_RXM_HASH_CMD_FLAG); - iowrite32(reghi, ctl + AGNX_RXM_HASH_CMD_HIGH); - iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); - - reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); - if (!(reglo & 0x80000000)) - printk(KERN_WARNING PFX "Update hash table failed\n"); -} - -void hash_delete(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id) -{ - void __iomem *ctl = priv->ctl; - - reglo &= 0xFFFF; - reglo |= 0x20000000; - reglo |= 0x40000000; /* Set status busy */ - reglo |= sta_id << 16; - - iowrite32(0, ctl + AGNX_RXM_HASH_CMD_FLAG); - iowrite32(reghi, ctl + AGNX_RXM_HASH_CMD_HIGH); - iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); - reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_HIGH); - - reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); - printk(PFX "RX hash cmd are : %.8x%.8x\n", reghi, reglo); - -} - -void hash_dump(struct agnx_priv *priv, u8 sta_id) -{ - void __iomem *ctl = priv->ctl; - u32 reghi, reglo; - - reglo = 0x40000000; /* status bit */ - iowrite32(reglo, ctl + AGNX_RXM_HASH_CMD_LOW); - iowrite32(sta_id << 16, ctl + AGNX_RXM_HASH_DUMP_DATA); - - udelay(80); - - reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_HIGH); - reglo = ioread32(ctl + AGNX_RXM_HASH_CMD_LOW); - printk(PFX "hash cmd are : %.8x%.8x\n", reghi, reglo); - reghi = ioread32(ctl + AGNX_RXM_HASH_CMD_FLAG); - printk(PFX "hash flag is : %.8x\n", reghi); - reghi = ioread32(ctl + AGNX_RXM_HASH_DUMP_MST); - reglo = ioread32(ctl + AGNX_RXM_HASH_DUMP_LST); - printk(PFX "hash dump mst lst: %.8x%.8x\n", reghi, reglo); - reghi = ioread32(ctl + AGNX_RXM_HASH_DUMP_DATA); - printk(PFX "hash dump data: %.8x\n", reghi); -} - -void get_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, unsigned int sta_idx) -{ - void __iomem *ctl = priv->ctl; - memcpy_fromio(power, ctl + AGNX_TXM_STAPOWTEMP + sizeof(*power) * sta_idx, - sizeof(*power)); -} - -inline void -set_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, unsigned int sta_idx) -{ - void __iomem *ctl = priv->ctl; - /* FIXME 2. Write Template to offset + station number */ - memcpy_toio(ctl + AGNX_TXM_STAPOWTEMP + sizeof(*power) * sta_idx, - power, sizeof(*power)); -} - - -void get_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, - unsigned int sta_idx, unsigned int wq_idx) -{ - void __iomem *data = priv->data; - memcpy_fromio(tx_wq, data + AGNX_PDU_TX_WQ + sizeof(*tx_wq) * STA_TX_WQ_NUM * sta_idx + - sizeof(*tx_wq) * wq_idx, sizeof(*tx_wq)); - -} - -inline void set_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, - unsigned int sta_idx, unsigned int wq_idx) -{ - void __iomem *data = priv->data; - memcpy_toio(data + AGNX_PDU_TX_WQ + sizeof(*tx_wq) * STA_TX_WQ_NUM * sta_idx + - sizeof(*tx_wq) * wq_idx, tx_wq, sizeof(*tx_wq)); -} - - -void get_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx) -{ - void __iomem *data = priv->data; - - memcpy_fromio(sta, data + AGNX_PDUPOOL + sizeof(*sta) * sta_idx, - sizeof(*sta)); -} - -inline void set_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx) -{ - void __iomem *data = priv->data; - - memcpy_toio(data + AGNX_PDUPOOL + sizeof(*sta) * sta_idx, - sta, sizeof(*sta)); -} - -/* FIXME */ -void sta_power_init(struct agnx_priv *priv, unsigned int sta_idx) -{ - struct agnx_sta_power power; - u32 reg; - AGNX_TRACE; - - memset(&power, 0, sizeof(power)); - reg = agnx_set_bits(EDCF, EDCF_SHIFT, 0x1); - power.reg = cpu_to_le32(reg); - set_sta_power(priv, &power, sta_idx); - udelay(40); -} /* add_power_template */ - - -/* @num: The #number of station that is visible to the card */ -static void sta_tx_workqueue_init(struct agnx_priv *priv, unsigned int sta_idx) -{ - struct agnx_sta_tx_wq tx_wq; - u32 reg; - unsigned int i; - - memset(&tx_wq, 0, sizeof(tx_wq)); - - reg = agnx_set_bits(WORK_QUEUE_VALID, WORK_QUEUE_VALID_SHIFT, 1); - reg |= agnx_set_bits(WORK_QUEUE_ACK_TYPE, WORK_QUEUE_ACK_TYPE_SHIFT, 1); -/* reg |= agnx_set_bits(WORK_QUEUE_ACK_TYPE, WORK_QUEUE_ACK_TYPE_SHIFT, 0); */ - tx_wq.reg2 |= cpu_to_le32(reg); - - /* Suppose all 8 traffic class are used */ - for (i = 0; i < STA_TX_WQ_NUM; i++) - set_sta_tx_wq(priv, &tx_wq, sta_idx, i); -} /* sta_tx_workqueue_init */ - - -static void sta_traffic_init(struct agnx_sta_traffic *traffic) -{ - u32 reg; - memset(traffic, 0, sizeof(*traffic)); - - reg = agnx_set_bits(NEW_PACKET, NEW_PACKET_SHIFT, 1); - reg |= agnx_set_bits(TRAFFIC_VALID, TRAFFIC_VALID_SHIFT, 1); -/* reg |= agnx_set_bits(TRAFFIC_ACK_TYPE, TRAFFIC_ACK_TYPE_SHIFT, 1); */ - traffic->reg0 = cpu_to_le32(reg); - - /* 3. setting RX Sequence Number to 4095 */ - reg = agnx_set_bits(RX_SEQUENCE_NUM, RX_SEQUENCE_NUM_SHIFT, 4095); - traffic->reg1 = cpu_to_le32(reg); -} - - -/* @num: The #number of station that is visible to the card */ -void sta_init(struct agnx_priv *priv, unsigned int sta_idx) -{ - /* FIXME the length of sta is 256 bytes Is that - * dangerous to stack overflow? */ - struct agnx_sta sta; - u32 reg; - int i; - - memset(&sta, 0, sizeof(sta)); - /* Set valid to 1 */ - reg = agnx_set_bits(STATION_VALID, STATION_VALID_SHIFT, 1); - /* Set Enable Concatenation to 0 (?) */ - reg |= agnx_set_bits(ENABLE_CONCATENATION, ENABLE_CONCATENATION_SHIFT, 0); - /* Set Enable Decompression to 0 (?) */ - reg |= agnx_set_bits(ENABLE_DECOMPRESSION, ENABLE_DECOMPRESSION_SHIFT, 0); - sta.reg = cpu_to_le32(reg); - - /* Initialize each of the Traffic Class Structures by: */ - for (i = 0; i < 8; i++) - sta_traffic_init(sta.traffic + i); - - set_sta(priv, &sta, sta_idx); - sta_tx_workqueue_init(priv, sta_idx); -} /* sta_descriptor_init */ - - diff --git a/drivers/staging/agnx/sta.h b/drivers/staging/agnx/sta.h deleted file mode 100644 index fd504e3f3870..000000000000 --- a/drivers/staging/agnx/sta.h +++ /dev/null @@ -1,222 +0,0 @@ -#ifndef AGNX_STA_H_ -#define AGNX_STA_H_ - -#define STA_TX_WQ_NUM 8 /* The number of TX workqueue one STA has */ - -struct agnx_hash_cmd { - __be32 cmdhi; -#define MACLO 0xFFFF0000 -#define MACLO_SHIFT 16 -#define STA_ID 0x0000FFF0 -#define STA_ID_SHIFT 4 -#define CMD 0x0000000C -#define CMD_SHIFT 2 -#define STATUS 0x00000002 -#define STATUS_SHIFT 1 -#define PASS 0x00000001 -#define PASS_SHIFT 1 - __be32 cmdlo; -} __attribute__((__packed__)); - - -/* - * Station Power Template - * FIXME Just for agn100 yet - */ -struct agnx_sta_power { - __le32 reg; -#define SIGNAL 0x000000FF /* signal */ -#define SIGNAL_SHIFT 0 -#define RATE 0x00000F00 -#define RATE_SHIFT 8 -#define TIFS 0x00001000 -#define TIFS_SHIFT 12 -#define EDCF 0x00002000 -#define EDCF_SHIFT 13 -#define CHANNEL_BOND 0x00004000 -#define CHANNEL_BOND_SHIFT 14 -#define PHY_MODE 0x00038000 -#define PHY_MODE_SHIFT 15 -#define POWER_LEVEL 0x007C0000 -#define POWER_LEVEL_SHIFT 18 -#define NUM_TRANSMITTERS 0x00800000 -#define NUM_TRANSMITTERS_SHIFT 23 -} __attribute__((__packed__)); - -/* - * TX Workqueue Descriptor - */ -struct agnx_sta_tx_wq { - __le32 reg0; -#define HEAD_POINTER_LOW 0xFF000000 /* Head pointer low */ -#define HEAD_POINTER_LOW_SHIFT 24 -#define TAIL_POINTER 0x00FFFFFF /* Tail pointer */ -#define TAIL_POINTER_SHIFT 0 - - __le32 reg3; -#define ACK_POINTER_LOW 0xFFFF0000 /* ACK pointer low */ -#define ACK_POINTER_LOW_SHIFT 16 -#define HEAD_POINTER_HIGH 0x0000FFFF /* Head pointer high */ -#define HEAD_POINTER_HIGH_SHIFT 0 - - __le32 reg1; -/* ACK timeout tail packet count */ -#define ACK_TIMOUT_TAIL_PACK_CNT 0xFFF00000 -#define ACK_TIMOUT_TAIL_PACK_CNT_SHIFT 20 -/* Head timeout tail packet count */ -#define HEAD_TIMOUT_TAIL_PACK_CNT 0x000FFF00 -#define HEAD_TIMOUT_TAIL_PACK_CNT_SHIFT 8 -#define ACK_POINTER_HIGH 0x000000FF /* ACK pointer high */ -#define ACK_POINTER_HIGH_SHIFT 0 - - __le32 reg2; -#define WORK_QUEUE_VALID 0x80000000 /* valid */ -#define WORK_QUEUE_VALID_SHIFT 31 -#define WORK_QUEUE_ACK_TYPE 0x40000000 /* ACK type */ -#define WORK_QUEUE_ACK_TYPE_SHIFT 30 -/* Head timeout window limit fragmentation count */ -#define HEAD_TIMOUT_WIN_LIM_FRAG_CNT 0x3FFF0000 -#define HEAD_TIMOUT_WIN_LIM_FRAG_CNT_SHIFT 16 -/* Head timeout window limit byte count */ -#define HEAD_TIMOUT_WIN_LIM_BYTE_CNT 0x0000FFFF -#define HEAD_TIMOUT_WIN_LIM_BYTE_CNT_SHIFT 0 -} __attribute__((__packed__)); - - -/* - * Traffic Class Structure - */ -struct agnx_sta_traffic { - __le32 reg0; -#define ACK_TIMOUT_CNT 0xFF800000 /* ACK Timeout Counts */ -#define ACK_TIMOUT_CNT_SHIFT 23 -#define TRAFFIC_ACK_TYPE 0x00600000 /* ACK Type */ -#define TRAFFIC_ACK_TYPE_SHIFT 21 -#define NEW_PACKET 0x00100000 /* New Packet */ -#define NEW_PACKET_SHIFT 20 -#define TRAFFIC_VALID 0x00080000 /* Valid */ -#define TRAFFIC_VALID_SHIFT 19 -#define RX_HDR_DESC_POINTER 0x0007FFFF /* RX Header Descripter pointer */ -#define RX_HDR_DESC_POINTER_SHIFT 0 - - __le32 reg1; -#define RX_PACKET_TIMESTAMP 0xFFFF0000 /* RX Packet Timestamp */ -#define RX_PACKET_TIMESTAMP_SHIFT 16 -#define TRAFFIC_RESERVED 0x0000E000 /* Reserved */ -#define TRAFFIC_RESERVED_SHIFT 13 -#define SV 0x00001000 /* sv */ -#define SV_SHIFT 12 -#define RX_SEQUENCE_NUM 0x00000FFF /* RX Sequence Number */ -#define RX_SEQUENCE_NUM_SHIFT 0 - - __le32 tx_replay_cnt_low; /* TX Replay Counter Low */ - - __le16 tx_replay_cnt_high; /* TX Replay Counter High */ - __le16 rx_replay_cnt_high; /* RX Replay Counter High */ - - __be32 rx_replay_cnt_low; /* RX Replay Counter Low */ -} __attribute__((__packed__)); - -/* - * Station Descriptors - */ -struct agnx_sta { - __le32 tx_session_keys[4]; /* Transmit Session Key (0-3) */ - __le32 rx_session_keys[4]; /* Receive Session Key (0-3) */ - - __le32 reg; -#define ID_1 0xC0000000 /* id 1 */ -#define ID_1_SHIFT 30 -#define ID_0 0x30000000 /* id 0 */ -#define ID_0_SHIFT 28 -#define ENABLE_CONCATENATION 0x0FF00000 /* Enable concatenation */ -#define ENABLE_CONCATENATION_SHIFT 20 -#define ENABLE_DECOMPRESSION 0x000FF000 /* Enable decompression */ -#define ENABLE_DECOMPRESSION_SHIFT 12 -#define STA_RESERVED 0x00000C00 /* Reserved */ -#define STA_RESERVED_SHIFT 10 -#define EAP 0x00000200 /* EAP */ -#define EAP_SHIFT 9 -#define ED_NULL 0x00000100 /* ED NULL */ -#define ED_NULL_SHIFT 8 -#define ENCRYPTION_POLICY 0x000000E0 /* Encryption Policy */ -#define ENCRYPTION_POLICY_SHIFT 5 -#define DEFINED_KEY_ID 0x00000018 /* Defined Key ID */ -#define DEFINED_KEY_ID_SHIFT 3 -#define FIXED_KEY 0x00000004 /* Fixed Key */ -#define FIXED_KEY_SHIFT 2 -#define KEY_VALID 0x00000002 /* Key Valid */ -#define KEY_VALID_SHIFT 1 -#define STATION_VALID 0x00000001 /* Station Valid */ -#define STATION_VALID_SHIFT 0 - - __le32 tx_aes_blks_unicast; /* TX AES Blks Unicast */ - __le32 rx_aes_blks_unicast; /* RX AES Blks Unicast */ - - __le16 aes_format_err_unicast_cnt; /* AES Format Error Unicast Counts */ - __le16 aes_replay_unicast; /* AES Replay Unicast */ - - __le16 aes_decrypt_err_unicast; /* AES Decrypt Error Unicast */ - __le16 aes_decrypt_err_default; /* AES Decrypt Error default */ - - __le16 single_retry_packets; /* Single Retry Packets */ - __le16 failed_tx_packets; /* Failed Tx Packets */ - - __le16 muti_retry_packets; /* Multiple Retry Packets */ - __le16 ack_timeouts; /* ACK Timeouts */ - - __le16 frag_tx_cnt; /* Fragment TX Counts */ - __le16 rts_brq_sent; /* RTS Brq Sent */ - - __le16 tx_packets; /* TX Packets */ - __le16 cts_back_timeout; /* CTS Back Timeout */ - - __le32 phy_stats_high; /* PHY Stats High */ - __le32 phy_stats_low; /* PHY Stats Low */ - - struct agnx_sta_traffic traffic[8]; /* Traffic Class Structure (8) */ - - __le16 traffic_class0_frag_success; /* Traffic Class 0 Fragment Success */ - __le16 traffic_class1_frag_success; /* Traffic Class 1 Fragment Success */ - __le16 traffic_class2_frag_success; /* Traffic Class 2 Fragment Success */ - __le16 traffic_class3_frag_success; /* Traffic Class 3 Fragment Success */ - __le16 traffic_class4_frag_success; /* Traffic Class 4 Fragment Success */ - __le16 traffic_class5_frag_success; /* Traffic Class 5 Fragment Success */ - __le16 traffic_class6_frag_success; /* Traffic Class 6 Fragment Success */ - __le16 traffic_class7_frag_success; /* Traffic Class 7 Fragment Success */ - - __le16 num_frag_non_prime_rates; /* number of Fragments for non-prime rates */ - __le16 ack_timeout_non_prime_rates; /* ACK Timeout for non-prime rates */ - -} __attribute__((__packed__)); - - -struct agnx_beacon_hdr { - struct agnx_sta_power power; /* Tx Station Power Template */ - u8 phy_hdr[6]; /* PHY Hdr */ - u8 frame_len_lo; /* Frame Length Lo */ - u8 frame_len_hi; /* Frame Length Hi */ - u8 mac_hdr[24]; /* MAC Header */ - /* FIXME */ - /* 802.11(abg) beacon */ -} __attribute__((__packed__)); - -void hash_write(struct agnx_priv *priv, const u8 *mac_addr, u8 sta_id); -void hash_dump(struct agnx_priv *priv, u8 sta_id); -void hash_read(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id); -void hash_delete(struct agnx_priv *priv, u32 reghi, u32 reglo, u8 sta_id); - -void get_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, unsigned int sta_idx); -void set_sta_power(struct agnx_priv *priv, struct agnx_sta_power *power, - unsigned int sta_idx); -void get_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, - unsigned int sta_idx, unsigned int wq_idx); -void set_sta_tx_wq(struct agnx_priv *priv, struct agnx_sta_tx_wq *tx_wq, - unsigned int sta_idx, unsigned int wq_idx); -void get_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx); -void set_sta(struct agnx_priv *priv, struct agnx_sta *sta, unsigned int sta_idx); - -void sta_power_init(struct agnx_priv *priv, unsigned int num); -void sta_init(struct agnx_priv *priv, unsigned int num); - -#endif /* AGNX_STA_H_ */ diff --git a/drivers/staging/agnx/table.c b/drivers/staging/agnx/table.c deleted file mode 100644 index b52fef9db0e3..000000000000 --- a/drivers/staging/agnx/table.c +++ /dev/null @@ -1,168 +0,0 @@ -#include -#include -#include "agnx.h" -#include "debug.h" -#include "phy.h" - -static const u32 -tx_fir_table[] = { 0x19, 0x5d, 0xce, 0x151, 0x1c3, 0x1ff, 0x1ea, 0x17c, 0xcf, - 0x19, 0x38e, 0x350, 0x362, 0x3ad, 0x5, 0x44, 0x59, 0x49, - 0x21, 0x3f7, 0x3e0, 0x3e3, 0x3f3, 0x0 }; - -void tx_fir_table_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - - for (i = 0; i < ARRAY_SIZE(tx_fir_table); i++) - iowrite32(tx_fir_table[i], ctl + AGNX_FIR_BASE + i*4); -} /* fir_table_setup */ - - -static const u32 -gain_table[] = { 0x8, 0x8, 0xf, 0x13, 0x17, 0x1b, 0x1f, 0x23, 0x27, 0x2b, - 0x2f, 0x33, 0x37, 0x3b, 0x3f, 0x43, 0x47, 0x4b, 0x4f, - 0x53, 0x57, 0x5b, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, - 0x5f, 0x5f, 0x5f, 0x5f }; - -void gain_table_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - int i; - - for (i = 0; i < ARRAY_SIZE(gain_table); i++) { - iowrite32(gain_table[i], ctl + AGNX_GAIN_TABLE + i*4); - iowrite32(gain_table[i], ctl + AGNX_GAIN_TABLE + i*4 + 0x80); - } -} /* gain_table_init */ - -void monitor_gain_table_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - unsigned int i; - - for (i = 0; i < 0x44; i += 4) { - iowrite32(0x61, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0x61, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0x44; i < 0x64; i += 4) { - iowrite32(0x6e, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0x6e, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0x64; i < 0x94; i += 4) { - iowrite32(0x7a, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0x7a, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0x94; i < 0xdc; i += 4) { - iowrite32(0x87, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0x87, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0xdc; i < 0x148; i += 4) { - iowrite32(0x95, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0x95, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0x148; i < 0x1e8; i += 4) { - iowrite32(0xa2, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0xa2, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } - for (i = 0x1e8; i <= 0x1fc; i += 4) { - iowrite32(0xb0, ctl + AGNX_MONGCR_BASE + i); - iowrite32(0xb0, ctl + AGNX_MONGCR_BASE + 0x200 + i); - } -} /* monitor_gain_table_init */ - - -void routing_table_init(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - unsigned int type, subtype; - u32 reg; - - disable_receiver(priv); - - for (type = 0; type < 0x3; type++) { - for (subtype = 0; subtype < 0x10; subtype++) { - /* 1. Set Routing table to R/W and to Return status on Read */ - reg = (type << ROUTAB_TYPE_SHIFT) | - (subtype << ROUTAB_SUBTYPE_SHIFT); - reg |= (1 << ROUTAB_RW_SHIFT) | (1 << ROUTAB_STATUS_SHIFT); - if (type == ROUTAB_TYPE_DATA) { - /* NULL goes to RFP */ - if (subtype == ROUTAB_SUBTYPE_NULL) -/* reg |= ROUTAB_ROUTE_RFP; */ - reg |= ROUTAB_ROUTE_CPU; - /* QOS NULL goes to CPU */ - else if (subtype == ROUTAB_SUBTYPE_QOSNULL) - reg |= ROUTAB_ROUTE_CPU; - /* All Data and QOS data subtypes go to Encryption */ - else if ((subtype == ROUTAB_SUBTYPE_DATA) || - (subtype == ROUTAB_SUBTYPE_DATAACK) || - (subtype == ROUTAB_SUBTYPE_DATAPOLL) || - (subtype == ROUTAB_SUBTYPE_DATAPOLLACK) || - (subtype == ROUTAB_SUBTYPE_QOSDATA) || - (subtype == ROUTAB_SUBTYPE_QOSDATAACK) || - (subtype == ROUTAB_SUBTYPE_QOSDATAPOLL) || - (subtype == ROUTAB_SUBTYPE_QOSDATAACKPOLL)) - reg |= ROUTAB_ROUTE_ENCRY; -/* reg |= ROUTAB_ROUTE_CPU; */ - /*Drop NULL and QOS NULL ack, poll and poll ack*/ - else if ((subtype == ROUTAB_SUBTYPE_NULLACK) || - (subtype == ROUTAB_SUBTYPE_QOSNULLACK) || - (subtype == ROUTAB_SUBTYPE_NULLPOLL) || - (subtype == ROUTAB_SUBTYPE_QOSNULLPOLL) || - (subtype == ROUTAB_SUBTYPE_NULLPOLLACK) || - (subtype == ROUTAB_SUBTYPE_QOSNULLPOLLACK)) -/* reg |= ROUTAB_ROUTE_DROP; */ - reg |= ROUTAB_ROUTE_CPU; - } else { - reg |= (ROUTAB_ROUTE_CPU); - } - iowrite32(reg, ctl + AGNX_RXM_ROUTAB); - /* Check to verify that the status bit cleared */ - routing_table_delay(); - } - } - enable_receiver(priv); -} /* routing_table_init */ - -void tx_engine_lookup_tbl_init(struct agnx_priv *priv) -{ - void __iomem *data = priv->data; - unsigned int i; - - for (i = 0; i <= 28; i += 4) - iowrite32(0xb00c, data + AGNX_ENGINE_LOOKUP_TBL + i); - for (i = 32; i <= 120; i += 8) { - iowrite32(0x1e58, data + AGNX_ENGINE_LOOKUP_TBL + i); - iowrite32(0xb00c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); - } - - for (i = 128; i <= 156; i += 4) - iowrite32(0x980c, data + AGNX_ENGINE_LOOKUP_TBL + i); - for (i = 160; i <= 248; i += 8) { - iowrite32(0x1858, data + AGNX_ENGINE_LOOKUP_TBL + i); - iowrite32(0x980c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); - } - - for (i = 256; i <= 284; i += 4) - iowrite32(0x980c, data + AGNX_ENGINE_LOOKUP_TBL + i); - for (i = 288; i <= 376; i += 8) { - iowrite32(0x1a58, data + AGNX_ENGINE_LOOKUP_TBL + i); - iowrite32(0x1858, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); - } - - for (i = 512; i <= 540; i += 4) - iowrite32(0xc00c, data + AGNX_ENGINE_LOOKUP_TBL + i); - for (i = 544; i <= 632; i += 8) { - iowrite32(0x2058, data + AGNX_ENGINE_LOOKUP_TBL + i); - iowrite32(0xc00c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); - } - - for (i = 640; i <= 668; i += 4) - iowrite32(0xc80c, data + AGNX_ENGINE_LOOKUP_TBL + i); - for (i = 672; i <= 764; i += 8) { - iowrite32(0x2258, data + AGNX_ENGINE_LOOKUP_TBL + i); - iowrite32(0xc80c, data + AGNX_ENGINE_LOOKUP_TBL + i + 4); - } -} - diff --git a/drivers/staging/agnx/table.h b/drivers/staging/agnx/table.h deleted file mode 100644 index f0626b5ee86b..000000000000 --- a/drivers/staging/agnx/table.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef AGNX_TABLE_H_ -#define AGNX_TABLE_H_ - -void tx_fir_table_init(struct agnx_priv *priv); -void gain_table_init(struct agnx_priv *priv); -void monitor_gain_table_init(struct agnx_priv *priv); -void routing_table_init(struct agnx_priv *priv); -void tx_engine_lookup_tbl_init(struct agnx_priv *priv); - -#endif /* AGNX_TABLE_H_ */ diff --git a/drivers/staging/agnx/xmit.c b/drivers/staging/agnx/xmit.c deleted file mode 100644 index 42db41070cf0..000000000000 --- a/drivers/staging/agnx/xmit.c +++ /dev/null @@ -1,836 +0,0 @@ -/** - * Airgo MIMO wireless driver - * - * Copyright (c) 2007 Li YanBo - - * Thanks for Jeff Williams do reverse engineer - * works and published the SPECS at http://airgo.wdwconsulting.net/mymoin - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include "agnx.h" -#include "debug.h" -#include "phy.h" - -unsigned int rx_frame_cnt; -/* unsigned int local_tx_sent_cnt = 0; */ - -static inline void disable_rx_engine(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - iowrite32(0x100, ctl + AGNX_CIR_RXCTL); - /* Wait for RX Control to have the Disable Rx Interrupt (0x100) set */ - ioread32(ctl + AGNX_CIR_RXCTL); -} - -static inline void enable_rx_engine(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - iowrite32(0x80, ctl + AGNX_CIR_RXCTL); - ioread32(ctl + AGNX_CIR_RXCTL); -} - -inline void disable_rx_interrupt(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - disable_rx_engine(priv); - reg = ioread32(ctl + AGNX_CIR_RXCFG); - reg &= ~0x20; - iowrite32(reg, ctl + AGNX_CIR_RXCFG); - ioread32(ctl + AGNX_CIR_RXCFG); -} - -inline void enable_rx_interrupt(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - u32 reg; - - reg = ioread32(ctl + AGNX_CIR_RXCFG); - reg |= 0x20; - iowrite32(reg, ctl + AGNX_CIR_RXCFG); - ioread32(ctl + AGNX_CIR_RXCFG); - enable_rx_engine(priv); -} - -static inline void rx_desc_init(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_desc *desc = priv->rx.desc + idx; - struct agnx_info *info = priv->rx.info + idx; - - memset(info, 0, sizeof(*info)); - - info->dma_len = IEEE80211_MAX_RTS_THRESHOLD + sizeof(struct agnx_hdr); - info->skb = dev_alloc_skb(info->dma_len); - if (info->skb == NULL) - agnx_bug("refill err"); - - info->mapping = pci_map_single(priv->pdev, skb_tail_pointer(info->skb), - info->dma_len, PCI_DMA_FROMDEVICE); - memset(desc, 0, sizeof(*desc)); - desc->dma_addr = cpu_to_be32(info->mapping); - /* Set the owner to the card */ - desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER); -} - -static inline void rx_desc_reinit(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_info *info = priv->rx.info + idx; - - /* Cause ieee80211 will free the skb buffer, so we needn't to free it again?! */ - pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE); - rx_desc_init(priv, idx); -} - -static inline void rx_desc_reusing(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_desc *desc = priv->rx.desc + idx; - struct agnx_info *info = priv->rx.info + idx; - - memset(desc, 0, sizeof(*desc)); - desc->dma_addr = cpu_to_be32(info->mapping); - /* Set the owner to the card */ - desc->frag = cpu_to_be32(be32_to_cpu(desc->frag) | OWNER); -} - -static void rx_desc_free(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_desc *desc = priv->rx.desc + idx; - struct agnx_info *info = priv->rx.info + idx; - - BUG_ON(!desc || !info); - if (info->mapping) - pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_FROMDEVICE); - if (info->skb) - dev_kfree_skb(info->skb); - memset(info, 0, sizeof(*info)); - memset(desc, 0, sizeof(*desc)); -} - -static inline void __tx_desc_free(struct agnx_priv *priv, - struct agnx_desc *desc, struct agnx_info *info) -{ - BUG_ON(!desc || !info); - /* TODO make sure mapping, skb and len are consistency */ - if (info->mapping) - pci_unmap_single(priv->pdev, info->mapping, - info->dma_len, PCI_DMA_TODEVICE); - if (info->type == PACKET) - dev_kfree_skb(info->skb); - - memset(info, 0, sizeof(*info)); - memset(desc, 0, sizeof(*desc)); -} - -static void txm_desc_free(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_desc *desc = priv->txm.desc + idx; - struct agnx_info *info = priv->txm.info + idx; - - __tx_desc_free(priv, desc, info); -} - -static void txd_desc_free(struct agnx_priv *priv, unsigned int idx) -{ - struct agnx_desc *desc = priv->txd.desc + idx; - struct agnx_info *info = priv->txd.info + idx; - - __tx_desc_free(priv, desc, info); -} - -int fill_rings(struct agnx_priv *priv) -{ - void __iomem *ctl = priv->ctl; - unsigned int i; - u32 reg; - AGNX_TRACE; - - priv->txd.idx_sent = priv->txm.idx_sent = 0; - priv->rx.idx = priv->txm.idx = priv->txd.idx = 0; - - for (i = 0; i < priv->rx.size; i++) - rx_desc_init(priv, i); - for (i = 0; i < priv->txm.size; i++) { - memset(priv->txm.desc + i, 0, sizeof(struct agnx_desc)); - memset(priv->txm.info + i, 0, sizeof(struct agnx_info)); - } - for (i = 0; i < priv->txd.size; i++) { - memset(priv->txd.desc + i, 0, sizeof(struct agnx_desc)); - memset(priv->txd.info + i, 0, sizeof(struct agnx_info)); - } - - /* FIXME Set the card RX TXM and TXD address */ - agnx_write32(ctl, AGNX_CIR_RXCMSTART, priv->rx.dma); - agnx_write32(ctl, AGNX_CIR_RXCMEND, priv->txm.dma); - - agnx_write32(ctl, AGNX_CIR_TXMSTART, priv->txm.dma); - agnx_write32(ctl, AGNX_CIR_TXMEND, priv->txd.dma); - - agnx_write32(ctl, AGNX_CIR_TXDSTART, priv->txd.dma); - agnx_write32(ctl, AGNX_CIR_TXDEND, priv->txd.dma + - sizeof(struct agnx_desc) * priv->txd.size); - - /* FIXME Relinquish control of rings to card */ - reg = agnx_read32(ctl, AGNX_CIR_BLKCTL); - reg &= ~0x800; - agnx_write32(ctl, AGNX_CIR_BLKCTL, reg); - return 0; -} /* fill_rings */ - -void unfill_rings(struct agnx_priv *priv) -{ - unsigned long flags; - unsigned int i; - AGNX_TRACE; - - spin_lock_irqsave(&priv->lock, flags); - - for (i = 0; i < priv->rx.size; i++) - rx_desc_free(priv, i); - for (i = 0; i < priv->txm.size; i++) - txm_desc_free(priv, i); - for (i = 0; i < priv->txd.size; i++) - txd_desc_free(priv, i); - - spin_unlock_irqrestore(&priv->lock, flags); -} - -/* Extract the bitrate out of a CCK PLCP header. - copy from bcm43xx driver */ -static inline u8 agnx_plcp_get_bitrate_cck(__be32 *phyhdr_11b) -{ - /* FIXME */ - switch (*(u8 *)phyhdr_11b) { - case 0x0A: - return 0; - case 0x14: - return 1; - case 0x37: - return 2; - case 0x6E: - return 3; - } - agnx_bug("Wrong plcp rate"); - return 0; -} - -/* FIXME */ -static inline u8 agnx_plcp_get_bitrate_ofdm(__be32 *phyhdr_11g) -{ - u8 rate = *(u8 *)phyhdr_11g & 0xF; - - printk(PFX "G mode rate is 0x%x\n", rate); - return rate; -} - -/* FIXME */ -static void get_rx_stats(struct agnx_priv *priv, struct agnx_hdr *hdr, - struct ieee80211_rx_status *stat) -{ - void __iomem *ctl = priv->ctl; - u8 *rssi; - u32 noise; - /* FIXME just for test */ - int snr = 40; /* signal-to-noise ratio */ - - memset(stat, 0, sizeof(*stat)); - /* RSSI */ - rssi = (u8 *)&hdr->phy_stats_lo; -/* stat->ssi = (rssi[0] + rssi[1] + rssi[2]) / 3; */ - /* Noise */ - noise = ioread32(ctl + AGNX_GCR_NOISE0); - noise += ioread32(ctl + AGNX_GCR_NOISE1); - noise += ioread32(ctl + AGNX_GCR_NOISE2); - stat->noise = noise / 3; - /* Signal quality */ -/* snr = stat->ssi - stat->noise; */ - if (snr >= 0 && snr < 40) - stat->signal = 5 * snr / 2; - else if (snr >= 40) - stat->signal = 100; - else - stat->signal = 0; - - - if (hdr->_11b0 && !hdr->_11g0) { - stat->rate_idx = agnx_plcp_get_bitrate_cck(&hdr->_11b0); - } else if (!hdr->_11b0 && hdr->_11g0) { - printk(PFX "RX: Found G mode packet\n"); - stat->rate_idx = agnx_plcp_get_bitrate_ofdm(&hdr->_11g0); - } else - agnx_bug("Unknown packets type"); - - - stat->band = IEEE80211_BAND_2GHZ; - stat->freq = agnx_channels[priv->channel - 1].center_freq; -/* stat->antenna = 3; - stat->mactime = be32_to_cpu(hdr->time_stamp); - stat->channel = priv->channel; */ -} - -static inline void combine_hdr_frag(struct ieee80211_hdr *ieeehdr, - struct sk_buff *skb) -{ - u16 fctl; - unsigned int hdrlen; - - fctl = le16_to_cpu(ieeehdr->frame_control); - hdrlen = ieee80211_hdrlen(fctl); - /* FIXME */ - if (hdrlen < (2+2+6)/*minimum hdr*/ || - hdrlen > sizeof(struct ieee80211_mgmt)) { - printk(KERN_ERR PFX "hdr len is %d\n", hdrlen); - agnx_bug("Wrong ieee80211 hdr detected"); - } - skb_push(skb, hdrlen); - memcpy(skb->data, ieeehdr, hdrlen); -} /* combine_hdr_frag */ - -static inline int agnx_packet_check(struct agnx_priv *priv, struct agnx_hdr *agnxhdr, - unsigned packet_len) -{ - if (agnx_get_bits(CRC_FAIL, CRC_FAIL_SHIFT, be32_to_cpu(agnxhdr->reg1)) == 1) { - printk(PFX "RX: CRC check fail\n"); - goto drop; - } - if (packet_len > 2048) { - printk(PFX "RX: Too long packet detected\n"); - goto drop; - } - - /* FIXME Just usable for Promious Mode, for Manage mode exclude FCS */ -/* if (packet_len - sizeof(*agnxhdr) < FCS_LEN) { */ -/* printk(PFX "RX: Too short packet detected\n"); */ -/* goto drop; */ -/* } */ - return 0; -drop: - priv->stats.dot11FCSErrorCount++; - return -1; -} - -void handle_rx_irq(struct agnx_priv *priv) -{ - struct ieee80211_rx_status status; - unsigned int len; -/* AGNX_TRACE; */ - - do { - struct agnx_desc *desc; - u32 frag; - struct agnx_info *info; - struct agnx_hdr *hdr; - struct sk_buff *skb; - unsigned int i = priv->rx.idx % priv->rx.size; - - desc = priv->rx.desc + i; - frag = be32_to_cpu(desc->frag); - if (frag & OWNER) - break; - - info = priv->rx.info + i; - skb = info->skb; - hdr = (struct agnx_hdr *)(skb->data); - - len = (frag & PACKET_LEN) >> PACKET_LEN_SHIFT; - if (agnx_packet_check(priv, hdr, len) == -1) { - rx_desc_reusing(priv, i); - continue; - } - skb_put(skb, len); - - do { - u16 fctl; - fctl = le16_to_cpu(((struct ieee80211_hdr *)hdr->mac_hdr)->frame_control); - if ((fctl & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_BEACON)/* && !(fctl & IEEE80211_STYPE_BEACON)) */ - dump_ieee80211_hdr((struct ieee80211_hdr *)hdr->mac_hdr, "RX"); - } while (0); - - if (hdr->_11b0 && !hdr->_11g0) { -/* int j; - u16 fctl = le16_to_cpu(((struct ieee80211_hdr *)hdr->mac_hdr) - ->frame_control); - if ( (fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { - agnx_print_rx_hdr(hdr); - agnx_print_sta(priv, BSSID_STAID); - for (j = 0; j < 8; j++) - agnx_print_sta_tx_wq(priv, BSSID_STAID, j); - } */ - - get_rx_stats(priv, hdr, &status); - skb_pull(skb, sizeof(*hdr)); - combine_hdr_frag((struct ieee80211_hdr *)hdr->mac_hdr, skb); - } else if (!hdr->_11b0 && hdr->_11g0) { -/* int j; */ - agnx_print_rx_hdr(hdr); - agnx_print_sta(priv, BSSID_STAID); -/* for (j = 0; j < 8; j++) */ - agnx_print_sta_tx_wq(priv, BSSID_STAID, 0); - - print_hex_dump_bytes("agnx: RX_PACKET: ", DUMP_PREFIX_NONE, - skb->data, skb->len + 8); - -/* if (agnx_plcp_get_bitrate_ofdm(&hdr->_11g0) == 0) */ - get_rx_stats(priv, hdr, &status); - skb_pull(skb, sizeof(*hdr)); - combine_hdr_frag((struct ieee80211_hdr *) - ((void *)&hdr->mac_hdr), skb); -/* dump_ieee80211_hdr((struct ieee80211_hdr *)skb->data, "RX G"); */ - } else - agnx_bug("Unknown packets type"); - memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx_irqsafe(priv->hw, skb); - rx_desc_reinit(priv, i); - - } while (priv->rx.idx++); -} /* handle_rx_irq */ - -static inline void handle_tx_irq(struct agnx_priv *priv, struct agnx_ring *ring) -{ - struct agnx_desc *desc; - struct agnx_info *info; - unsigned int idx; - - for (idx = ring->idx_sent; idx < ring->idx; idx++) { - unsigned int i = idx % ring->size; - u32 frag; - - desc = ring->desc + i; - info = ring->info + i; - - frag = be32_to_cpu(desc->frag); - if (frag & OWNER) { - if (info->type == HEADER) - break; - else - agnx_bug("TX error"); - } - - pci_unmap_single(priv->pdev, info->mapping, info->dma_len, PCI_DMA_TODEVICE); - - do { -/* int j; */ - size_t len; - len = info->skb->len - sizeof(struct agnx_hdr) + info->hdr_len; -/* if (len == 614) { */ -/* agnx_print_desc(desc); */ - if (info->type == PACKET) { -/* agnx_print_tx_hdr((struct agnx_hdr *)info->skb->data); */ -/* agnx_print_sta_power(priv, LOCAL_STAID); */ -/* agnx_print_sta(priv, LOCAL_STAID); */ -/* for (j = 0; j < 8; j++) */ -/* agnx_print_sta_tx_wq(priv, LOCAL_STAID, 0); */ -/* agnx_print_sta_power(priv, BSSID_STAID); */ -/* agnx_print_sta(priv, BSSID_STAID); */ -/* for (j = 0; j < 8; j++) */ -/* agnx_print_sta_tx_wq(priv, BSSID_STAID, 0); */ - } -/* } */ - } while (0); - - if (info->type == PACKET) { -/* dump_txm_registers(priv); - dump_rxm_registers(priv); - dump_bm_registers(priv); - dump_cir_registers(priv); */ - } - - if (info->type == PACKET) { -/* struct ieee80211_hdr *hdr; */ - struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(info->skb); - - skb_pull(info->skb, sizeof(struct agnx_hdr)); - memcpy(skb_push(info->skb, info->hdr_len), &info->hdr, info->hdr_len); - -/* dump_ieee80211_hdr((struct ieee80211_hdr *)info->skb->data, "TX_HANDLE"); */ -/* print_hex_dump_bytes("agnx: TX_HANDLE: ", DUMP_PREFIX_NONE, */ -/* info->skb->data, info->skb->len); */ - - if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK)) - txi->flags |= IEEE80211_TX_STAT_ACK; - - ieee80211_tx_status_irqsafe(priv->hw, info->skb); - - -/* info->tx_status.queue_number = (ring->size - i) / 2; */ -/* ieee80211_tx_status_irqsafe(priv->hw, info->skb, &(info->tx_status)); */ -/* } else */ -/* dev_kfree_skb_irq(info->skb); */ - } - memset(desc, 0, sizeof(*desc)); - memset(info, 0, sizeof(*info)); - } - - ring->idx_sent = idx; - /* TODO fill the priv->low_level_stats */ - - /* ieee80211_wake_queue(priv->hw, 0); */ -} - -void handle_txm_irq(struct agnx_priv *priv) -{ - handle_tx_irq(priv, &priv->txm); -} - -void handle_txd_irq(struct agnx_priv *priv) -{ - handle_tx_irq(priv, &priv->txd); -} - -void handle_other_irq(struct agnx_priv *priv) -{ -/* void __iomem *ctl = priv->ctl; */ - u32 status = priv->irq_status; - void __iomem *ctl = priv->ctl; - u32 reg; - - if (status & IRQ_TX_BEACON) { - iowrite32(IRQ_TX_BEACON, ctl + AGNX_INT_STAT); - printk(PFX "IRQ: TX Beacon control is 0X%.8X\n", ioread32(ctl + AGNX_TXM_BEACON_CTL)); - printk(PFX "IRQ: TX Beacon rx frame num: %d\n", rx_frame_cnt); - } - if (status & IRQ_TX_RETRY) { - reg = ioread32(ctl + AGNX_TXM_RETRYSTAID); - printk(PFX "IRQ: TX Retry, RETRY STA ID is %x\n", reg); - } - if (status & IRQ_TX_ACTIVITY) - printk(PFX "IRQ: TX Activity\n"); - if (status & IRQ_RX_ACTIVITY) - printk(PFX "IRQ: RX Activity\n"); - if (status & IRQ_RX_X) - printk(PFX "IRQ: RX X\n"); - if (status & IRQ_RX_Y) { - reg = ioread32(ctl + AGNX_INT_MASK); - reg &= ~IRQ_RX_Y; - iowrite32(reg, ctl + AGNX_INT_MASK); - iowrite32(IRQ_RX_Y, ctl + AGNX_INT_STAT); - printk(PFX "IRQ: RX Y\n"); - } - if (status & IRQ_RX_HASHHIT) { - reg = ioread32(ctl + AGNX_INT_MASK); - reg &= ~IRQ_RX_HASHHIT; - iowrite32(reg, ctl + AGNX_INT_MASK); - iowrite32(IRQ_RX_HASHHIT, ctl + AGNX_INT_STAT); - printk(PFX "IRQ: RX Hash Hit\n"); - - } - if (status & IRQ_RX_FRAME) { - reg = ioread32(ctl + AGNX_INT_MASK); - reg &= ~IRQ_RX_FRAME; - iowrite32(reg, ctl + AGNX_INT_MASK); - iowrite32(IRQ_RX_FRAME, ctl + AGNX_INT_STAT); - printk(PFX "IRQ: RX Frame\n"); - rx_frame_cnt++; - } - if (status & IRQ_ERR_INT) { - iowrite32(IRQ_ERR_INT, ctl + AGNX_INT_STAT); -/* agnx_hw_reset(priv); */ - printk(PFX "IRQ: Error Interrupt\n"); - } - if (status & IRQ_TX_QUE_FULL) - printk(PFX "IRQ: TX Workqueue Full\n"); - if (status & IRQ_BANDMAN_ERR) - printk(PFX "IRQ: Bandwidth Management Error\n"); - if (status & IRQ_TX_DISABLE) - printk(PFX "IRQ: TX Disable\n"); - if (status & IRQ_RX_IVASESKEY) - printk(PFX "IRQ: RX Invalid Session Key\n"); - if (status & IRQ_REP_THHIT) - printk(PFX "IRQ: Replay Threshold Hit\n"); - if (status & IRQ_TIMER1) - printk(PFX "IRQ: Timer1\n"); - if (status & IRQ_TIMER_CNT) - printk(PFX "IRQ: Timer Count\n"); - if (status & IRQ_PHY_FASTINT) - printk(PFX "IRQ: Phy Fast Interrupt\n"); - if (status & IRQ_PHY_SLOWINT) - printk(PFX "IRQ: Phy Slow Interrupt\n"); - if (status & IRQ_OTHER) - printk(PFX "IRQ: 0x80000000\n"); -} /* handle_other_irq */ - - -static inline void route_flag_set(struct agnx_hdr *txhdr) -{ -/* u32 reg = 0; */ - - /* FIXME */ -/* reg = (0x7 << ROUTE_COMPRESSION_SHIFT) & ROUTE_COMPRESSION; */ -/* txhdr->reg5 = cpu_to_be32(reg); */ - txhdr->reg5 = (0xa << 0x0) | (0x7 << 0x18); -/* txhdr->reg5 = cpu_to_be32((0xa << 0x0) | (0x7 << 0x18)); */ -/* txhdr->reg5 = cpu_to_be32(0x7 << 0x0); */ -} - -/* Return 0 if no match */ -static inline unsigned int get_power_level(unsigned int rate, unsigned int antennas_num) -{ - unsigned int power_level; - - switch (rate) { - case 10: - case 20: - case 55: - case 60: - case 90: - case 120: - power_level = 22; - break; - - case 180: - power_level = 19; - break; - - case 240: - power_level = 18; - break; - - case 360: - power_level = 16; - break; - - case 480: - power_level = 15; - break; - - case 540: - power_level = 14; - break; - default: - agnx_bug("Error rate setting\n"); - } - - if (power_level && (antennas_num == 2)) - power_level -= 3; - - return power_level; -} - -static inline void fill_agnx_hdr(struct agnx_priv *priv, struct agnx_info *tx_info) -{ - struct agnx_hdr *txhdr = (struct agnx_hdr *)tx_info->skb->data; - size_t len; - u16 fc = le16_to_cpu(*(__le16 *)&tx_info->hdr); - u32 reg; - - memset(txhdr, 0, sizeof(*txhdr)); - -/* reg = agnx_set_bits(STATION_ID, STATION_ID_SHIFT, LOCAL_STAID); */ - reg = agnx_set_bits(STATION_ID, STATION_ID_SHIFT, BSSID_STAID); - reg |= agnx_set_bits(WORKQUEUE_ID, WORKQUEUE_ID_SHIFT, 0); - txhdr->reg4 = cpu_to_be32(reg); - - /* Set the Hardware Sequence Number to 1? */ - reg = agnx_set_bits(SEQUENCE_NUMBER, SEQUENCE_NUMBER_SHIFT, 0); -/* reg = agnx_set_bits(SEQUENCE_NUMBER, SEQUENCE_NUMBER_SHIFT, 1); */ - reg |= agnx_set_bits(MAC_HDR_LEN, MAC_HDR_LEN_SHIFT, tx_info->hdr_len); - txhdr->reg1 = cpu_to_be32(reg); - /* Set the agnx_hdr's MAC header */ - memcpy(txhdr->mac_hdr, &tx_info->hdr, tx_info->hdr_len); - - reg = agnx_set_bits(ACK, ACK_SHIFT, 1); -/* reg = agnx_set_bits(ACK, ACK_SHIFT, 0); */ - reg |= agnx_set_bits(MULTICAST, MULTICAST_SHIFT, 0); -/* reg |= agnx_set_bits(MULTICAST, MULTICAST_SHIFT, 1); */ - reg |= agnx_set_bits(RELAY, RELAY_SHIFT, 0); - reg |= agnx_set_bits(TM, TM_SHIFT, 0); - txhdr->reg0 = cpu_to_be32(reg); - - /* Set the long and short retry limits */ - txhdr->tx.short_retry_limit = tx_info->txi->control.rates[0].count; - txhdr->tx.long_retry_limit = tx_info->txi->control.rates[0].count; - - /* FIXME */ - len = tx_info->skb->len - sizeof(*txhdr) + tx_info->hdr_len + FCS_LEN; - if (fc & IEEE80211_FCTL_PROTECTED) - len += 8; - len = 2398; - reg = agnx_set_bits(FRAG_SIZE, FRAG_SIZE_SHIFT, len); - len = tx_info->skb->len - sizeof(*txhdr); - reg |= agnx_set_bits(PAYLOAD_LEN, PAYLOAD_LEN_SHIFT, len); - txhdr->reg3 = cpu_to_be32(reg); - - route_flag_set(txhdr); -} /* fill_hdr */ - -static void txm_power_set(struct agnx_priv *priv, - struct ieee80211_tx_info *txi) -{ - struct agnx_sta_power power; - u32 reg; - - /* FIXME */ - if (txi->control.rates[0].idx < 0) { - /* For B mode Short Preamble */ - reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211B_SHORT); -/* control->tx_rate = -control->tx_rate; */ - } else - reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211G); -/* reg = agnx_set_bits(PHY_MODE, PHY_MODE_SHIFT, AGNX_MODE_80211B_LONG); */ - reg |= agnx_set_bits(SIGNAL, SIGNAL_SHIFT, 0xB); - reg |= agnx_set_bits(RATE, RATE_SHIFT, 0xB); -/* reg |= agnx_set_bits(POWER_LEVEL, POWER_LEVEL_SHIFT, 15); */ - reg |= agnx_set_bits(POWER_LEVEL, POWER_LEVEL_SHIFT, 20); - /* if rate < 11M set it to 0 */ - reg |= agnx_set_bits(NUM_TRANSMITTERS, NUM_TRANSMITTERS_SHIFT, 1); -/* reg |= agnx_set_bits(EDCF, EDCF_SHIFT, 1); */ -/* reg |= agnx_set_bits(TIFS, TIFS_SHIFT, 1); */ - - power.reg = reg; -/* power.reg = cpu_to_le32(reg); */ - -/* set_sta_power(priv, &power, LOCAL_STAID); */ - set_sta_power(priv, &power, BSSID_STAID); -} - -static inline int tx_packet_check(struct sk_buff *skb) -{ - unsigned int ieee_len = ieee80211_get_hdrlen_from_skb(skb); - if (skb->len > 2048) { - printk(KERN_ERR PFX "length is %d\n", skb->len); - agnx_bug("Too long TX skb"); - return -1; - } - /* FIXME */ - if (skb->len == ieee_len) { - printk(PFX "A strange TX packet\n"); - return -1; - /* tx_faile_irqsafe(); */ - } - return 0; -} - -static int __agnx_tx(struct agnx_priv *priv, struct sk_buff *skb, - struct agnx_ring *ring) -{ - struct agnx_desc *hdr_desc, *frag_desc; - struct agnx_info *hdr_info, *frag_info; - struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); - unsigned long flags; - unsigned int i; - - spin_lock_irqsave(&priv->lock, flags); - - /* The RX interrupt need be Disable until this TX packet - is handled in the next tx interrupt */ - disable_rx_interrupt(priv); - - i = ring->idx; - ring->idx += 2; -/* if (priv->txm_idx - priv->txm_idx_sent == AGNX_TXM_RING_SIZE - 2) */ -/* ieee80211_stop_queue(priv->hw, 0); */ - - /* Set agnx header's info and desc */ - i %= ring->size; - hdr_desc = ring->desc + i; - hdr_info = ring->info + i; - hdr_info->hdr_len = ieee80211_get_hdrlen_from_skb(skb); - memcpy(&hdr_info->hdr, skb->data, hdr_info->hdr_len); - - /* Add the agnx header to the front of the SKB */ - skb_push(skb, sizeof(struct agnx_hdr) - hdr_info->hdr_len); - - hdr_info->txi = txi; - hdr_info->dma_len = sizeof(struct agnx_hdr); - hdr_info->skb = skb; - hdr_info->type = HEADER; - fill_agnx_hdr(priv, hdr_info); - hdr_info->mapping = pci_map_single(priv->pdev, skb->data, - hdr_info->dma_len, PCI_DMA_TODEVICE); - do { - u32 frag = 0; - frag |= agnx_set_bits(FIRST_FRAG, FIRST_FRAG_SHIFT, 1); - frag |= agnx_set_bits(LAST_FRAG, LAST_FRAG_SHIFT, 0); - frag |= agnx_set_bits(PACKET_LEN, PACKET_LEN_SHIFT, skb->len); - frag |= agnx_set_bits(FIRST_FRAG_LEN, FIRST_FRAG_LEN_SHIFT, 1); - frag |= agnx_set_bits(OWNER, OWNER_SHIFT, 1); - hdr_desc->frag = cpu_to_be32(frag); - } while (0); - hdr_desc->dma_addr = cpu_to_be32(hdr_info->mapping); - - - /* Set Frag's info and desc */ - i = (i + 1) % ring->size; - frag_desc = ring->desc + i; - frag_info = ring->info + i; - memcpy(frag_info, hdr_info, sizeof(struct agnx_info)); - frag_info->type = PACKET; - frag_info->dma_len = skb->len - hdr_info->dma_len; - frag_info->mapping = pci_map_single(priv->pdev, skb->data + hdr_info->dma_len, - frag_info->dma_len, PCI_DMA_TODEVICE); - do { - u32 frag = 0; - frag |= agnx_set_bits(FIRST_FRAG, FIRST_FRAG_SHIFT, 0); - frag |= agnx_set_bits(LAST_FRAG, LAST_FRAG_SHIFT, 1); - frag |= agnx_set_bits(PACKET_LEN, PACKET_LEN_SHIFT, skb->len); - frag |= agnx_set_bits(SUB_FRAG_LEN, SUB_FRAG_LEN_SHIFT, frag_info->dma_len); - frag_desc->frag = cpu_to_be32(frag); - } while (0); - frag_desc->dma_addr = cpu_to_be32(frag_info->mapping); - - txm_power_set(priv, txi); - -/* do { */ -/* int j; */ -/* size_t len; */ -/* len = skb->len - hdr_info->dma_len + hdr_info->hdr_len; */ -/* if (len == 614) { */ -/* agnx_print_desc(hdr_desc); */ -/* agnx_print_desc(frag_desc); */ -/* agnx_print_tx_hdr((struct agnx_hdr *)skb->data); */ -/* agnx_print_sta_power(priv, LOCAL_STAID); */ -/* agnx_print_sta(priv, LOCAL_STAID); */ -/* for (j = 0; j < 8; j++) */ -/* agnx_print_sta_tx_wq(priv, LOCAL_STAID, j); */ -/* agnx_print_sta_power(priv, BSSID_STAID); */ -/* agnx_print_sta(priv, BSSID_STAID); */ -/* for (j = 0; j < 8; j++) */ -/* agnx_print_sta_tx_wq(priv, BSSID_STAID, j); */ -/* } */ -/* } while (0); */ - - spin_unlock_irqrestore(&priv->lock, flags); - - /* FIXME ugly code */ - /* Trigger TXM */ - do { - u32 reg; - reg = (ioread32(priv->ctl + AGNX_CIR_TXMCTL)); - reg |= 0x8; - iowrite32((reg), priv->ctl + AGNX_CIR_TXMCTL); - } while (0); - - /* Trigger TXD */ - do { - u32 reg; - reg = (ioread32(priv->ctl + AGNX_CIR_TXDCTL)); - reg |= 0x8; - iowrite32((reg), priv->ctl + AGNX_CIR_TXDCTL); - } while (0); - - return 0; -} - -int _agnx_tx(struct agnx_priv *priv, struct sk_buff *skb) -{ - u16 fctl; - - if (tx_packet_check(skb)) - return 0; - -/* print_hex_dump_bytes("agnx: TX_PACKET: ", DUMP_PREFIX_NONE, */ -/* skb->data, skb->len); */ - - fctl = le16_to_cpu(*((__le16 *)skb->data)); - - if ((fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) - return __agnx_tx(priv, skb, &priv->txd); - else - return __agnx_tx(priv, skb, &priv->txm); -} diff --git a/drivers/staging/agnx/xmit.h b/drivers/staging/agnx/xmit.h deleted file mode 100644 index 93ac4157e697..000000000000 --- a/drivers/staging/agnx/xmit.h +++ /dev/null @@ -1,250 +0,0 @@ -#ifndef AGNX_XMIT_H_ -#define AGNX_XMIT_H_ - -#include - -struct agnx_priv; - -static inline u32 agnx_set_bits(u32 mask, u8 shift, u32 value) -{ - return (value << shift) & mask; -} - -static inline u32 agnx_get_bits(u32 mask, u8 shift, u32 value) -{ - return (value & mask) >> shift; -} - - -struct agnx_rx { - __be16 rx_packet_duration; /* RX Packet Duration */ - __be16 replay_cnt; /* Replay Count */ -} __attribute__((__packed__)); - - -struct agnx_tx { - u8 long_retry_limit; /* Long Retry Limit */ - u8 short_retry_limit; /* Short Retry Limit */ - u8 long_retry_cnt; /* Long Retry Count */ - u8 short_retry_cnt; /* Short Retry Count */ -} __attribute__((__packed__)); - - -/* Copy from bcm43xx */ -#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] -#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) -#define PAD_BYTES(nr_bytes) P4D_BYTES(__LINE__, nr_bytes) - -#define P4D_BIT3S(magic, nr_bits) __be32 __padding##magic:nr_bits -#define P4D_BITS(line, nr_bits) P4D_BIT3S(line, nr_bits) -#define PAD_BITS(nr_bits) P4D_BITS(__LINE__, nr_bits) - - -struct agnx_hdr { - __be32 reg0; -#define RTS 0x80000000 /* RTS */ -#define RTS_SHIFT 31 -#define MULTICAST 0x40000000 /* multicast */ -#define MULTICAST_SHIFT 30 -#define ACK 0x30000000 /* ACK */ -#define ACK_SHIFT 28 -#define TM 0x08000000 /* TM */ -#define TM_SHIFT 27 -#define RELAY 0x04000000 /* Relay */ -#define RELAY_SHIFT 26 -/* PAD_BITS(4); */ -#define REVISED_FCS 0x00380000 /* revised FCS */ -#define REVISED_FCS_SHIFT 19 -#define NEXT_BUFFER_ADDR 0x0007FFFF /* Next Buffer Address */ -#define NEXT_BUFFER_ADDR_SHIFT 0 - - __be32 reg1; -#define MAC_HDR_LEN 0xFC000000 /* MAC Header Length */ -#define MAC_HDR_LEN_SHIFT 26 -#define DURATION_OVERIDE 0x02000000 /* Duration Override */ -#define DURATION_OVERIDE_SHIFT 25 -#define PHY_HDR_OVERIDE 0x01000000 /* PHY Header Override */ -#define PHY_HDR_OVERIDE_SHIFT 24 -#define CRC_FAIL 0x00800000 /* CRC fail */ -#define CRC_FAIL_SHIFT 23 -/* PAD_BITS(1); */ -#define SEQUENCE_NUMBER 0x00200000 /* Sequence Number */ -#define SEQUENCE_NUMBER_SHIFT 21 -/* PAD_BITS(2); */ -#define BUFF_HEAD_ADDR 0x0007FFFF /* Buffer Head Address */ -#define BUFF_HEAD_ADDR_SHIFT 0 - - __be32 reg2; -#define PDU_COUNT 0xFC000000 /* PDU Count */ -#define PDU_COUNT_SHIFT 26 -/* PAD_BITS(3); */ -#define WEP_KEY 0x00600000 /* WEP Key # */ -#define WEP_KEY_SHIFT 21 -#define USES_WEP_KEY 0x00100000 /* Uses WEP Key */ -#define USES_WEP_KEY_SHIFT 20 -#define KEEP_ALIVE 0x00080000 /* Keep alive */ -#define KEEP_ALIVE_SHIFT 19 -#define BUFF_TAIL_ADDR 0x0007FFFF /* Buffer Tail Address */ -#define BUFF_TAIL_ADDR_SHIFT 0 - - __be32 reg3; -#define CTS_11G 0x80000000 /* CTS in 11g */ -#define CTS_11G_SHIFT 31 -#define RTS_11G 0x40000000 /* RTS in 11g */ -#define RTS_11G_SHIFT 30 -/* PAD_BITS(2); */ -#define FRAG_SIZE 0x0FFF0000 /* fragment size */ -#define FRAG_SIZE_SHIFT 16 -#define PAYLOAD_LEN 0x0000FFF0 /* payload length */ -#define PAYLOAD_LEN_SHIFT 4 -#define FRAG_NUM 0x0000000F /* number of frags */ -#define FRAG_NUM_SHIFT 0 - - __be32 reg4; -/* PAD_BITS(4); */ -#define RELAY_STAID 0x0FFF0000 /* relayStald */ -#define RELAY_STAID_SHIFT 16 -#define STATION_ID 0x0000FFF0 /* Station ID */ -#define STATION_ID_SHIFT 4 -#define WORKQUEUE_ID 0x0000000F /* Workqueue ID */ -#define WORKQUEUE_ID_SHIFT 0 - - /* FIXME this register maybe is LE? */ - __be32 reg5; -/* PAD_BITS(4); */ -#define ROUTE_HOST 0x0F000000 -#define ROUTE_HOST_SHIFT 24 -#define ROUTE_CARD_CPU 0x00F00000 -#define ROUTE_CARD_CPU_SHIFT 20 -#define ROUTE_ENCRYPTION 0x000F0000 -#define ROUTE_ENCRYPTION_SHIFT 16 -#define ROUTE_TX 0x0000F000 -#define ROUTE_TX_SHIFT 12 -#define ROUTE_RX1 0x00000F00 -#define ROUTE_RX1_SHIFT 8 -#define ROUTE_RX2 0x000000F0 -#define ROUTE_RX2_SHIFT 4 -#define ROUTE_COMPRESSION 0x0000000F -#define ROUTE_COMPRESSION_SHIFT 0 - - __be32 _11g0; /* 11g */ - __be32 _11g1; /* 11g */ - __be32 _11b0; /* 11b */ - __be32 _11b1; /* 11b */ - u8 mac_hdr[32]; /* MAC header */ - - __be16 rts_duration; /* RTS duration */ - __be16 last_duration; /* Last duration */ - __be16 sec_last_duration; /* Second to Last duration */ - __be16 other_duration; /* Other duration */ - __be16 tx_last_duration; /* TX Last duration */ - __be16 tx_other_duration; /* TX Other Duration */ - __be16 last_11g_len; /* Length of last 11g */ - __be16 other_11g_len; /* Lenght of other 11g */ - - __be16 last_11b_len; /* Length of last 11b */ - __be16 other_11b_len; /* Lenght of other 11b */ - - - __be16 reg6; -#define MBF 0xF000 /* mbf */ -#define MBF_SHIFT 12 -#define RSVD4 0x0FFF /* rsvd4 */ -#define RSVD4_SHIFT 0 - - __be16 rx_frag_stat; /* RX fragmentation status */ - - __be32 time_stamp; /* TimeStamp */ - __be32 phy_stats_hi; /* PHY stats hi */ - __be32 phy_stats_lo; /* PHY stats lo */ - __be32 mic_key0; /* MIC key 0 */ - __be32 mic_key1; /* MIC key 1 */ - - union { /* RX/TX Union */ - struct agnx_rx rx; - struct agnx_tx tx; - }; - - u8 rx_channel; /* Recieve Channel */ - PAD_BYTES(3); - - u8 reserved[4]; -} __attribute__((__packed__)); - - -struct agnx_desc { -#define PACKET_LEN 0xFFF00000 -#define PACKET_LEN_SHIFT 20 -/* ------------------------------------------------ */ -#define FIRST_PACKET_MASK 0x00080000 -#define FIRST_PACKET_MASK_SHIFT 19 -#define FIRST_RESERV2 0x00040000 -#define FIRST_RESERV2_SHIFT 18 -#define FIRST_TKIP_ERROR 0x00020000 -#define FIRST_TKIP_ERROR_SHIFT 17 -#define FIRST_TKIP_PACKET 0x00010000 -#define FIRST_TKIP_PACKET_SHIFT 16 -#define FIRST_RESERV1 0x0000F000 -#define FIRST_RESERV1_SHIFT 12 -#define FIRST_FRAG_LEN 0x00000FF8 -#define FIRST_FRAG_LEN_SHIFT 3 -/* ------------------------------------------------ */ -#define SUB_RESERV2 0x000c0000 -#define SUB_RESERV2_SHIFT 18 -#define SUB_TKIP_ERROR 0x00020000 -#define SUB_TKIP_ERROR_SHIFT 17 -#define SUB_TKIP_PACKET 0x00010000 -#define SUB_TKIP_PACKET_SHIFT 16 -#define SUB_RESERV1 0x00008000 -#define SUB_RESERV1_SHIFT 15 -#define SUB_FRAG_LEN 0x00007FF8 -#define SUB_FRAG_LEN_SHIFT 3 -/* ------------------------------------------------ */ -#define FIRST_FRAG 0x00000004 -#define FIRST_FRAG_SHIFT 2 -#define LAST_FRAG 0x00000002 -#define LAST_FRAG_SHIFT 1 -#define OWNER 0x00000001 -#define OWNER_SHIFT 0 - __be32 frag; - __be32 dma_addr; -} __attribute__((__packed__)); - -enum {HEADER, PACKET}; - -struct agnx_info { - struct sk_buff *skb; - dma_addr_t mapping; - u32 dma_len; /* dma buffer len */ - /* Below fields only usful for tx */ - u32 hdr_len; /* ieee80211 header length */ - unsigned int type; - struct ieee80211_tx_info *txi; - struct ieee80211_hdr hdr; -}; - - -struct agnx_ring { - struct agnx_desc *desc; - dma_addr_t dma; - struct agnx_info *info; - /* Will lead to overflow when sent packet number enough? */ - unsigned int idx; - unsigned int idx_sent; /* only usful for txd and txm */ - unsigned int size; -}; - -#define AGNX_RX_RING_SIZE 128 -#define AGNX_TXD_RING_SIZE 256 -#define AGNX_TXM_RING_SIZE 128 - -void disable_rx_interrupt(struct agnx_priv *priv); -void enable_rx_interrupt(struct agnx_priv *priv); -int fill_rings(struct agnx_priv *priv); -void unfill_rings(struct agnx_priv *priv); -void handle_rx_irq(struct agnx_priv *priv); -void handle_txd_irq(struct agnx_priv *priv); -void handle_txm_irq(struct agnx_priv *priv); -void handle_other_irq(struct agnx_priv *priv); -int _agnx_tx(struct agnx_priv *priv, struct sk_buff *skb); -#endif /* AGNX_XMIT_H_ */ -- cgit v1.2.3 From 999b9da40ea4c9efb31689c08d1d1b7a621ecce0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 8 Oct 2009 11:32:31 -0700 Subject: Staging: remove cowloop driver The author has found a number of problems with the current version of this driver in the current kernel, and is reworking it to get things working again. Because of that, it would be better to remove the driver now and add it back in a future kernel release. Cc: H.J. Thomassen Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/cowloop/Kconfig | 16 - drivers/staging/cowloop/Makefile | 1 - drivers/staging/cowloop/TODO | 11 - drivers/staging/cowloop/cowloop.c | 2842 ------------------------------------- drivers/staging/cowloop/cowloop.h | 66 - 7 files changed, 2939 deletions(-) delete mode 100644 drivers/staging/cowloop/Kconfig delete mode 100644 drivers/staging/cowloop/Makefile delete mode 100644 drivers/staging/cowloop/TODO delete mode 100644 drivers/staging/cowloop/cowloop.c delete mode 100644 drivers/staging/cowloop/cowloop.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index ab64971d3fe7..7df3ba4f1f4d 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -127,7 +127,5 @@ source "drivers/staging/sep/Kconfig" source "drivers/staging/iio/Kconfig" -source "drivers/staging/cowloop/Kconfig" - endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index baffd0e96474..747571172269 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -45,4 +45,3 @@ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_RAR_REGISTER) += rar/ obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ -obj-$(CONFIG_COWLOOP) += cowloop/ diff --git a/drivers/staging/cowloop/Kconfig b/drivers/staging/cowloop/Kconfig deleted file mode 100644 index 58d2a23bd2c1..000000000000 --- a/drivers/staging/cowloop/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -config COWLOOP - tristate "copy-on-write pseudo Block Driver" - depends on BLOCK - default n - ---help--- - Cowloop is a "copy-on-write" pseudo block driver. It can be - stacked on top of a "real" block driver, and catches all write - operations on their way from the file systems layer above to - the real driver below, effectively shielding the lower driver - from those write accesses. The requests are then diverted to - an ordinary file, located somewhere else (configurable). Later - read requests are checked to see whether they can be serviced - by the "real" block driver below, or must be pulled in from - the diverted location. More information and userspace tools to - use the driver are on the project's website - http://www.ATComputing.nl/cowloop/ diff --git a/drivers/staging/cowloop/Makefile b/drivers/staging/cowloop/Makefile deleted file mode 100644 index 2b6b81a63d21..000000000000 --- a/drivers/staging/cowloop/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_COWLOOP) += cowloop.o diff --git a/drivers/staging/cowloop/TODO b/drivers/staging/cowloop/TODO deleted file mode 100644 index 9399d1c16e15..000000000000 --- a/drivers/staging/cowloop/TODO +++ /dev/null @@ -1,11 +0,0 @@ -TODO: - - checkpatch.pl cleanups - - run sparse to ensure clean - - fix up 32/64bit ioctl issues - - move proc file usage to debugfs - - audit ioctls - - add documentation - - get linux-fsdevel to review it - -Please send patches to "H.J. Thomassen" and -Greg Kroah-Hartman diff --git a/drivers/staging/cowloop/cowloop.c b/drivers/staging/cowloop/cowloop.c deleted file mode 100644 index a71c743a1196..000000000000 --- a/drivers/staging/cowloop/cowloop.c +++ /dev/null @@ -1,2842 +0,0 @@ -/* -** COWLOOP block device driver (2.6 kernel compliant) -** ======================================================================= -** Read-write loop-driver with copy-on-write functionality. -** -** Synopsis: -** -** modprobe cowloop [maxcows=..] [rdofile=..... cowfile=.... [option=r]] -** -** Definition of number of configured cowdevices: -** maxcows= number of configured cowdevices (default: 16) -** (do not confuse this with MAXCOWS: absolute maximum as compiled) -** -** One pair of filenames can be supplied during insmod/modprobe to open -** the first cowdevice: -** rdofile= read-only file (or filesystem) -** cowfile= storage-space for modified blocks of read-only file(system) -** option=r repair cowfile automatically if it appears to be dirty -** -** Other cowdevices can be activated via the command "cowdev" -** whenever the cowloop-driver is loaded. -** -** The read-only file may be of type 'regular' or 'block-device'. -** -** The cowfile must be of type 'regular'. -** If an existing regular file is used as cowfile, its contents will be -** used again for the current read-only file. When the cowfile has not been -** closed properly during a previous session (i.e. rmmod cowloop), the -** cowloop-driver refuses to open it unless the parameter "option=r" is -** specified. -** -** Layout of cowfile: -** -** +-----------------------------+ -** | cow head block | MAPUNIT bytes -** |-----------------------------| -** | | MAPUNIT bytes -** |--- ---| -** | | MAPUNIT bytes -** |--- ---| -** | used-block bitmap | MAPUNIT bytes -** |-----------------------------| -** | gap to align start-offset | -** | to 4K multiple | -** |-----------------------------| <---- start-offset cow blocks -** | | -** | written cow blocks | MAPUNIT bytes -** | ..... | -** -** cowhead block: -** - contains general info about the rdofile which is related -** to this cowfile -** -** used-block bitmap: -** - contains one bit per block with a size of MAPUNIT bytes -** - bit-value '1' = block has been written on cow -** '0' = block unused on cow -** - total bitmap rounded to multiples of MAPUNIT -** -** ============================================================================ -** Author: Gerlof Langeveld - AT Computing (March 2003) -** Current maintainer: Hendrik-Jan Thomassen - AT Computing (Summer 2006) -** Email: hjt@ATComputing.nl -** ---------------------------------------------------------------------------- -** Copyright (C) 2003-2009 AT Consultancy -** -** This program is free software; you can redistribute it and/or modify it -** under the terms of the GNU General Public License as published by the -** Free Software Foundation; either version 2, or (at your option) any -** later version. -** -** 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. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -** ---------------------------------------------------------------------------- -** -** Major modifications: -** -** 200405 Ported to kernel-version 2.6 Hendrik-Jan Thomassen -** 200405 Added cowhead to cowfile to garantee -** consistency with read-only file Gerlof Langeveld -** 200405 Postponed flushing of bitmaps to improve -** performance. Gerlof Langeveld -** 200405 Inline recovery for dirty cowfiles. Gerlof Langeveld -** 200502 Redesign to support more cowdevices. Gerlof Langeveld -** 200502 Support devices/file > 2 Gbytes. Gerlof Langeveld -** 200507 Check for free space to expand cowfile. Gerlof Langeveld -** 200902 Upgrade for kernel 2.6.28 Hendrik-Jan Thomassen -** -** Inspired by -** loop.c by Theodore Ts'o and -** cloop.c by Paul `Rusty' Russell & Klaus Knopper. -** -** Design-considerations: -** -** For the first experiments with the cowloop-driver, the request-queue -** made use of the do_generic_file_read() which worked fine except -** in combination with the cloop-driver; that combination -** resulted in a non-interruptible hangup of the system during -** heavy load. Other experiments using the `make_request' interface also -** resulted in unpredictable system hangups (with proper use of spinlocks). -** -** To overcome these problems, the cowloop-driver starts a kernel-thread -** for every active cowdevice. -** All read- and write-request on the read-only file and copy-on-write file -** are handled in the context of that thread. -** A scheme has been designed to wakeup the kernel-thread as -** soon as I/O-requests are available in the request-queue; this thread -** handles the requests one-by-one by calling the proper read- or -** write-function related to the open read-only file or copy-on-write file. -** When all pending requests have been handled, the kernel-thread goes -** back to sleep-state. -** This approach requires some additional context-switches; however the -** performance loss during heavy I/O is less than 3%. -** -** -------------------------------------------------------------------------*/ -/* The following is the cowloop package version number. It must be - identical to the content of the include-file "version.h" that is - used in all supporting utilities: */ -char revision[] = "$Revision: 3.1 $"; /* cowlo_init_module() has - assumptions about this string's format */ - -/* Note that the following numbers are *not* the cowloop package version - numbers, but separate revision history numbers to track the - modifications of this particular source file: */ -/* $Log: cowloop.c,v $ -** -** Revision 1.30 2009/02/08 hjt -** Integrated earlier fixes -** Upgraded to kernel 2.6.28 (thanks Jerome Poulin) -** -** Revision 1.29 2006/12/03 22:12:00 hjt -** changed 'cowdevlock' from spinlock to semaphore, to avoid -** "scheduling while atomic". Contributed by Juergen Christ. -** Added version.h again -** -** Revision 1.28 2006/08/16 16:00:00 hjt -** malloc each individual cowloopdevice struct separately -** -** Revision 1.27 2006/03/14 14:57:03 root -** Removed include version.h -** -** Revision 1.26 2005/08/08 11:22:48 root -** Implement possibility to close a cow file or reopen a cowfile read-only. -** -** Revision 1.25 2005/08/03 14:00:39 root -** Added modinfo info to driver. -** -** Revision 1.24 2005/07/21 06:14:53 root -** Cosmetic changes source code. -** -** Revision 1.23 2005/07/20 13:07:32 root -** Supply ioctl to write watchdog program to react on lack of cowfile space. -** -** Revision 1.22 2005/07/20 07:53:34 root -** Regular verification of free space in filesystem holding the cowfile -** (give warnings whenever space is almost exhausted). -** Terminology change: checksum renamed to fingerprint. -** -** Revision 1.21 2005/07/19 09:21:52 root -** Removing maximum limit of 16 Gb per cowdevice. -** -** Revision 1.20 2005/07/19 07:50:33 root -** Minor bugfixes and cosmetic changes. -** -** Revision 1.19 2005/06/10 12:29:55 root -** Removed lock/unlock operation from cowlo_open(). -** -** Revision 1.18 2005/05/09 12:56:26 root -** Allow a cowdevice to be open more than once -** (needed for support of ReiserFS and XFS). -** -** Revision 1.17 2005/03/17 14:36:16 root -** Fixed some license issues. -** -** Revision 1.16 2005/03/07 14:42:05 root -** Only allow one parallel open per cowdevice. -** -** Revision 1.15 2005/02/18 11:52:04 gerlof -** Redesign to support more than one cowdevice > 2 Gb space. -** -** Revision 1.14 2004/08/17 14:19:16 gerlof -** Modified output of /proc/cowloop. -** -** Revision 1.13 2004/08/16 07:21:10 gerlof -** Separate statistical counter for read on rdofile and cowfile. -** -** Revision 1.12 2004/08/11 06:52:11 gerlof -** Modified messages. -** -** Revision 1.11 2004/08/11 06:44:11 gerlof -** Modified log messages. -** -** Revision 1.10 2004/08/10 12:27:27 gerlof -** Cosmetic changes. -** -** Revision 1.9 2004/08/09 11:43:37 gerlof -** Removed double definition of major number (COWMAJOR). -** -** Revision 1.8 2004/08/09 08:03:39 gerlof -** Cleanup of messages. -** -** Revision 1.7 2004/05/27 06:37:33 gerlof -** Modified /proc message. -** -** Revision 1.6 2004/05/26 21:23:28 gerlof -** Modified /proc output. -** -** Revision 1.5 2004/05/26 13:23:34 gerlof -** Support cowsync to force flushing the bitmaps and cowhead. -** -** Revision 1.4 2004/05/26 11:11:10 gerlof -** Updated the comment to the actual situation. -** -** Revision 1.3 2004/05/26 10:50:00 gerlof -** Implemented recovery-option. -** -** Revision 1.2 2004/05/25 15:14:41 gerlof -** Modified bitmap flushing strategy. -** -*/ - -#define COWMAJOR 241 - -// #define COWDEBUG - -#ifdef COWDEBUG -#define DEBUGP printk -#define DCOW KERN_ALERT -#else -#define DEBUGP(format, x...) -#endif - -#include -#include -#ifndef AUTOCONF_INCLUDED -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cowloop.h" - -MODULE_LICENSE("GPL"); -/* MODULE_AUTHOR("Gerlof Langeveld "); obsolete address */ -MODULE_AUTHOR("Hendrik-Jan Thomassen "); /* current maintainer */ -MODULE_DESCRIPTION("Copy-on-write loop driver"); -MODULE_PARM_DESC(maxcows, " Number of configured cowdevices (default 16)"); -MODULE_PARM_DESC(rdofile, " Read-only file for /dev/cow/0"); -MODULE_PARM_DESC(cowfile, " Cowfile for /dev/cow/0"); -MODULE_PARM_DESC(option, " Repair cowfile if inconsistent: option=r"); - -#define DEVICE_NAME "cow" - -#define DFLCOWS 16 /* default cowloop devices */ - -static int maxcows = DFLCOWS; -module_param(maxcows, int, 0); -static char *rdofile = ""; -module_param(rdofile, charp, 0); -static char *cowfile = ""; -module_param(cowfile, charp, 0); -static char *option = ""; -module_param(option, charp, 0); - -/* -** per cowdevice several bitmap chunks are allowed of MAPCHUNKSZ each -** -** each bitmap chunk can describe MAPCHUNKSZ * 8 * MAPUNIT bytes of data -** suppose: -** MAPCHUNKSZ 4096 and MAPUNIT 1024 --> 4096 * 8 * 1024 = 32 Mb per chunk -*/ -#define MAPCHUNKSZ 4096 /* #bytes per bitmap chunk (do not change) */ - -#define SPCMINBLK 100 /* space threshold to give warning messages */ -#define SPCDFLINTVL 16 /* once every SPCDFLINTVL writes to cowfile, */ - /* available space in filesystem is checked */ - -#define CALCMAP(x) ((x)/(MAPCHUNKSZ*8)) -#define CALCBYTE(x) (((x)%(MAPCHUNKSZ*8))>>3) -#define CALCBIT(x) ((x)&7) - -#define ALLCOW 1 -#define ALLRDO 2 -#define MIXEDUP 3 - -static char allzeroes[MAPUNIT]; - -/* -** administration per cowdevice (pair of cowfile/rdofile) -*/ - -/* bit-values for state */ -#define COWDEVOPEN 0x01 /* cowdevice opened */ -#define COWRWCOWOPEN 0x02 /* cowfile opened read-write */ -#define COWRDCOWOPEN 0x04 /* cowfile opened read-only */ -#define COWWATCHDOG 0x08 /* ioctl for watchdog cowfile space active */ - -#define COWCOWOPEN (COWRWCOWOPEN|COWRDCOWOPEN) - -struct cowloop_device -{ - /* - ** current status - */ - int state; /* bit-values (see above) */ - int opencnt; /* # opens for cowdevice */ - - /* - ** open file pointers - */ - struct file *rdofp, *cowfp; /* open file pointers */ - char *rdoname, *cowname; /* file names */ - - /* - ** request queue administration - */ - struct request_queue *rqueue; - spinlock_t rqlock; - struct gendisk *gd; - - /* - ** administration about read-only file - */ - unsigned int numblocks; /* # blocks input file in MAPUNIT */ - unsigned int blocksz; /* minimum unit to access this dev */ - unsigned long fingerprint; /* fingerprint of current rdofile */ - struct block_device *belowdev; /* block device below us */ - struct gendisk *belowgd; /* gendisk for blk dev below us */ - struct request_queue *belowq; /* req. queue of blk dev below us */ - - /* - ** bitmap administration to register which blocks are modified - */ - long int mapsize; /* total size of bitmap (bytes) */ - long int mapremain; /* remaining bytes in last bitmap */ - int mapcount; /* number of bitmaps in use */ - char **mapcache; /* area with pointers to bitmaps */ - - char *iobuf; /* databuffer of MAPUNIT bytes */ - struct cowhead *cowhead; /* buffer containing cowhead */ - - /* - ** administration for interface with the kernel-thread - */ - int pid; /* pid==0: no thread available */ - struct request *req; /* request to be handled now */ - wait_queue_head_t waitq; /* wait-Q: thread waits for work */ - char closedown; /* boolean: thread exit required */ - char qfilled; /* boolean: I/O request pending */ - char iobusy; /* boolean: req under treatment */ - - /* - ** administration to keep track of free space in cowfile filesystem - */ - unsigned long blksize; /* block size of fs (bytes) */ - unsigned long blktotal; /* recent total space in fs (blocks) */ - unsigned long blkavail; /* recent free space in fs (blocks) */ - - wait_queue_head_t watchq; /* wait-Q: watcher awaits threshold */ - unsigned long watchthresh; /* threshold of watcher (blocks) */ - - /* - ** statistical counters - */ - unsigned long rdoreads; /* number of read-actions rdo */ - unsigned long cowreads; /* number of read-actions cow */ - unsigned long cowwrites; /* number of write-actions */ - unsigned long nrcowblocks; /* number of blocks in use on cow */ -}; - -static struct cowloop_device **cowdevall; /* ptr to ptrs to all cowdevices */ -static struct semaphore cowdevlock; /* generic lock for cowdevs */ - -static struct gendisk *cowctlgd; /* gendisk control channel */ -static spinlock_t cowctlrqlock; /* for req.q. of ctrl. channel */ - -/* -** private directory /proc/cow -*/ -struct proc_dir_entry *cowlo_procdir; - -/* -** function prototypes -*/ -static long int cowlo_do_request (struct request *req); -static void cowlo_sync (void); -static int cowlo_checkio (struct cowloop_device *, int, loff_t); -static int cowlo_readmix (struct cowloop_device *, void *, int, loff_t); -static int cowlo_writemix (struct cowloop_device *, void *, int, loff_t); -static long int cowlo_readrdo (struct cowloop_device *, void *, int, loff_t); -static long int cowlo_readcow (struct cowloop_device *, void *, int, loff_t); -static long int cowlo_readcowraw (struct cowloop_device *, void *, int, loff_t); -static long int cowlo_writecow (struct cowloop_device *, void *, int, loff_t); -static long int cowlo_writecowraw(struct cowloop_device *, void *, int, loff_t); -static int cowlo_ioctl (struct block_device *, fmode_t, - unsigned int, unsigned long); -static int cowlo_makepair (struct cowpair __user *); -static int cowlo_removepair (unsigned long __user *); -static int cowlo_watch (struct cowpair __user *); -static int cowlo_cowctl (unsigned long __user *, int); -static int cowlo_openpair (char *, char *, int, int); -static int cowlo_closepair (struct cowloop_device *); -static int cowlo_openrdo (struct cowloop_device *, char *); -static int cowlo_opencow (struct cowloop_device *, char *, int); -static void cowlo_undo_openrdo(struct cowloop_device *); -static void cowlo_undo_opencow(struct cowloop_device *); - -/*****************************************************************************/ -/* System call handling */ -/*****************************************************************************/ - -/* -** handle system call open()/mount() -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int cowlo_open(struct block_device *bdev, fmode_t mode) -{ - struct inode *inode = bdev->bd_inode; - - if (!inode) - return -EINVAL; - - if (imajor(inode) != COWMAJOR) { - printk(KERN_WARNING - "cowloop - unexpected major %d\n", imajor(inode)); - return -ENODEV; - } - - switch (iminor(inode)) { - case COWCTL: - DEBUGP(DCOW"cowloop - open %d control\n", COWCTL); - break; - - default: - DEBUGP(DCOW"cowloop - open minor %d\n", iminor(inode)); - - if ( iminor(inode) >= maxcows ) - return -ENODEV; - - if ( !((cowdevall[iminor(inode)])->state & COWDEVOPEN) ) - return -ENODEV; - - (cowdevall[iminor(inode)])->opencnt++; - } - - return 0; -} - -/* -** handle system call close()/umount() -** -** returns: -** 0 - okay -*/ -static int cowlo_release(struct gendisk *gd, fmode_t mode) -{ - struct block_device *bdev; - struct inode *inode; - - bdev = bdget_disk(gd, 0); - inode = bdev->bd_inode; - if (!inode) - return 0; - - DEBUGP(DCOW"cowloop - release (close) minor %d\n", iminor(inode)); - - if ( iminor(inode) != COWCTL) - (cowdevall[iminor(inode)])->opencnt--; - - return 0; -} - -/* -** handle system call ioctl() -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int cowlo_ioctl(struct block_device *bdev, fmode_t mode, - unsigned int cmd, unsigned long arg) -{ - struct hd_geometry geo; - struct inode *inode = bdev->bd_inode; - - DEBUGP(DCOW "cowloop - ioctl cmd %x\n", cmd); - - switch ( iminor(inode) ) { - - /* - ** allowed via control device only - */ - case COWCTL: - switch (cmd) { - /* - ** write all bitmap chunks and cowheaders to cowfiles - */ - case COWSYNC: - down(&cowdevlock); - cowlo_sync(); - up(&cowdevlock); - return 0; - - /* - ** open a new cowdevice (pair of rdofile/cowfile) - */ - case COWMKPAIR: - return cowlo_makepair((void __user *)arg); - - /* - ** close a cowdevice (pair of rdofile/cowfile) - */ - case COWRMPAIR: - return cowlo_removepair((void __user *)arg); - - /* - ** watch free space of filesystem containing cowfile - */ - case COWWATCH: - return cowlo_watch((void __user *)arg); - - /* - ** close cowfile for active device - */ - case COWCLOSE: - return cowlo_cowctl((void __user *)arg, COWCLOSE); - - /* - ** reopen cowfile read-only for active device - */ - case COWRDOPEN: - return cowlo_cowctl((void __user *)arg, COWRDOPEN); - - default: - return -EINVAL; - } /* end of switch on command */ - - /* - ** allowed for any other cowdevice - */ - default: - switch (cmd) { - /* - ** HDIO_GETGEO must be supported for fdisk, etc - */ - case HDIO_GETGEO: - geo.cylinders = 0; - geo.heads = 0; - geo.sectors = 0; - - if (copy_to_user((void __user *)arg, &geo, sizeof geo)) - return -EFAULT; - return 0; - - default: - return -EINVAL; - } /* end of switch on ioctl-cmd code parameter */ - } /* end of switch on minor number */ -} - -static struct block_device_operations cowlo_fops = -{ - .owner = THIS_MODULE, - .open = cowlo_open, /* called upon open */ - .release = cowlo_release, /* called upon close */ - .ioctl = cowlo_ioctl, /* called upon ioctl */ -}; - -/* -** handle ioctl-command COWMKPAIR: -** open a new cowdevice (pair of rdofile/cowfile) on-the-fly -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_makepair(struct cowpair __user *arg) -{ - int i, rv=0; - struct cowpair cowpair; - unsigned char *cowpath; - unsigned char *rdopath; - - /* - ** retrieve info about pathnames - */ - if ( copy_from_user(&cowpair, arg, sizeof cowpair) ) - return -EFAULT; - - if ( (MAJOR(cowpair.device) != COWMAJOR) && (cowpair.device != ANYDEV) ) - return -EINVAL; - - if ( (MINOR(cowpair.device) >= maxcows) && (cowpair.device != ANYDEV) ) - return -EINVAL; - - /* - ** retrieve pathname strings - */ - if ( (cowpair.cowflen > PATH_MAX) || (cowpair.rdoflen > PATH_MAX) ) - return -ENAMETOOLONG; - - if ( !(cowpath = kmalloc(cowpair.cowflen+1, GFP_KERNEL)) ) - return -ENOMEM; - - if ( copy_from_user(cowpath, (void __user *)cowpair.cowfile, - cowpair.cowflen) ) { - kfree(cowpath); - return -EFAULT; - } - *(cowpath+cowpair.cowflen) = 0; - - if ( !(rdopath = kmalloc(cowpair.rdoflen+1, GFP_KERNEL)) ) { - kfree(cowpath); - return -ENOMEM; - } - - if ( copy_from_user(rdopath, (void __user *)cowpair.rdofile, - cowpair.rdoflen) ) { - kfree(rdopath); - kfree(cowpath); - return -EFAULT; - } - *(rdopath+cowpair.rdoflen) = 0; - - /* - ** open new cowdevice - */ - if ( cowpair.device == ANYDEV) { - /* - ** search first unused minor - */ - for (i=0, rv=-EBUSY; i < maxcows; i++) { - if ( !((cowdevall[i])->state & COWDEVOPEN) ) { - rv = cowlo_openpair(rdopath, cowpath, 0, i); - break; - } - } - - if (rv) { /* open failed? */ - kfree(rdopath); - kfree(cowpath); - return rv; - } - - /* - ** return newly allocated cowdevice to user space - */ - cowpair.device = MKDEV(COWMAJOR, i); - - if ( copy_to_user(arg, &cowpair, sizeof cowpair)) { - kfree(rdopath); - kfree(cowpath); - return -EFAULT; - } - } else { /* specific minor requested */ - if ( (rv = cowlo_openpair(rdopath, cowpath, 0, - MINOR(cowpair.device)))) { - kfree(rdopath); - kfree(cowpath); - return rv; - } - } - - return 0; -} - -/* -** handle ioctl-command COWRMPAIR: -** deactivate an existing cowdevice (pair of rdofile/cowfile) on-the-fly -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_removepair(unsigned long __user *arg) -{ - unsigned long cowdevice; - struct cowloop_device *cowdev; - - /* - ** retrieve info about device to be removed - */ - if ( copy_from_user(&cowdevice, arg, sizeof cowdevice)) - return -EFAULT; - - /* - ** verify major-minor number - */ - if ( MAJOR(cowdevice) != COWMAJOR) - return -EINVAL; - - if ( MINOR(cowdevice) >= maxcows) - return -EINVAL; - - cowdev = cowdevall[MINOR(cowdevice)]; - - if ( !(cowdev->state & COWDEVOPEN) ) - return -ENODEV; - - /* - ** synchronize bitmaps and close cowdevice - */ - if (cowdev->state & COWRWCOWOPEN) { - down(&cowdevlock); - cowlo_sync(); - up(&cowdevlock); - } - - return cowlo_closepair(cowdev); -} - -/* -** handle ioctl-command COWWATCH: -** watch the free space of the filesystem containing a cowfile -** of an open cowdevice -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_watch(struct cowpair __user *arg) -{ - struct cowloop_device *cowdev; - struct cowwatch cowwatch; - - /* - ** retrieve structure holding info - */ - if ( copy_from_user(&cowwatch, arg, sizeof cowwatch)) - return -EFAULT; - - /* - ** verify if cowdevice exists and is currently open - */ - if ( MINOR(cowwatch.device) >= maxcows) - return -EINVAL; - - cowdev = cowdevall[MINOR(cowwatch.device)]; - - if ( !(cowdev->state & COWDEVOPEN) ) - return -ENODEV; - - /* - ** if the WATCHWAIT-option is set, wait until the indicated - ** threshold is reached (only one waiter allowed) - */ - if (cowwatch.flags & WATCHWAIT) { - /* - ** check if already another waiter active - ** for this cowdevice - */ - if (cowdev->state & COWWATCHDOG) - return -EAGAIN; - - cowdev->state |= COWWATCHDOG; - - cowdev->watchthresh = (unsigned long long) - cowwatch.threshold / - (cowdev->blksize / 1024); - - if (wait_event_interruptible(cowdev->watchq, - cowdev->watchthresh >= cowdev->blkavail)) { - cowdev->state &= ~COWWATCHDOG; - return EINTR; - } - - cowdev->state &= ~COWWATCHDOG; - } - - cowwatch.totalkb = (unsigned long long)cowdev->blktotal * - cowdev->blksize / 1024; - cowwatch.availkb = (unsigned long long)cowdev->blkavail * - cowdev->blksize / 1024; - - if ( copy_to_user(arg, &cowwatch, sizeof cowwatch)) - return -EFAULT; - - return 0; -} - -/* -** handle ioctl-commands COWCLOSE and COWRDOPEN: -** COWCLOSE - close the cowfile while the cowdevice remains open; -** this allows an unmount of the filesystem on which -** the cowfile resides -** COWRDOPEN - close the cowfile and reopen it for read-only; -** this allows a remount read-ony of the filesystem -** on which the cowfile resides -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_cowctl(unsigned long __user *arg, int cmd) -{ - struct cowloop_device *cowdev; - unsigned long cowdevice; - - /* - ** retrieve info about device to be removed - */ - if ( copy_from_user(&cowdevice, arg, sizeof cowdevice)) - return -EFAULT; - - /* - ** verify major-minor number - */ - if ( MAJOR(cowdevice) != COWMAJOR) - return -EINVAL; - - if ( MINOR(cowdevice) >= maxcows) - return -EINVAL; - - cowdev = cowdevall[MINOR(cowdevice)]; - - if ( !(cowdev->state & COWDEVOPEN) ) - return -ENODEV; - - /* - ** synchronize bitmaps and close cowfile - */ - if (cowdev->state & COWRWCOWOPEN) { - down(&cowdevlock); - cowlo_sync(); - up(&cowdevlock); - } - - /* - ** handle specific ioctl-command - */ - switch (cmd) { - case COWRDOPEN: - /* - ** if the cowfile is still opened read-write - */ - if (cowdev->state & COWRWCOWOPEN) { - /* - ** close the cowfile - */ - if (cowdev->cowfp) - filp_close(cowdev->cowfp, 0); - - cowdev->state &= ~COWRWCOWOPEN; - - /* - ** open again for read-only - */ - cowdev->cowfp = filp_open(cowdev->cowname, - O_RDONLY|O_LARGEFILE, 0600); - - if ( (cowdev->cowfp == NULL) || IS_ERR(cowdev->cowfp) ) { - printk(KERN_ERR - "cowloop - failed to reopen cowfile %s\n", - cowdev->cowname); - return -EINVAL; - } - - /* - ** mark cowfile open for read-only - */ - cowdev->state |= COWRDCOWOPEN; - } else { - return -EINVAL; - } - break; - - case COWCLOSE: - /* - ** if the cowfile is still open - */ - if (cowdev->state & COWCOWOPEN) { - /* - ** close the cowfile - */ - if (cowdev->cowfp) - filp_close(cowdev->cowfp, 0); - - cowdev->state &= ~COWCOWOPEN; - } - } - - return 0; -} - - -/*****************************************************************************/ -/* Handling of I/O-requests for a cowdevice */ -/*****************************************************************************/ - -/* -** function to be called by core-kernel to handle the I/O-requests -** in the queue -*/ -static void cowlo_request(struct request_queue *q) -{ - struct request *req; - struct cowloop_device *cowdev; - - DEBUGP(DCOW "cowloop - request function called....\n"); - - while((req = blk_peek_request(q)) != NULL) { - DEBUGP(DCOW "cowloop - got next request\n"); - - if (! blk_fs_request(req)) { - /* this is not a normal file system request */ - __blk_end_request_cur(req, -EIO); - continue; - } - cowdev = req->rq_disk->private_data; - - if (cowdev->iobusy) - return; - else - cowdev->iobusy = 1; - - /* - ** when no kernel-thread is available, the request will - ** produce an I/O-error - */ - if (!cowdev->pid) { - printk(KERN_ERR"cowloop - no thread available\n"); - __blk_end_request_cur(req, -EIO); /* request failed */ - cowdev->iobusy = 0; - continue; - } - - /* - ** handle I/O-request in the context of the kernel-thread - */ - cowdev->req = req; - cowdev->qfilled = 1; - - wake_up_interruptible_sync(&cowdev->waitq); - - /* - ** get out of this function now while the I/O-request is - ** under treatment of the kernel-thread; this function - ** will be called again after the current I/O-request has - ** been finished by the thread - */ - return; - } -} - -/* -** daemon-process (kernel-thread) executes this function -*/ -static int -cowlo_daemon(struct cowloop_device *cowdev) -{ - int rv; - int minor; - char myname[16]; - - for (minor = 0; minor < maxcows; minor++) { - if (cowdev == cowdevall[minor]) break; - } - sprintf(myname, "cowloopd%d", minor); - - daemonize(myname); - - while (!cowdev->closedown) { - /* - ** sleep while waiting for an I/O request; - ** note that no non-interruptible wait has been used - ** because the non-interruptible version of - ** a *synchronous* wake_up does not exist (any more) - */ - if (wait_event_interruptible(cowdev->waitq, cowdev->qfilled)){ - flush_signals(current); /* ignore signal-based wakeup */ - continue; - } - - if (cowdev->closedown) /* module will be unloaded ? */{ - cowdev->pid = 0; - return 0; - } - - /* - ** woken up by the I/O-request handler: treat requested I/O - */ - cowdev->qfilled = 0; - - rv = cowlo_do_request(cowdev->req); - - /* - ** reacquire the queue-spinlock for manipulating - ** the request-queue and dequeue the request - */ - spin_lock_irq(&cowdev->rqlock); - - __blk_end_request_cur(cowdev->req, rv); - cowdev->iobusy = 0; - - /* - ** initiate the next request from the queue - */ - cowlo_request(cowdev->rqueue); - - spin_unlock_irq(&cowdev->rqlock); - } - return 0; -} - -/* -** function to be called in the context of the kernel thread -** to handle the queued I/O-requests -** -** returns: -** 0 - fail -** 1 - success -*/ -static long int -cowlo_do_request(struct request *req) -{ - unsigned long len; - long int rv; - loff_t offset; - struct cowloop_device *cowdev = req->rq_disk->private_data; - - /* - ** calculate some variables which are needed later on - */ - len = blk_rq_cur_sectors(req) << 9; - offset = (loff_t) blk_rq_pos(req) << 9; - - DEBUGP(DCOW"cowloop - req cmd=%d offset=%lld len=%lu addr=%p\n", - *(req->cmd), offset, len, req->buffer); - - /* - ** handle READ- or WRITE-request - */ - switch (rq_data_dir(req)) { - /**********************************************************/ - case READ: - switch ( cowlo_checkio(cowdev, len, offset) ) { - case ALLCOW: - rv = cowlo_readcow(cowdev, req->buffer, len, offset); - break; - - case ALLRDO: - rv = cowlo_readrdo(cowdev, req->buffer, len, offset); - break; - - case MIXEDUP: - rv = cowlo_readmix(cowdev, req->buffer, len, offset); - break; - - default: - rv = 0; /* never happens */ - } - break; - - /**********************************************************/ - case WRITE: - switch ( cowlo_checkio(cowdev, len, offset) ) { - case ALLCOW: - /* - ** straight-forward write will do... - */ - DEBUGP(DCOW"cowloop - write straight "); - - rv = cowlo_writecow(cowdev, req->buffer, len, offset); - break; /* from switch */ - - case ALLRDO: - if ( (len & MUMASK) == 0) { - DEBUGP(DCOW"cowloop - write straight "); - - rv = cowlo_writecow(cowdev, req->buffer, - len, offset); - break; - } - - case MIXEDUP: - rv = cowlo_writemix(cowdev, req->buffer, len, offset); - break; - - default: - rv = 0; /* never happens */ - } - break; - - default: - printk(KERN_ERR - "cowloop - unrecognized command %d\n", *(req->cmd)); - rv = 0; - } - - return (rv <= 0 ? 0 : 1); -} - -/* -** check for a given I/O-request if all underlying blocks -** (with size MAPUNIT) are either in the read-only file or in -** the cowfile (or a combination of the two) -** -** returns: -** ALLRDO - all underlying blocks in rdofile -** ALLCOW - all underlying blocks in cowfile -** MIXEDUP - underlying blocks partly in rdofile and partly in cowfile -*/ -static int -cowlo_checkio(struct cowloop_device *cowdev, int len, loff_t offset) -{ - unsigned long mapnum, bytenum, bitnum, blocknr, partlen; - long int totcnt, cowcnt; - char *mc; - - /* - ** notice that the requested block might cross - ** a blocksize boundary while one of the concerned - ** blocks resides in the read-only file and another - ** one in the copy-on-write file; in that case the - ** request will be broken up into pieces - */ - if ( (len <= MAPUNIT) && - (MAPUNIT - (offset & MUMASK) <= len) ) { - /* - ** easy situation: - ** requested data-block entirely fits within - ** the mapunit used for the bitmap - ** check if that block is located in rdofile or - ** cowfile - */ - blocknr = offset >> MUSHIFT; - - mapnum = CALCMAP (blocknr); - bytenum = CALCBYTE(blocknr); - bitnum = CALCBIT (blocknr); - - if (*(*(cowdev->mapcache+mapnum)+bytenum)&(1< 0; len-=partlen, offset+=partlen, totcnt++){ - /* - ** calculate blocknr of involved block - */ - blocknr = offset >> MUSHIFT; - - /* - ** calculate partial length for this transfer - */ - partlen = MAPUNIT - (offset & MUMASK); - if (partlen > len) - partlen = len; - - /* - ** is this block located in the cowfile - */ - mapnum = CALCMAP (blocknr); - bytenum = CALCBYTE(blocknr); - bitnum = CALCBIT (blocknr); - - mc = *(cowdev->mapcache+mapnum); - - if (*(mc+bytenum)&(1< 0; len-=partlen, buf+=partlen, offset+=partlen) { - /* - ** calculate blocknr of entire block - */ - blocknr = offset >> MUSHIFT; - - /* - ** calculate partial length for this transfer - */ - partlen = MAPUNIT - (offset & MUMASK); - if (partlen > len) - partlen = len; - - /* - ** is this block located in the cowfile - */ - mapnum = CALCMAP (blocknr); - bytenum = CALCBYTE(blocknr); - bitnum = CALCBIT (blocknr); - mc = *(cowdev->mapcache+mapnum); - - if (*(mc+bytenum)&(1< 0; len-=partlen, buf+=partlen, offset+=partlen) { - /* - ** calculate partial length for this transfer - */ - partlen = MAPUNIT - (offset & MUMASK); - if (partlen > len) - partlen = len; - - /* - ** calculate blocknr of entire block - */ - blocknr = offset >> MUSHIFT; - - /* - ** has this block been written before? - */ - mapnum = CALCMAP (blocknr); - bytenum = CALCBYTE(blocknr); - bitnum = CALCBIT (blocknr); - mc = *(cowdev->mapcache+mapnum); - - if (*(mc+bytenum)&(1<iobuf, - MAPUNIT, (loff_t)blocknr << MUSHIFT) <= 0) - rv = 0; - } - - /* - ** transfer modified part into - ** the block just read - */ - memcpy(cowdev->iobuf + (offset & MUMASK), buf, partlen); - - /* - ** write entire block to cowfile - */ - DEBUGP(DCOW"cowloop - split " - "partlen=%ld off=%lld\n", - partlen, (loff_t)blocknr << MUSHIFT); - - if (cowlo_writecow(cowdev, cowdev->iobuf, MAPUNIT, - (loff_t)blocknr << MUSHIFT) <= 0) - rv = 0; - } - } - - return rv; -} - -/*****************************************************************************/ -/* I/O-support for read-only file and copy-on-write file */ -/*****************************************************************************/ - -/* -** read data from the read-only file -** -** return-value: similar to user-mode read -*/ -static long int -cowlo_readrdo(struct cowloop_device *cowdev, void *buf, int len, loff_t offset) -{ - long int rv; - mm_segment_t old_fs; - loff_t saveoffset = offset; - - DEBUGP(DCOW"cowloop - readrdo called\n"); - - old_fs = get_fs(); - set_fs( get_ds() ); - rv = cowdev->rdofp->f_op->read(cowdev->rdofp, buf, len, &offset); - set_fs(old_fs); - - if (rv < len) { - printk(KERN_WARNING "cowloop - read-failure %ld on rdofile" - "- offset=%lld len=%d\n", - rv, saveoffset, len); - } - - cowdev->rdoreads++; - return rv; -} - -/* -** read cowfile from a modified offset, i.e. skipping the bitmap and cowhead -** -** return-value: similar to user-mode read -*/ -static long int -cowlo_readcow(struct cowloop_device *cowdev, void *buf, int len, loff_t offset) -{ - DEBUGP(DCOW"cowloop - readcow called\n"); - - offset += cowdev->cowhead->doffset; - - return cowlo_readcowraw(cowdev, buf, len, offset); -} - -/* -** read cowfile from an absolute offset -** -** return-value: similar to user-mode read -*/ -static long int -cowlo_readcowraw(struct cowloop_device *cowdev, - void *buf, int len, loff_t offset) -{ - long int rv; - mm_segment_t old_fs; - loff_t saveoffset = offset; - - DEBUGP(DCOW"cowloop - readcowraw called\n"); - - /* - ** be sure that cowfile is opened for read-write - */ - if ( !(cowdev->state & COWCOWOPEN) ) { - printk(KERN_WARNING - "cowloop - read request from cowfile refused\n"); - - return -EBADF; - } - - /* - ** issue low level read - */ - old_fs = get_fs(); - set_fs( get_ds() ); - rv = cowdev->cowfp->f_op->read(cowdev->cowfp, buf, len, &offset); - set_fs(old_fs); - - if (rv < len) { - printk(KERN_WARNING - "cowloop - read-failure %ld on cowfile" - "- offset=%lld len=%d\n", rv, saveoffset, len); - } - - cowdev->cowreads++; - return rv; -} - -/* -** write cowfile from a modified offset, i.e. skipping the bitmap and cowhead -** -** if a block is written for the first time while its contents consists -** of binary zeroes only, the concerning bitmap is flushed to the cowfile -** -** return-value: similar to user-mode write -*/ -static long int -cowlo_writecow(struct cowloop_device *cowdev, void *buf, int len, loff_t offset) -{ - long int rv; - unsigned long mapnum=0, mapbyte=0, mapbit=0, cowblock=0, partlen; - char *tmpptr, *mapptr = NULL; - loff_t tmpoffset, mapoffset = 0; - - DEBUGP(DCOW"cowloop - writecow called\n"); - - /* - ** be sure that cowfile is opened for read-write - */ - if ( !(cowdev->state & COWRWCOWOPEN) ) { - printk(KERN_WARNING - "cowloop - Write request to cowfile refused\n"); - - return -EBADF; - } - - /* - ** write the entire block to the cowfile - */ - tmpoffset = offset + cowdev->cowhead->doffset; - - rv = cowlo_writecowraw(cowdev, buf, len, tmpoffset); - - /* - ** verify if enough space available on filesystem holding - ** the cowfile - ** - when the last write failed (might be caused by lack of space) - ** - when a watcher is active (to react adequatly) - ** - when the previous check indicated fs was almost full - ** - with regular intervals - */ - if ( (rv <= 0) || - (cowdev->state & COWWATCHDOG) || - (cowdev->blkavail / 2 < SPCDFLINTVL) || - (cowdev->cowwrites % SPCDFLINTVL == 0) ) { - struct kstatfs ks; - - if (vfs_statfs(cowdev->cowfp->f_dentry, &ks)==0){ - if (ks.f_bavail <= SPCMINBLK) { - switch (ks.f_bavail) { - case 0: - case 1: - case 2: - case 3: - printk(KERN_ALERT - "cowloop - " - "ALERT: cowfile full!\n"); - break; - - default: - printk(KERN_WARNING - "cowloop - cowfile almost " - "full (only %llu Kb free)\n", - (unsigned long long) - ks.f_bsize * ks.f_bavail /1024); - } - } - - cowdev->blktotal = ks.f_blocks; - cowdev->blkavail = ks.f_bavail; - - /* - ** wakeup watcher if threshold has been reached - */ - if ( (cowdev->state & COWWATCHDOG) && - (cowdev->watchthresh >= cowdev->blkavail) ) { - wake_up_interruptible(&cowdev->watchq); - } - } - } - - if (rv <= 0) - return rv; - - DEBUGP(DCOW"cowloop - block written\n"); - - /* - ** check if block(s) is/are written to the cowfile - ** for the first time; if so, adapt the bitmap - */ - for (; len > 0; len-=partlen, offset+=partlen, buf+=partlen) { - /* - ** calculate partial length for this transfer - */ - partlen = MAPUNIT - (offset & MUMASK); - if (partlen > len) - partlen = len; - - /* - ** calculate bitnr of written chunk of cowblock - */ - cowblock = offset >> MUSHIFT; - - mapnum = CALCMAP (cowblock); - mapbyte = CALCBYTE(cowblock); - mapbit = CALCBIT (cowblock); - - if (*(*(cowdev->mapcache+mapnum)+mapbyte) & (1<mapcache+mapnum)+mapbyte) |= (1<nrcowblocks++; - - DEBUGP(DCOW"cowloop - bitupdate blk=%ld map=%ld " - "byte=%ld bit=%ld\n", - cowblock, mapnum, mapbyte, mapbit); - - /* - ** check if the cowhead in the cowfile is currently - ** marked clean; if so, mark it dirty and flush it - */ - if ( !(cowdev->cowhead->flags &= COWDIRTY)) { - cowdev->cowhead->flags |= COWDIRTY; - - cowlo_writecowraw(cowdev, cowdev->cowhead, - MAPUNIT, (loff_t)0); - } - - /* - ** if the written datablock contained binary zeroes, - ** the bitmap block should be marked to be flushed to disk - ** (blocks containing all zeroes cannot be recovered by - ** the cowrepair-program later on if cowloop is not properly - ** removed via rmmod) - */ - if ( memcmp(buf, allzeroes, partlen) ) /* not all zeroes? */ - continue; /* no flush needed */ - - /* - ** calculate positions of bitmap block to be flushed - ** - pointer of bitmap block in memory - ** - offset of bitmap block in cowfile - */ - tmpptr = *(cowdev->mapcache+mapnum) + (mapbyte & (~MUMASK)); - tmpoffset = (loff_t) MAPUNIT + mapnum * MAPCHUNKSZ + - (mapbyte & (~MUMASK)); - - /* - ** flush a bitmap block at the moment that all bits have - ** been set in that block, i.e. at the moment that we - ** switch to another bitmap block - */ - if ( (mapoffset != 0) && (mapoffset != tmpoffset) ) { - if (cowlo_writecowraw(cowdev, mapptr, MAPUNIT, - mapoffset) < 0) { - printk(KERN_WARNING - "cowloop - write-failure on bitmap - " - "blk=%ld map=%ld byte=%ld bit=%ld\n", - cowblock, mapnum, mapbyte, mapbit); - } - - DEBUGP(DCOW"cowloop - bitmap blk written %lld\n", - mapoffset); - } - - /* - ** remember offset in cowfile and offset in memory - ** for bitmap to be flushed; flushing will be done - ** as soon as all updates in this bitmap block have - ** been done - */ - mapoffset = tmpoffset; - mapptr = tmpptr; - } - - /* - ** any new block written containing binary zeroes? - */ - if (mapoffset) { - if (cowlo_writecowraw(cowdev, mapptr, MAPUNIT, mapoffset) < 0) { - printk(KERN_WARNING - "cowloop - write-failure on bitmap - " - "blk=%ld map=%ld byte=%ld bit=%ld\n", - cowblock, mapnum, mapbyte, mapbit); - } - - DEBUGP(DCOW"cowloop - bitmap block written %lld\n", mapoffset); - } - - return rv; -} - -/* -** write cowfile from an absolute offset -** -** return-value: similar to user-mode write -*/ -static long int -cowlo_writecowraw(struct cowloop_device *cowdev, - void *buf, int len, loff_t offset) -{ - long int rv; - mm_segment_t old_fs; - loff_t saveoffset = offset; - - DEBUGP(DCOW"cowloop - writecowraw called\n"); - - /* - ** be sure that cowfile is opened for read-write - */ - if ( !(cowdev->state & COWRWCOWOPEN) ) { - printk(KERN_WARNING - "cowloop - write request to cowfile refused\n"); - - return -EBADF; - } - - /* - ** issue low level write - */ - old_fs = get_fs(); - set_fs( get_ds() ); - rv = cowdev->cowfp->f_op->write(cowdev->cowfp, buf, len, &offset); - set_fs(old_fs); - - if (rv < len) { - printk(KERN_WARNING - "cowloop - write-failure %ld on cowfile" - "- offset=%lld len=%d\n", rv, saveoffset, len); - } - - cowdev->cowwrites++; - return rv; -} - - -/* -** readproc-function: called when the corresponding /proc-file is read -*/ -static int -cowlo_readproc(char *buf, char **start, off_t pos, int cnt, int *eof, void *p) -{ - struct cowloop_device *cowdev = p; - - revision[sizeof revision - 3] = '\0'; - - return sprintf(buf, - " cowloop version: %9s\n\n" - " device state: %s%s%s%s\n" - " number of opens: %9d\n" - " pid of thread: %9d\n\n" - " read-only file: %9s\n" - " rdoreads: %9lu\n\n" - "copy-on-write file: %9s\n" - " state cowfile: %9s\n" - " bitmap-blocks: %9lu (of %d bytes)\n" - " cowblocks in use: %9lu (of %d bytes)\n" - " cowreads: %9lu\n" - " cowwrites: %9lu\n", - &revision[11], - - cowdev->state & COWDEVOPEN ? "devopen " : "", - cowdev->state & COWRWCOWOPEN ? "cowopenrw " : "", - cowdev->state & COWRDCOWOPEN ? "cowopenro " : "", - cowdev->state & COWWATCHDOG ? "watchdog " : "", - - cowdev->opencnt, - cowdev->pid, - cowdev->rdoname, - cowdev->rdoreads, - cowdev->cowname, - cowdev->cowhead->flags & COWDIRTY ? "dirty":"clean", - cowdev->mapsize >> MUSHIFT, MAPUNIT, - cowdev->nrcowblocks, MAPUNIT, - cowdev->cowreads, - cowdev->cowwrites); -} - -/*****************************************************************************/ -/* Setup and destroy cowdevices */ -/*****************************************************************************/ - -/* -** open and prepare a cowdevice (rdofile and cowfile) and allocate bitmaps -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_openpair(char *rdof, char *cowf, int autorecover, int minor) -{ - long int rv; - struct cowloop_device *cowdev = cowdevall[minor]; - struct kstatfs ks; - - down(&cowdevlock); - - /* - ** requested device exists? - */ - if (minor >= maxcows) { - up(&cowdevlock); - return -ENODEV; - } - - /* - ** requested device already assigned to cowdevice? - */ - if (cowdev->state & COWDEVOPEN) { - up(&cowdevlock); - return -EBUSY; - } - - /* - ** initialize administration - */ - memset(cowdev, 0, sizeof *cowdev); - - spin_lock_init (&cowdev->rqlock); - init_waitqueue_head(&cowdev->waitq); - init_waitqueue_head(&cowdev->watchq); - - /* - ** open the read-only file - */ - DEBUGP(DCOW"cowloop - call openrdo....\n"); - - if ( (rv = cowlo_openrdo(cowdev, rdof)) ) { - cowlo_undo_openrdo(cowdev); - up(&cowdevlock); - return rv; - } - - /* - ** open the cowfile - */ - DEBUGP(DCOW"cowloop - call opencow....\n"); - - if ( (rv = cowlo_opencow(cowdev, cowf, autorecover)) ) { - cowlo_undo_openrdo(cowdev); - cowlo_undo_opencow(cowdev); - up(&cowdevlock); - return rv; - } - - /* - ** administer total and available size of filesystem holding cowfile - */ - if (vfs_statfs(cowdev->cowfp->f_dentry, &ks)==0) { - cowdev->blksize = ks.f_bsize; - cowdev->blktotal = ks.f_blocks; - cowdev->blkavail = ks.f_bavail; - } else { - cowdev->blksize = 1024; /* avoid division by zero */ - } - - /* - ** flush the (recovered) bitmaps and cowhead to the cowfile - */ - DEBUGP(DCOW"cowloop - call cowsync....\n"); - - cowlo_sync(); - - /* - ** allocate gendisk for the cow device - */ - DEBUGP(DCOW"cowloop - alloc disk....\n"); - - if ((cowdev->gd = alloc_disk(1)) == NULL) { - printk(KERN_WARNING - "cowloop - unable to alloc_disk for cowloop\n"); - - cowlo_undo_openrdo(cowdev); - cowlo_undo_opencow(cowdev); - up(&cowdevlock); - return -ENOMEM; - } - - cowdev->gd->major = COWMAJOR; - cowdev->gd->first_minor = minor; - cowdev->gd->minors = 1; - cowdev->gd->fops = &cowlo_fops; - cowdev->gd->private_data = cowdev; - sprintf(cowdev->gd->disk_name, "%s%d", DEVICE_NAME, minor); - - /* in .5 Kb units */ - set_capacity(cowdev->gd, (cowdev->numblocks*(MAPUNIT/512))); - - DEBUGP(DCOW"cowloop - init request queue....\n"); - - if ((cowdev->rqueue = blk_init_queue(cowlo_request, &cowdev->rqlock)) - == NULL) { - printk(KERN_WARNING - "cowloop - unable to get request queue for cowloop\n"); - - del_gendisk(cowdev->gd); - cowlo_undo_openrdo(cowdev); - cowlo_undo_opencow(cowdev); - up(&cowdevlock); - return -EINVAL; - } - - blk_queue_logical_block_size(cowdev->rqueue, cowdev->blocksz); - cowdev->gd->queue = cowdev->rqueue; - - /* - ** start kernel thread to handle requests - */ - DEBUGP(DCOW"cowloop - kickoff daemon....\n"); - - cowdev->pid = kernel_thread((int (*)(void *))cowlo_daemon, cowdev, 0); - - /* - ** create a file below directory /proc/cow for this new cowdevice - */ - if (cowlo_procdir) { - char tmpname[64]; - - sprintf(tmpname, "%d", minor); - - create_proc_read_entry(tmpname, 0 , cowlo_procdir, - cowlo_readproc, cowdev); - } - - cowdev->state |= COWDEVOPEN; - - cowdev->rdoname = rdof; - cowdev->cowname = cowf; - - /* - ** enable the new disk; this triggers the first request! - */ - DEBUGP(DCOW"cowloop - call add_disk....\n"); - - add_disk(cowdev->gd); - - up(&cowdevlock); - return 0; -} - -/* -** close a cowdevice (pair of rdofile/cowfile) and release memory -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_closepair(struct cowloop_device *cowdev) -{ - int minor; - - down(&cowdevlock); - - /* - ** if cowdevice is not activated at all, refuse - */ - if ( !(cowdev->state & COWDEVOPEN) ) { - up(&cowdevlock); - return -ENODEV; - } - - /* - ** if this cowdevice is still open, refuse - */ - if (cowdev->opencnt > 0) { - up(&cowdevlock); - return -EBUSY; - } - - up(&cowdevlock); - - /* - ** wakeup watcher (if any) - */ - if (cowdev->state & COWWATCHDOG) { - cowdev->watchthresh = cowdev->blkavail; - wake_up_interruptible(&cowdev->watchq); - } - - /* - ** wakeup kernel-thread to be able to exit - ** and wait until it has exited - */ - cowdev->closedown = 1; - cowdev->qfilled = 1; - wake_up_interruptible(&cowdev->waitq); - - while (cowdev->pid) - schedule(); - - del_gendisk(cowdev->gd); /* revert the alloc_disk() */ - put_disk(cowdev->gd); /* revert the add_disk() */ - - if (cowlo_procdir) { - char tmpname[64]; - - for (minor = 0; minor < maxcows; minor++) { - if (cowdev == cowdevall[minor]) break; - } - sprintf(tmpname, "%d", minor); - - remove_proc_entry(tmpname, cowlo_procdir); - } - - blk_cleanup_queue(cowdev->rqueue); - - /* - ** release memory for filenames if these names have - ** been allocated dynamically - */ - if ( (cowdev->cowname) && (cowdev->cowname != cowfile)) - kfree(cowdev->cowname); - - if ( (cowdev->rdoname) && (cowdev->rdoname != rdofile)) - kfree(cowdev->rdoname); - - cowlo_undo_openrdo(cowdev); - cowlo_undo_opencow(cowdev); - - cowdev->state &= ~COWDEVOPEN; - - return 0; -} - -/* -** open the read-only file -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_openrdo(struct cowloop_device *cowdev, char *rdof) -{ - struct file *f; - struct inode *inode; - long int i, nrval; - - DEBUGP(DCOW"cowloop - openrdo called\n"); - - /* - ** open the read-only file - */ - if(*rdof == '\0') { - printk(KERN_ERR - "cowloop - specify name for read-only file\n\n"); - return -EINVAL; - } - - f = filp_open(rdof, O_RDONLY|O_LARGEFILE, 0); - - if ( (f == NULL) || IS_ERR(f) ) { - printk(KERN_ERR - "cowloop - open of rdofile %s failed\n", rdof); - return -EINVAL; - } - - cowdev->rdofp = f; - - inode = f->f_dentry->d_inode; - - if ( !S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode) ) { - printk(KERN_ERR - "cowloop - %s not regular file or blockdev\n", rdof); - return -EINVAL; - } - - DEBUGP(DCOW"cowloop - determine size rdo....\n"); - - /* - ** determine block-size and total size of read-only file - */ - if (S_ISREG(inode->i_mode)) { - /* - ** read-only file is a regular file - */ - cowdev->blocksz = 512; /* other value fails */ - cowdev->numblocks = inode->i_size >> MUSHIFT; - - if (inode->i_size & MUMASK) { - printk(KERN_WARNING - "cowloop - rdofile %s truncated to multiple " - "of %d bytes\n", rdof, MAPUNIT); - } - - DEBUGP(DCOW"cowloop - RO=regular: numblocks=%d, blocksz=%d\n", - cowdev->numblocks, cowdev->blocksz); - } else { - /* - ** read-only file is a block device - */ - cowdev->belowdev = inode->i_bdev; - cowdev->belowgd = cowdev->belowdev->bd_disk; /* gendisk */ - - if (cowdev->belowdev->bd_part) { - cowdev->numblocks = cowdev->belowdev->bd_part->nr_sects - / (MAPUNIT/512); - } - - if (cowdev->belowgd) { - cowdev->belowq = cowdev->belowgd->queue; - - if (cowdev->numblocks == 0) { - cowdev->numblocks = get_capacity(cowdev->belowgd) - / (MAPUNIT/512); - } - } - - - if (cowdev->belowq) - cowdev->blocksz = queue_logical_block_size(cowdev->belowq); - - if (cowdev->blocksz == 0) - cowdev->blocksz = BLOCK_SIZE; /* default 2^10 */ - - DEBUGP(DCOW"cowloop - numblocks=%d, " - "blocksz=%d, belowgd=%p, belowq=%p\n", - cowdev->numblocks, cowdev->blocksz, - cowdev->belowgd, cowdev->belowq); - - DEBUGP(DCOW"cowloop - belowdev.bd_block_size=%d\n", - cowdev->belowdev->bd_block_size); - } - - if (cowdev->numblocks == 0) { - printk(KERN_ERR "cowloop - %s has no contents\n", rdof); - return -EINVAL; - } - - /* - ** reserve space in memory as generic I/O buffer - */ - cowdev->iobuf = kmalloc(MAPUNIT, GFP_KERNEL); - - if (!cowdev->iobuf) { - printk(KERN_ERR - "cowloop - cannot get space for buffer %d\n", MAPUNIT); - return -ENOMEM; - } - - DEBUGP(DCOW"cowloop - determine fingerprint rdo....\n"); - - /* - ** determine fingerprint for read-only file - ** calculate fingerprint from first four datablocks - ** which do not contain binary zeroes - */ - for (i=0, cowdev->fingerprint=0, nrval=0; - (nrval < 4)&&(i < cowdev->numblocks); i++) { - int j; - unsigned char cs; - - /* - ** read next block - */ - if (cowlo_readrdo(cowdev, cowdev->iobuf, MAPUNIT, - (loff_t)i << MUSHIFT) < 1) - break; - - /* - ** calculate fingerprint by adding all byte-values - */ - for (j=0, cs=0; j < MAPUNIT; j++) - cs += *(cowdev->iobuf+j); - - if (cs == 0) /* block probably contained zeroes */ - continue; - - /* - ** shift byte-value to proper place in final fingerprint - */ - cowdev->fingerprint |= cs << (nrval*8); - nrval++; - } - - return 0; -} - -/* -** undo memory allocs and file opens issued so far -** related to the read-only file -*/ -static void -cowlo_undo_openrdo(struct cowloop_device *cowdev) -{ - if(cowdev->iobuf); - kfree(cowdev->iobuf); - - if (cowdev->rdofp) - filp_close(cowdev->rdofp, 0); -} - -/* -** open the cowfile -** -** returns: -** 0 - okay -** < 0 - error value -*/ -static int -cowlo_opencow(struct cowloop_device *cowdev, char *cowf, int autorecover) -{ - long int i, rv; - int minor; - unsigned long nb; - struct file *f; - struct inode *inode; - loff_t offset; - struct cowloop_device *cowtmp; - - DEBUGP(DCOW"cowloop - opencow called\n"); - - /* - ** open copy-on-write file (read-write) - */ - if (cowf[0] == '\0') { - printk(KERN_ERR - "cowloop - specify name of copy-on-write file\n\n"); - return -EINVAL; - } - - f = filp_open(cowf, O_RDWR|O_LARGEFILE, 0600); - - if ( (f == NULL) || IS_ERR(f) ) { - /* - ** non-existing cowfile: try to create - */ - f = filp_open(cowf, O_RDWR|O_CREAT|O_LARGEFILE, 0600); - - if ( (f == NULL) || IS_ERR(f) ) { - printk(KERN_ERR - "cowloop - failed to open file %s for read-write\n\n", - cowf); - return -EINVAL; - } - } - - cowdev->cowfp = f; - - inode = f->f_dentry->d_inode; - - if (!S_ISREG(inode->i_mode)) { - printk(KERN_ERR "cowloop - %s is not regular file\n", cowf); - return -EINVAL; - } - - /* - ** check if this cowfile is already in use for another cowdevice - */ - for (minor = 0; minor < maxcows; minor++) { - - cowtmp = cowdevall[minor]; - - if ( !(cowtmp->state & COWDEVOPEN) ) - continue; - - if (cowtmp == cowdev) - continue; - - if (cowtmp->cowfp->f_dentry->d_inode == f->f_dentry->d_inode) { - printk(KERN_ERR - "cowloop - %s: already in use as cow\n", cowf); - return -EBUSY; - } - } - - /* - ** mark cowfile open for read-write - */ - cowdev->state |= COWRWCOWOPEN; - - /* - ** calculate size (in bytes) for total bitmap in cowfile; - ** when the size of the cowhead block is added, the start-offset - ** for the modified data blocks can be found - */ - nb = cowdev->numblocks; - - if (nb%8) /* transform #bits to #bytes */ - nb+=8; /* rounded if necessary */ - nb /= 8; - - if (nb & MUMASK) /* round up #bytes to MAPUNIT chunks */ - cowdev->mapsize = ( (nb>>MUSHIFT) +1) << MUSHIFT; - else - cowdev->mapsize = nb; - - /* - ** reserve space in memory for the cowhead - */ - cowdev->cowhead = kmalloc(MAPUNIT, GFP_KERNEL); - - if (!cowdev->cowhead) { - printk(KERN_ERR "cowloop - cannot get space for cowhead %d\n", - MAPUNIT); - return -ENOMEM; - } - - memset(cowdev->cowhead, 0, MAPUNIT); - - DEBUGP(DCOW"cowloop - prepare cowhead....\n"); - - /* - ** check if the cowfile exists or should be created - */ - if (inode->i_size != 0) { - /* - ** existing cowfile: read the cow head - */ - if (inode->i_size < MAPUNIT) { - printk(KERN_ERR - "cowloop - existing cowfile %s too small\n", - cowf); - return -EINVAL; - } - - cowlo_readcowraw(cowdev, cowdev->cowhead, MAPUNIT, (loff_t) 0); - - /* - ** verify if the existing file is really a cowfile - */ - if (cowdev->cowhead->magic != COWMAGIC) { - printk(KERN_ERR - "cowloop - cowfile %s has incorrect format\n", - cowf); - return -EINVAL; - } - - /* - ** verify the cowhead version of the cowfile - */ - if (cowdev->cowhead->version > COWVERSION) { - printk(KERN_ERR - "cowloop - cowfile %s newer than this driver\n", - cowf); - return -EINVAL; - } - - /* - ** make sure that this is not a packed cowfile - */ - if (cowdev->cowhead->flags & COWPACKED) { - printk(KERN_ERR - "cowloop - packed cowfile %s not accepted\n", cowf); - return -EINVAL; - } - - /* - ** verify if the cowfile has been properly closed - */ - if (cowdev->cowhead->flags & COWDIRTY) { - /* - ** cowfile was not properly closed; - ** check if automatic recovery is required - ** (actual recovery will be done later on) - */ - if (!autorecover) { - printk(KERN_ERR - "cowloop - cowfile %s is dirty " - "(not properly closed by rmmod?)\n", - cowf); - printk(KERN_ERR - "cowloop - run cowrepair or specify " - "'option=r' to recover\n"); - return -EINVAL; - } - } - - /* - ** verify if the cowfile is really related to this rdofile - */ - if (cowdev->cowhead->rdoblocks != cowdev->numblocks) { - printk(KERN_ERR - "cowloop - cowfile %s (size %lld) not related " - "to rdofile (size %lld)\n", - cowf, - (long long)cowdev->cowhead->rdoblocks <numblocks <cowhead->rdofingerprint != cowdev->fingerprint) { - printk(KERN_ERR - "cowloop - cowfile %s not related to rdofile " - " (fingerprint err - rdofile modified?)\n", cowf); - return -EINVAL; - } - } else { - /* - ** new cowfile: determine the minimal size (cowhead+bitmap) - */ - offset = (loff_t) MAPUNIT + cowdev->mapsize - 1; - - if ( cowlo_writecowraw(cowdev, "", 1, offset) < 1) { - printk(KERN_ERR - "cowloop - cannot set cowfile to size %lld\n", - offset+1); - return -EINVAL; - } - - /* - ** prepare new cowhead - */ - cowdev->cowhead->magic = COWMAGIC; - cowdev->cowhead->version = COWVERSION; - cowdev->cowhead->mapunit = MAPUNIT; - cowdev->cowhead->mapsize = cowdev->mapsize; - cowdev->cowhead->rdoblocks = cowdev->numblocks; - cowdev->cowhead->rdofingerprint = cowdev->fingerprint; - cowdev->cowhead->cowused = 0; - - /* - ** calculate start offset of data in cowfile, - ** rounded up to multiple of 4K to avoid - ** unnecessary disk-usage for written datablocks in - ** the sparsed cowfile on e.g. 4K filesystems - */ - cowdev->cowhead->doffset = - ((MAPUNIT+cowdev->mapsize+4095)>>12)<<12; - } - - cowdev->cowhead->flags = 0; - - DEBUGP(DCOW"cowloop - reserve space bitmap....\n"); - - /* - ** reserve space in memory for the entire bitmap and - ** fill it with the bitmap-data from disk; the entire - ** bitmap is allocated in several chunks because kmalloc - ** has restrictions regarding the allowed size per kmalloc - */ - cowdev->mapcount = (cowdev->mapsize+MAPCHUNKSZ-1)/MAPCHUNKSZ; - - /* - ** the size of every bitmap chunk will be MAPCHUNKSZ bytes, except for - ** the last bitmap chunk: calculate remaining size for this chunk - */ - if (cowdev->mapsize % MAPCHUNKSZ == 0) - cowdev->mapremain = MAPCHUNKSZ; - else - cowdev->mapremain = cowdev->mapsize % MAPCHUNKSZ; - - /* - ** allocate space to store all pointers for the bitmap-chunks - ** (initialize area with zeroes to allow proper undo) - */ - cowdev->mapcache = kmalloc(cowdev->mapcount * sizeof(char *), - GFP_KERNEL); - if (!cowdev->mapcache) { - printk(KERN_ERR - "cowloop - can not allocate space for bitmap ptrs\n"); - return -ENOMEM; - } - - memset(cowdev->mapcache, 0, cowdev->mapcount * sizeof(char *)); - - /* - ** allocate space to store the bitmap-chunks themselves - */ - for (i=0; i < cowdev->mapcount; i++) { - if (i < (cowdev->mapcount-1)) - *(cowdev->mapcache+i) = kmalloc(MAPCHUNKSZ, GFP_KERNEL); - else - *(cowdev->mapcache+i) = kmalloc(cowdev->mapremain, - GFP_KERNEL); - - if (*(cowdev->mapcache+i) == NULL) { - printk(KERN_ERR "cowloop - no space for bitmapchunk %ld" - " totmapsz=%ld, mapcnt=%d mapunit=%d\n", - i, cowdev->mapsize, cowdev->mapcount, - MAPUNIT); - return -ENOMEM; - } - } - - DEBUGP(DCOW"cowloop - read bitmap from cow....\n"); - - /* - ** read the entire bitmap from the cowfile into the in-memory cache; - ** count the number of blocks that are in use already - ** (statistical purposes) - */ - for (i=0, offset=MAPUNIT; i < cowdev->mapcount; - i++, offset+=MAPCHUNKSZ) { - unsigned long numbytes; - - if (i < (cowdev->mapcount-1)) - /* - ** full bitmap chunk - */ - numbytes = MAPCHUNKSZ; - else - /* - ** last bitmap chunk: might be partly filled - */ - numbytes = cowdev->mapremain; - - cowlo_readcowraw(cowdev, *(cowdev->mapcache+i), - numbytes, offset); - } - - /* - ** if the cowfile was dirty and automatic recovery is required, - ** reconstruct a proper bitmap in memory now - */ - if (cowdev->cowhead->flags & COWDIRTY) { - unsigned long long blocknum; - char databuf[MAPUNIT]; - unsigned long mapnum, mapbyte, mapbit; - - printk(KERN_NOTICE "cowloop - recover dirty cowfile %s....\n", - cowf); - - /* - ** read all data blocks - */ - for (blocknum=0, rv=1, offset=0; - cowlo_readcow(cowdev, databuf, MAPUNIT, offset) > 0; - blocknum++, offset += MAPUNIT) { - - /* - ** if this datablock contains real data (not binary - ** zeroes), set the corresponding bit in the bitmap - */ - if ( memcmp(databuf, allzeroes, MAPUNIT) == 0) - continue; - - mapnum = CALCMAP (blocknum); - mapbyte = CALCBYTE(blocknum); - mapbit = CALCBIT (blocknum); - - *(*(cowdev->mapcache+mapnum)+mapbyte) |= (1<nrcowblocks = 0; i < cowdev->mapcount; i++) { - long numbytes; - char *p; - - if (i < (cowdev->mapcount-1)) - numbytes = MAPCHUNKSZ; - else - numbytes = cowdev->mapremain; - - p = *(cowdev->mapcache+i); - - for (numbytes--; numbytes >= 0; numbytes--, p++) { - /* - ** for only eight checks the following construction - ** is faster than a loop-construction - */ - if ((*p) & 0x01) cowdev->nrcowblocks++; - if ((*p) & 0x02) cowdev->nrcowblocks++; - if ((*p) & 0x04) cowdev->nrcowblocks++; - if ((*p) & 0x08) cowdev->nrcowblocks++; - if ((*p) & 0x10) cowdev->nrcowblocks++; - if ((*p) & 0x20) cowdev->nrcowblocks++; - if ((*p) & 0x40) cowdev->nrcowblocks++; - if ((*p) & 0x80) cowdev->nrcowblocks++; - } - } - - /* - ** consistency-check for number of bits set in bitmap - */ - if ( !(cowdev->cowhead->flags & COWDIRTY) && - (cowdev->cowhead->cowused != cowdev->nrcowblocks) ) { - printk(KERN_ERR "cowloop - inconsistent cowfile admi\n"); - return -EINVAL; - } - - return 0; -} - -/* -** undo memory allocs and file opens issued so far -** related to the cowfile -*/ -static void -cowlo_undo_opencow(struct cowloop_device *cowdev) -{ - int i; - - if (cowdev->mapcache) { - for (i=0; i < cowdev->mapcount; i++) { - if (*(cowdev->mapcache+i) != NULL) - kfree( *(cowdev->mapcache+i) ); - } - - kfree(cowdev->mapcache); - } - - if (cowdev->cowhead) - kfree(cowdev->cowhead); - - if ( (cowdev->state & COWCOWOPEN) && (cowdev->cowfp) ) - filp_close(cowdev->cowfp, 0); - - /* - ** mark cowfile closed - */ - cowdev->state &= ~COWCOWOPEN; -} - -/* -** flush the entire bitmap and the cowhead (clean) to the cowfile -** -** must be called with the cowdevices-lock set -*/ -static void -cowlo_sync(void) -{ - int i, minor; - loff_t offset; - struct cowloop_device *cowdev; - - for (minor=0; minor < maxcows; minor++) { - cowdev = cowdevall[minor]; - if ( ! (cowdev->state & COWRWCOWOPEN) ) - continue; - - for (i=0, offset=MAPUNIT; i < cowdev->mapcount; - i++, offset += MAPCHUNKSZ) { - unsigned long numbytes; - - if (i < (cowdev->mapcount-1)) - /* - ** full bitmap chunk - */ - numbytes = MAPCHUNKSZ; - else - /* - ** last bitmap chunk: might be partly filled - */ - numbytes = cowdev->mapremain; - - DEBUGP(DCOW - "cowloop - flushing bitmap %2d (%3ld Kb)\n", - i, numbytes/1024); - - if (cowlo_writecowraw(cowdev, *(cowdev->mapcache+i), - numbytes, offset) < numbytes) { - break; - } - } - - /* - ** flush clean up-to-date cowhead to cowfile - */ - cowdev->cowhead->cowused = cowdev->nrcowblocks; - cowdev->cowhead->flags &= ~COWDIRTY; - - DEBUGP(DCOW "cowloop - flushing cowhead (%3d Kb)\n", - MAPUNIT/1024); - - cowlo_writecowraw(cowdev, cowdev->cowhead, MAPUNIT, (loff_t) 0); - } -} - -/*****************************************************************************/ -/* Module loading/unloading */ -/*****************************************************************************/ - -/* -** called during insmod/modprobe -*/ -static int __init -cowlo_init_module(void) -{ - int rv; - int minor, uptocows; - - revision[sizeof revision - 3] = '\0'; - - printk(KERN_NOTICE "cowloop - (C) 2009 ATComputing.nl - version: %s\n", &revision[11]); - printk(KERN_NOTICE "cowloop - info: www.ATComputing.nl/cowloop\n"); - - memset(allzeroes, 0, MAPUNIT); - - /* - ** Setup administration for all possible cowdevices. - ** Note that their minor numbers go from 0 to MAXCOWS-1 inclusive - ** and minor == MAXCOWS-1 is reserved for the control device. - */ - if ((maxcows < 1) || (maxcows > MAXCOWS)) { - printk(KERN_WARNING - "cowloop - maxcows exceeds maximum of %d\n", MAXCOWS); - - maxcows = DFLCOWS; - } - - /* allocate room for a table with a pointer to each cowloop_device: */ - if ( (cowdevall = kmalloc(maxcows * sizeof(struct cowloop_device *), - GFP_KERNEL)) == NULL) { - printk(KERN_WARNING - "cowloop - can not alloc table for %d devs\n", maxcows); - uptocows = 0; - rv = -ENOMEM; - goto error_out; - } - memset(cowdevall, 0, maxcows * sizeof(struct cowloop_device *)); - /* then hook an actual cowloop_device struct to each pointer: */ - for (minor=0; minor < maxcows; minor++) { - if ((cowdevall[minor] = kmalloc(sizeof(struct cowloop_device), - GFP_KERNEL)) == NULL) { - printk(KERN_WARNING - "cowloop - can not alloc admin-struct for dev no %d\n", minor); - - uptocows = minor; /* this is how far we got.... */ - rv = -ENOMEM; - goto error_out; - } - memset(cowdevall[minor], 0, sizeof(struct cowloop_device)); - } - uptocows = maxcows; /* we got all devices */ - - sema_init(&cowdevlock, 1); - - /* - ** register cowloop module - */ - if ( register_blkdev(COWMAJOR, DEVICE_NAME) < 0) { - printk(KERN_WARNING - "cowloop - unable to get major %d for cowloop\n", COWMAJOR); - rv = -EIO; - goto error_out; - } - - /* - ** create a directory below /proc to allocate a file - ** for each cowdevice that is allocated later on - */ - cowlo_procdir = proc_mkdir("cow", NULL); - - /* - ** check if a cowdevice has to be opened during insmod/modprobe; - ** two parameters should be specified then: rdofile= and cowfile= - */ - if( (rdofile[0] != '\0') && (cowfile[0] != '\0') ) { - char *po = option; - int wantrecover = 0; - - /* - ** check if automatic recovery is wanted - */ - while (*po) { - if (*po == 'r') { - wantrecover = 1; - break; - } - po++; - } - - /* - ** open new cowdevice with minor number 0 - */ - if ( (rv = cowlo_openpair(rdofile, cowfile, wantrecover, 0))) { - remove_proc_entry("cow", NULL); - unregister_blkdev(COWMAJOR, DEVICE_NAME); - goto error_out; - } - } else { - /* - ** check if only one parameter has been specified - */ - if( (rdofile[0] != '\0') || (cowfile[0] != '\0') ) { - printk(KERN_ERR - "cowloop - only one filename specified\n"); - remove_proc_entry("cow", NULL); - unregister_blkdev(COWMAJOR, DEVICE_NAME); - rv = -EINVAL; - goto error_out; - } - } - - /* - ** allocate fake disk as control channel to handle the requests - ** to activate and deactivate cowdevices dynamically - */ - if (!(cowctlgd = alloc_disk(1))) { - printk(KERN_WARNING - "cowloop - unable to alloc_disk for cowctl\n"); - - remove_proc_entry("cow", NULL); - (void) cowlo_closepair(cowdevall[0]); - unregister_blkdev(COWMAJOR, DEVICE_NAME); - rv = -ENOMEM; - goto error_out; - } - - spin_lock_init(&cowctlrqlock); - cowctlgd->major = COWMAJOR; - cowctlgd->first_minor = COWCTL; - cowctlgd->minors = 1; - cowctlgd->fops = &cowlo_fops; - cowctlgd->private_data = NULL; - /* the device has capacity 0, so there will be no q-requests */ - cowctlgd->queue = blk_init_queue(NULL, &cowctlrqlock); - sprintf(cowctlgd->disk_name, "cowctl"); - set_capacity(cowctlgd, 0); - - add_disk(cowctlgd); - - printk(KERN_NOTICE "cowloop - number of configured cowdevices: %d\n", - maxcows); - if (rdofile[0] != '\0') { - printk(KERN_NOTICE "cowloop - initialized on rdofile=%s\n", - rdofile); - } else { - printk(KERN_NOTICE "cowloop - initialized without rdofile yet\n"); - } - return 0; - -error_out: - for (minor=0; minor < uptocows ; minor++) { - kfree(cowdevall[minor]); - } - kfree(cowdevall); - return rv; -} - -/* -** called during rmmod -*/ -static void __exit -cowlo_cleanup_module(void) -{ - int minor; - - /* - ** flush bitmaps and cowheads to the cowfiles - */ - down(&cowdevlock); - cowlo_sync(); - up(&cowdevlock); - - /* - ** close all cowdevices - */ - for (minor=0; minor < maxcows; minor++) - (void) cowlo_closepair(cowdevall[minor]); - - unregister_blkdev(COWMAJOR, DEVICE_NAME); - - /* - ** get rid of /proc/cow and unregister the driver - */ - remove_proc_entry("cow", NULL); - - for (minor = 0; minor < maxcows; minor++) { - kfree(cowdevall[minor]); - } - kfree(cowdevall); - - del_gendisk(cowctlgd); /* revert the alloc_disk() */ - put_disk (cowctlgd); /* revert the add_disk() */ - blk_cleanup_queue(cowctlgd->queue); /* cleanup the empty queue */ - - printk(KERN_NOTICE "cowloop - unloaded\n"); -} - -module_init(cowlo_init_module); -module_exit(cowlo_cleanup_module); diff --git a/drivers/staging/cowloop/cowloop.h b/drivers/staging/cowloop/cowloop.h deleted file mode 100644 index bbd4a35ac667..000000000000 --- a/drivers/staging/cowloop/cowloop.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -** DO NOT MODIFY THESE VALUES (would make old cowfiles unusable) -*/ -#define MAPUNIT 1024 /* blocksize for bit in bitmap */ -#define MUSHIFT 10 /* bitshift for bit in bitmap */ -#define MUMASK 0x3ff /* bitmask for bit in bitmap */ - -#define COWMAGIC 0x574f437f /* byte-swapped '7f C O W' */ -#define COWDIRTY 0x01 -#define COWPACKED 0x02 -#define COWVERSION 1 - -struct cowhead -{ - int magic; /* identifies a cowfile */ - short version; /* version of cowhead */ - short flags; /* flags indicating status */ - unsigned long mapunit; /* blocksize per bit in bitmap */ - unsigned long mapsize; /* total size of bitmap (bytes) */ - unsigned long doffset; /* start-offset datablocks in cow */ - unsigned long rdoblocks; /* size of related read-only file */ - unsigned long rdofingerprint; /* fingerprint of read-only file */ - unsigned long cowused; /* number of datablocks used in cow */ -}; - -#define COWDEVDIR "/dev/cow/" -#define COWDEVICE COWDEVDIR "%ld" -#define COWCONTROL COWDEVDIR "ctl" - -#define MAXCOWS 1024 -#define COWCTL (MAXCOWS-1) /* minor number of /dev/cow/ctl */ - -#define COWPROCDIR "/proc/cow/" -#define COWPROCFILE COWPROCDIR "%d" - -/* -** ioctl related stuff -*/ -#define ANYDEV ((unsigned long)-1) - -struct cowpair -{ - unsigned char *rdofile; /* pathname of the rdofile */ - unsigned char *cowfile; /* pathname of the cowfile */ - unsigned short rdoflen; /* length of rdofile pathname */ - unsigned short cowflen; /* length of cowfile pathname */ - unsigned long device; /* requested/returned device number */ -}; - -struct cowwatch -{ - int flags; /* request flags */ - unsigned long device; /* requested device number */ - unsigned long threshold; /* continue if free Kb < threshold */ - unsigned long totalkb; /* ret: total filesystem size (Kb) */ - unsigned long availkb; /* ret: free filesystem size (Kb) */ -}; - -#define WATCHWAIT 0x01 /* block until threshold reached */ - -#define COWSYNC _IO ('C', 1) -#define COWMKPAIR _IOW ('C', 2, struct cowpair) -#define COWRMPAIR _IOW ('C', 3, unsigned long) -#define COWWATCH _IOW ('C', 4, struct cowwatch) -#define COWCLOSE _IOW ('C', 5, unsigned long) -#define COWRDOPEN _IOW ('C', 6, unsigned long) -- cgit v1.2.3 From 4074e77ca3c0c5047acb012a440022ca1f3a2dab Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Mon, 21 Sep 2009 12:52:15 +0300 Subject: Staging: w35und: Fix ->beacon_int breakage Commit f424afa17899408cbd267a4c4534ca6fc9d8f71c ("mac80211: remove deprecated API") removed ->beacon_int from struct ieee80211_conf. Fix breakage in w35und by setting beacon period in ->add_interface to bss_conf.beacon_int. Cc: Jiri Benc Cc: Jiri Kosina Cc: Johannes Berg Cc: Pavel Machek Cc: Sandro Bonazzola Signed-off-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- drivers/staging/winbond/wbusb.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index 8950724f168e..ee6425e43571 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -51,10 +51,26 @@ static struct ieee80211_supported_band wbsoft_band_2GHz = { .n_bitrates = ARRAY_SIZE(wbsoft_rates), }; +static void hal_set_beacon_period(struct hw_data *pHwData, u16 beacon_period) +{ + u32 tmp; + + if (pHwData->SurpriseRemove) + return; + + pHwData->BeaconPeriod = beacon_period; + tmp = pHwData->BeaconPeriod << 16; + tmp |= pHwData->ProbeDelay; + Wb35Reg_Write(pHwData, 0x0848, tmp); +} + static int wbsoft_add_interface(struct ieee80211_hw *dev, struct ieee80211_if_init_conf *conf) { - printk("wbsoft_add interface called\n"); + struct wbsoft_priv *priv = dev->priv; + + hal_set_beacon_period(&priv->sHwData, conf->vif->bss_conf.beacon_int); + return 0; } @@ -138,19 +154,6 @@ static void hal_set_radio_mode(struct hw_data *pHwData, unsigned char radio_off) Wb35Reg_Write(pHwData, 0x0824, reg->M24_MacControl); } -static void hal_set_beacon_period(struct hw_data *pHwData, u16 beacon_period) -{ - u32 tmp; - - if (pHwData->SurpriseRemove) - return; - - pHwData->BeaconPeriod = beacon_period; - tmp = pHwData->BeaconPeriod << 16; - tmp |= pHwData->ProbeDelay; - Wb35Reg_Write(pHwData, 0x0848, tmp); -} - static void hal_set_current_channel_ex(struct hw_data *pHwData, ChanInfo channel) { @@ -244,7 +247,6 @@ static void hal_set_accept_beacon(struct hw_data *pHwData, u8 enable) static int wbsoft_config(struct ieee80211_hw *dev, u32 changed) { struct wbsoft_priv *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; ChanInfo ch; printk("wbsoft_config called\n"); @@ -254,7 +256,6 @@ static int wbsoft_config(struct ieee80211_hw *dev, u32 changed) ch.ChanNo = 1; hal_set_current_channel(&priv->sHwData, ch); - hal_set_beacon_period(&priv->sHwData, conf->beacon_int); hal_set_accept_broadcast(&priv->sHwData, 1); hal_set_accept_promiscuous(&priv->sHwData, 1); hal_set_accept_multicast(&priv->sHwData, 1); -- cgit v1.2.3 From 6d7fd647c236c5b4ac4a27b17180ea8d7d169c8d Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 8 Oct 2009 17:27:51 -0400 Subject: Staging: winbond: implement prepare_multicast and fix API usage This patch adds a prepare_multicast callback for the winbond driver to properly receive mc_count in ->configure_filter. This also fixes incompatible pointer assignment build errors because ->configure_filter had changed. This is build tested only, but that's more than the original code received. Signed-off-by: Jeff Mahoney Signed-off-by: Greg Kroah-Hartman --- drivers/staging/winbond/wbusb.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index ee6425e43571..067082a7d759 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -99,10 +99,16 @@ static int wbsoft_get_tx_stats(struct ieee80211_hw *hw, return 0; } +static u64 wbsoft_prepare_multicast(struct ieee80211_hw *hw, int mc_count, + struct dev_addr_list *mc_list) +{ + return mc_count; +} + static void wbsoft_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { unsigned int new_flags; @@ -110,7 +116,7 @@ static void wbsoft_configure_filter(struct ieee80211_hw *dev, if (*total_flags & FIF_PROMISC_IN_BSS) new_flags |= FIF_PROMISC_IN_BSS; - else if ((*total_flags & FIF_ALLMULTI) || (mc_count > 32)) + else if ((*total_flags & FIF_ALLMULTI) || (multicast > 32)) new_flags |= FIF_ALLMULTI; dev->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS; @@ -278,6 +284,7 @@ static const struct ieee80211_ops wbsoft_ops = { .add_interface = wbsoft_add_interface, .remove_interface = wbsoft_remove_interface, .config = wbsoft_config, + .prepare_multicast = wbsoft_prepare_multicast, .configure_filter = wbsoft_configure_filter, .get_stats = wbsoft_get_stats, .get_tx_stats = wbsoft_get_tx_stats, -- cgit v1.2.3 From 1c115ebe224d5345e1b2dde21625631029ebdc83 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 8 Oct 2009 17:32:42 -0400 Subject: Staging: iio: Don't build on s390 The IIO core expects request_irq to work, which doesn't appear to exist on s390. Signed-off-by: Jeff Mahoney Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index beb99a547f09..4586650d65c3 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -4,6 +4,7 @@ menuconfig IIO tristate "Industrial I/O support" + depends on !S390 ---help--- The industrial I/O subsystem provides a unified framework for drivers for many different types of embedded sensors using a -- cgit v1.2.3 From 3d14b518481eb1fa10c6f6ef9efd5f4cc0f84b28 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 8 Oct 2009 17:30:40 -0400 Subject: Staging: rtl8192e: Add #include This driver uses vmalloc but for whatever reason vmalloc.h isn't included on ppc. Signed-off-by: Jeff Mahoney Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8192e/r8192E_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/rtl8192e/r8192E_core.c b/drivers/staging/rtl8192e/r8192E_core.c index d4fa65489655..b0802a7aeb5f 100644 --- a/drivers/staging/rtl8192e/r8192E_core.c +++ b/drivers/staging/rtl8192e/r8192E_core.c @@ -46,6 +46,7 @@ #undef DEBUG_TX_DESC //#define CONFIG_RTL8192_IO_MAP +#include #include #include "r8192E_hw.h" #include "r8192E.h" -- cgit v1.2.3 From a8ba8bffbe1f3f8b3450d294b9e68d299b158209 Mon Sep 17 00:00:00 2001 From: "Serge E. Hallyn" Date: Wed, 16 Sep 2009 16:27:41 -0500 Subject: Staging: p9auth: a few fixes 1. The memory into which we copy 'u1@u2' needs space for u1, @, u2, and a final \0 which strcat copies in. 2. Strsep changes the value of its first argument. So use a temporary variable to pass to it, so we pass the original value to kfree! 3. Allocate an extra char to user_buf, because we need a trailing \0 since we later kstrdup it. I am about to send out an LTP testcase for this driver, but in addition the correctness of the hashing can be verified as follows: #include #include #include int main(int argc, char *argv[]) { char in[41], out[20]; unsigned int v; int i, ret; ret = read(STDIN_FILENO, in, 40); if (ret != 40) exit(1); in[40] = '\0'; for (i = 0; i < 20; i++) { sscanf(&in[2*i], "%02x", &v); out[i] = v; } write(STDOUT_FILENO, out, 20); } as root, to test userid 501 switching to uid 0, choosing 'random' string 'ab': echo -n "501@0" > plain openssl sha1 -hmac 'ab' plain |awk '{ print $2 '} > dgst ./unhex < dgst > dgst.u mknod /dev/caphash 504 0 mknod /dev/capuse 504 1 chmod ugo+w /dev/capuse cat dgst.u > /dev/caphash as uid 501, echo "501@0@ab" > /dev/capuse id -u # should now show 0. Signed-off-by: Serge E. Hallyn Signed-off-by: Greg Kroah-Hartman --- drivers/staging/p9auth/p9auth.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/staging/p9auth/p9auth.c b/drivers/staging/p9auth/p9auth.c index 9111dcba37a1..8ccfff723eec 100644 --- a/drivers/staging/p9auth/p9auth.c +++ b/drivers/staging/p9auth/p9auth.c @@ -183,7 +183,7 @@ static ssize_t cap_write(struct file *filp, const char __user *buf, user_buf_running = NULL; hash_str = NULL; node_ptr = kmalloc(sizeof(struct cap_node), GFP_KERNEL); - user_buf = kzalloc(count, GFP_KERNEL); + user_buf = kzalloc(count+1, GFP_KERNEL); if (!node_ptr || !user_buf) goto out; @@ -207,6 +207,7 @@ static ssize_t cap_write(struct file *filp, const char __user *buf, list_add(&(node_ptr->list), &(dev->head->list)); node_ptr = NULL; } else { + char *tmpu; if (!cap_devices[0].head || list_empty(&(cap_devices[0].head->list))) { retval = -EINVAL; @@ -218,10 +219,10 @@ static ssize_t cap_write(struct file *filp, const char __user *buf, * need to split it and hash 'user1@user2' using 'randomstring' * as the key. */ - user_buf_running = kstrdup(user_buf, GFP_KERNEL); - source_user = strsep(&user_buf_running, "@"); - target_user = strsep(&user_buf_running, "@"); - rand_str = strsep(&user_buf_running, "@"); + tmpu = user_buf_running = kstrdup(user_buf, GFP_KERNEL); + source_user = strsep(&tmpu, "@"); + target_user = strsep(&tmpu, "@"); + rand_str = tmpu; if (!source_user || !target_user || !rand_str) { retval = -EINVAL; goto out; @@ -229,7 +230,8 @@ static ssize_t cap_write(struct file *filp, const char __user *buf, /* hash the string user1@user2 with rand_str as the key */ len = strlen(source_user) + strlen(target_user) + 1; - hash_str = kzalloc(len, GFP_KERNEL); + /* src, @, len, \0 */ + hash_str = kzalloc(len+1, GFP_KERNEL); strcat(hash_str, source_user); strcat(hash_str, "@"); strcat(hash_str, target_user); -- cgit v1.2.3 From b8b5cd9f87e08f72c78d9197bf199821fda4ba36 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 21 Sep 2009 14:55:23 -0400 Subject: Staging: comedi: Corrected type of a printk argument in resize_async_buffer(). Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/comedi_fops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index f54bb9b3ee37..aaad76e0a76a 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -2337,7 +2337,7 @@ static int resize_async_buffer(struct comedi_device *dev, } DPRINTK("comedi%i subd %d buffer resized to %i bytes\n", - dev->minor, s - dev->subdevices, async->prealloc_bufsz); + dev->minor, (int)(s - dev->subdevices), async->prealloc_bufsz); return 0; } -- cgit v1.2.3 From b1f68dc1d610159db0e8546117d86657f35a03b5 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 21 Sep 2009 15:12:32 -0400 Subject: Staging: comedi: jr3_pci: Initialize transf variable fully in jr3_pci_poll_subdevice(). Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/jr3_pci.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/staging/comedi/drivers/jr3_pci.c b/drivers/staging/comedi/drivers/jr3_pci.c index 14bf29bf5781..0d2c2eb23b23 100644 --- a/drivers/staging/comedi/drivers/jr3_pci.c +++ b/drivers/staging/comedi/drivers/jr3_pci.c @@ -515,6 +515,7 @@ static struct poll_delay_t jr3_pci_poll_subdevice(struct comedi_subdevice *s) { struct poll_delay_t result = poll_delay_min_max(1000, 2000); struct jr3_pci_subdev_private *p = s->private; + int i; if (p) { volatile struct jr3_channel *channel = p->channel; @@ -570,18 +571,11 @@ static struct poll_delay_t jr3_pci_poll_subdevice(struct comedi_subdevice *s) p->serial_no); /* Transformation all zeros */ - transf.link[0].link_type = - (enum link_types)0; - transf.link[0].link_amount = 0; - transf.link[1].link_type = - (enum link_types)0; - transf.link[1].link_amount = 0; - transf.link[2].link_type = - (enum link_types)0; - transf.link[2].link_amount = 0; - transf.link[3].link_type = - (enum link_types)0; - transf.link[3].link_amount = 0; + for (i = 0; i < ARRAY_SIZE(transf.link); i++) { + transf.link[i].link_type = + (enum link_types)0; + transf.link[i].link_amount = 0; + } set_transforms(channel, transf, 0); use_transform(channel, 0); -- cgit v1.2.3 From aa65d22ae6412f474e5ba23fc363137b6c53eef9 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 21 Sep 2009 15:23:32 -0400 Subject: Staging: comedi: cb_pcidio: fix "section mismatch" error Store PCI device IDs in the board info and use this for matching IDs in the code instead of using the module device table. This avoids a "section mismatch" error. Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/cb_pcidio.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/comedi/drivers/cb_pcidio.c b/drivers/staging/comedi/drivers/cb_pcidio.c index 4d10bc31d461..09e6e3bdfb3e 100644 --- a/drivers/staging/comedi/drivers/cb_pcidio.c +++ b/drivers/staging/comedi/drivers/cb_pcidio.c @@ -53,7 +53,8 @@ Passing a zero for an option is the same as leaving it unspecified. * Some drivers use arrays such as this, other do not. */ struct pcidio_board { - const char *name; /* anme of the board */ + const char *name; /* name of the board */ + int dev_id; int n_8255; /* number of 8255 chips on board */ /* indices of base address regions */ @@ -64,18 +65,21 @@ struct pcidio_board { static const struct pcidio_board pcidio_boards[] = { { .name = "pci-dio24", + .dev_id = 0x0028, .n_8255 = 1, .pcicontroler_badrindex = 1, .dioregs_badrindex = 2, }, { .name = "pci-dio24h", + .dev_id = 0x0014, .n_8255 = 1, .pcicontroler_badrindex = 1, .dioregs_badrindex = 2, }, { .name = "pci-dio48h", + .dev_id = 0x000b, .n_8255 = 2, .pcicontroler_badrindex = 0, .dioregs_badrindex = 1, @@ -206,7 +210,7 @@ static int pcidio_attach(struct comedi_device *dev, struct comedi_devconfig *it) continue; /* loop through cards supported by this driver */ for (index = 0; index < ARRAY_SIZE(pcidio_boards); index++) { - if (pcidio_pci_table[index].device != pcidev->device) + if (pcidio_boards[index].dev_id != pcidev->device) continue; /* was a particular bus/slot requested? */ -- cgit v1.2.3 From 10f27014f42f0a4cb32bf45f214fa7ad7c261b1e Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 21 Sep 2009 15:29:59 -0400 Subject: Staging: comedi: s526: Fix number of channels on DIO subdevice Correct operation of INSN_CONFIG_DIO_INPUT and INSN_CONFIG_DIO_OUTPUT and support INSN_CONFIG_DIO_QUERY. Thanks to Alessio Margan for some testing. Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/s526.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/staging/comedi/drivers/s526.c b/drivers/staging/comedi/drivers/s526.c index b89e1ec267c5..736441f3c815 100644 --- a/drivers/staging/comedi/drivers/s526.c +++ b/drivers/staging/comedi/drivers/s526.c @@ -375,7 +375,7 @@ static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) if (thisboard->have_dio) { s->type = COMEDI_SUBD_DIO; s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - s->n_chan = 2; + s->n_chan = 8; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = s526_dio_insn_bits; @@ -949,7 +949,7 @@ static int s526_dio_insn_bits(struct comedi_device *dev, data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */ /* or we could just return the software copy of the output values if * it was a purely digital output subdevice */ - /* data[1]=s->state; */ + /* data[1]=s->state & 0xFF; */ return 2; } @@ -959,28 +959,33 @@ static int s526_dio_insn_config(struct comedi_device *dev, struct comedi_insn *insn, unsigned int *data) { int chan = CR_CHAN(insn->chanspec); - short value; + int group, mask; printk("S526 DIO insn_config\n"); - if (insn->n != 1) - return -EINVAL; - - value = inw(ADDR_REG(REG_DIO)); - /* The input or output configuration of each digital line is * configured by a special insn_config instruction. chanspec * contains the channel to be changed, and data[0] contains the * value COMEDI_INPUT or COMEDI_OUTPUT. */ - if (data[0] == COMEDI_OUTPUT) { - value |= 1 << (chan + 10); /* bit 10/11 set the group 1/2's mode */ - s->io_bits |= (0xF << chan); - } else { - value &= ~(1 << (chan + 10)); /* 1 is output, 0 is input. */ - s->io_bits &= ~(0xF << chan); + group = chan >> 2; + mask = 0xF << (group << 2); + switch (data[0]) { + case INSN_CONFIG_DIO_OUTPUT: + s->state |= 1 << (group + 10); // bit 10/11 set the group 1/2's mode + s->io_bits |= mask; + break; + case INSN_CONFIG_DIO_INPUT: + s->state &= ~(1 << (group + 10));// 1 is output, 0 is input. + s->io_bits &= ~mask; + break; + case INSN_CONFIG_DIO_QUERY: + data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT; + return insn->n; + default: + return -EINVAL; } - outw(value, ADDR_REG(REG_DIO)); + outw(s->state, ADDR_REG(REG_DIO)); return 1; } -- cgit v1.2.3 From ca98ee7bb4005365ad395b53229e2f6d01ca7cbd Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 21 Sep 2009 15:35:42 -0400 Subject: Staging: comedi: s526: Get rid of global variable 'cmReg'. Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/s526.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/staging/comedi/drivers/s526.c b/drivers/staging/comedi/drivers/s526.c index 736441f3c815..dbde118f2a16 100644 --- a/drivers/staging/comedi/drivers/s526.c +++ b/drivers/staging/comedi/drivers/s526.c @@ -126,10 +126,10 @@ struct counter_mode_register_t { unsigned short reserved:1; }; -union { +union cmReg { struct counter_mode_register_t reg; unsigned short value; -} cmReg; +}; #define MAX_GPCT_CONFIG_DATA 6 @@ -285,6 +285,7 @@ static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) int i, n; /* short value; */ /* int subdev_channel = 0; */ + union cmReg cmReg; printk("comedi%d: s526: ", dev->minor); @@ -516,6 +517,7 @@ static int s526_gpct_insn_config(struct comedi_device *dev, int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */ int i; short value; + union cmReg cmReg; /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel); */ @@ -741,6 +743,7 @@ static int s526_gpct_winsn(struct comedi_device *dev, { int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */ short value; + union cmReg cmReg; printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel); cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel)); -- cgit v1.2.3 From 2b0318a600989ed85fe020079fa20132f8aebaf3 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 21 Sep 2009 15:38:34 -0400 Subject: Staging: comedi: s526: Take account of arch's byte order. Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/s526.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/staging/comedi/drivers/s526.c b/drivers/staging/comedi/drivers/s526.c index dbde118f2a16..9a8ebbe96999 100644 --- a/drivers/staging/comedi/drivers/s526.c +++ b/drivers/staging/comedi/drivers/s526.c @@ -43,6 +43,7 @@ comedi_config /dev/comedi0 s526 0x2C0,0x3 #include "../comedidev.h" #include +#include #define S526_SIZE 64 @@ -113,6 +114,7 @@ static const int s526_ports[] = { }; struct counter_mode_register_t { +#if defined (__LITTLE_ENDIAN_BITFIELD) unsigned short coutSource:1; unsigned short coutPolarity:1; unsigned short autoLoadResetRcap:3; @@ -124,6 +126,21 @@ struct counter_mode_register_t { unsigned short outputRegLatchCtrl:1; unsigned short preloadRegSel:1; unsigned short reserved:1; + #elif defined(__BIG_ENDIAN_BITFIELD) + unsigned short reserved:1; + unsigned short preloadRegSel:1; + unsigned short outputRegLatchCtrl:1; + unsigned short countDirCtrl:1; + unsigned short countDir:1; + unsigned short clockSource:2; + unsigned short ctEnableCtrl:2; + unsigned short hwCtEnableSource:2; + unsigned short autoLoadResetRcap:3; + unsigned short coutPolarity:1; + unsigned short coutSource:1; +#else +#error Unknown bit field order +#endif }; union cmReg { -- cgit v1.2.3 From 5044a2c0e0e951afeb4dce87e18e10036635410a Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 21 Sep 2009 15:48:33 -0400 Subject: Staging: comedi: s526: fixes for pulse generator Some changes and corrections to handling of INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR, and INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR, so they interpret insn->data[] as per the comments in the code. Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/s526.c | 50 +++++++++++++---------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/drivers/staging/comedi/drivers/s526.c b/drivers/staging/comedi/drivers/s526.c index 9a8ebbe96999..07c21e686f27 100644 --- a/drivers/staging/comedi/drivers/s526.c +++ b/drivers/staging/comedi/drivers/s526.c @@ -453,11 +453,11 @@ static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) udelay(1000); printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n))); - /* Load the pre-laod register high word */ + /* Load the pre-load register high word */ /* value = (short) (0x55); */ /* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */ - /* Load the pre-laod register low word */ + /* Load the pre-load register low word */ /* value = (short)(0xaa55); */ /* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */ @@ -587,19 +587,8 @@ static int s526_gpct_insn_config(struct comedi_device *dev, #if 1 /* Set Counter Mode Register */ - cmReg.reg.coutSource = 0; /* out RCAP */ - cmReg.reg.coutPolarity = 0; /* Polarity inverted */ - cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */ - cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */ - cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */ - cmReg.reg.clockSource = 3; /* x4 */ - cmReg.reg.countDir = 0; /* up */ - cmReg.reg.countDirCtrl = 0; /* quadrature */ - cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */ - cmReg.reg.preloadRegSel = 0; /* PR0 */ - cmReg.reg.reserved = 0; + cmReg.value = insn->data[1] & 0xFFFF; - /* Set Counter Mode Register */ /* printk("s526: Counter Mode register=%x\n", cmReg.value); */ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); @@ -634,11 +623,11 @@ static int s526_gpct_insn_config(struct comedi_device *dev, cmReg.value = (short)(insn->data[1] & 0xFFFF); outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); - /* Load the pre-laod register high word */ + /* Load the pre-load register high word */ value = (short)((insn->data[2] >> 16) & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); - /* Load the pre-laod register low word */ + /* Load the pre-load register low word */ value = (short)(insn->data[2] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); @@ -672,11 +661,11 @@ static int s526_gpct_insn_config(struct comedi_device *dev, cmReg.reg.preloadRegSel = 0; /* PR0 */ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); - /* Load the pre-laod register 0 high word */ + /* Load the pre-load register 0 high word */ value = (short)((insn->data[2] >> 16) & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); - /* Load the pre-laod register 0 low word */ + /* Load the pre-load register 0 low word */ value = (short)(insn->data[2] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); @@ -685,17 +674,17 @@ static int s526_gpct_insn_config(struct comedi_device *dev, cmReg.reg.preloadRegSel = 1; /* PR1 */ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); - /* Load the pre-laod register 1 high word */ + /* Load the pre-load register 1 high word */ value = (short)((insn->data[3] >> 16) & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); - /* Load the pre-laod register 1 low word */ + /* Load the pre-load register 1 low word */ value = (short)(insn->data[3] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); /* Write the Counter Control Register */ - if (insn->data[3] != 0) { - value = (short)(insn->data[3] & 0xFFFF); + if (insn->data[4] != 0) { + value = (short)(insn->data[4] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel)); } break; @@ -717,11 +706,11 @@ static int s526_gpct_insn_config(struct comedi_device *dev, cmReg.reg.preloadRegSel = 0; /* PR0 */ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); - /* Load the pre-laod register 0 high word */ + /* Load the pre-load register 0 high word */ value = (short)((insn->data[2] >> 16) & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); - /* Load the pre-laod register 0 low word */ + /* Load the pre-load register 0 low word */ value = (short)(insn->data[2] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); @@ -730,17 +719,17 @@ static int s526_gpct_insn_config(struct comedi_device *dev, cmReg.reg.preloadRegSel = 1; /* PR1 */ outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel)); - /* Load the pre-laod register 1 high word */ + /* Load the pre-load register 1 high word */ value = (short)((insn->data[3] >> 16) & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel)); - /* Load the pre-laod register 1 low word */ + /* Load the pre-load register 1 low word */ value = (short)(insn->data[3] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel)); /* Write the Counter Control Register */ - if (insn->data[3] != 0) { - value = (short)(insn->data[3] & 0xFFFF); + if (insn->data[4] != 0) { + value = (short)(insn->data[4] & 0xFFFF); outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel)); } break; @@ -795,9 +784,8 @@ static int s526_gpct_winsn(struct comedi_device *dev, (devpriv->s526_gpct_config[subdev_channel]).data[1] = insn->data[1]; } else { - printk("%d \t %d\n", insn->data[1], insn->data[2]); - printk - ("s526: INSN_WRITE: PTG: Problem with Pulse params\n"); + printk("s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n", + insn->data[0], insn->data[1]); return -EINVAL; } -- cgit v1.2.3 From 2b49d7c40ec0806d93ced89ac6039ddcf223dea7 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 21 Sep 2009 16:01:56 -0400 Subject: Staging: comedi: ni_65xx.c: fix insn_bits shift calculation. Fix insn_bits bitshift calculation for subdevice with non-zero base_port. Thanks to cJ-comedi at zougloub dot eu for spotting the bug. Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/ni_65xx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/comedi/drivers/ni_65xx.c b/drivers/staging/comedi/drivers/ni_65xx.c index 6b118c15b49e..b664ce083fc5 100644 --- a/drivers/staging/comedi/drivers/ni_65xx.c +++ b/drivers/staging/comedi/drivers/ni_65xx.c @@ -418,15 +418,15 @@ static int ni_65xx_dio_insn_bits(struct comedi_device *dev, return -EINVAL; base_bitfield_channel = CR_CHAN(insn->chanspec); for (j = 0; j < max_ports_per_bitfield; ++j) { + const unsigned port_offset = ni_65xx_port_by_channel(base_bitfield_channel) + j; const unsigned port = - sprivate(s)->base_port + - ni_65xx_port_by_channel(base_bitfield_channel) + j; + sprivate(s)->base_port + port_offset; unsigned base_port_channel; unsigned port_mask, port_data, port_read_bits; int bitshift; if (port >= ni_65xx_total_num_ports(board(dev))) break; - base_port_channel = port * ni_65xx_channels_per_port; + base_port_channel = port_offset * ni_65xx_channels_per_port; port_mask = data[0]; port_data = data[1]; bitshift = base_port_channel - base_bitfield_channel; -- cgit v1.2.3 From 078a10df24ac87d18c2d52fb501ff652f481fb5b Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 21 Sep 2009 16:05:01 -0400 Subject: Staging: comedi: ni_65xx.c: fix output inversion problem. When reading DO subdevice with inverted outputs invert the values read back from the ports to match the inversion of values written. Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/ni_65xx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/comedi/drivers/ni_65xx.c b/drivers/staging/comedi/drivers/ni_65xx.c index b664ce083fc5..bbf75eb6d7f2 100644 --- a/drivers/staging/comedi/drivers/ni_65xx.c +++ b/drivers/staging/comedi/drivers/ni_65xx.c @@ -457,6 +457,12 @@ static int ni_65xx_dio_insn_bits(struct comedi_device *dev, port_read_bits = readb(private(dev)->mite->daq_io_addr + Port_Data(port)); /* printk("read 0x%x from port %i\n", port_read_bits, port); */ + if (s->type == COMEDI_SUBD_DO && board(dev)->invert_outputs) { + /* Outputs inverted, so invert value read back from + * DO subdevice. (Does not apply to boards with DIO + * subdevice.) */ + port_read_bits ^= 0xFF; + } if (bitshift > 0) { port_read_bits <<= bitshift; } else { -- cgit v1.2.3 From ecb8486da36c5359eb910674c15c324f0157e43f Mon Sep 17 00:00:00 2001 From: Frank Mori Hess Date: Mon, 21 Sep 2009 16:10:21 -0400 Subject: Staging: comedi: ni_pcimio: Added device id for pxi-6225. Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/ni_pcimio.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/staging/comedi/drivers/ni_pcimio.c b/drivers/staging/comedi/drivers/ni_pcimio.c index 19d87553d906..24c8b8ed5b4c 100644 --- a/drivers/staging/comedi/drivers/ni_pcimio.c +++ b/drivers/staging/comedi/drivers/ni_pcimio.c @@ -29,7 +29,7 @@ Devices: [National Instruments] PCI-MIO-16XE-50 (ni_pcimio), PCI-MIO-16XE-10, PXI-6030E, PCI-MIO-16E-1, PCI-MIO-16E-4, PCI-6014, PCI-6040E, PXI-6040E, PCI-6030E, PCI-6031E, PCI-6032E, PCI-6033E, PCI-6071E, PCI-6023E, PCI-6024E, PCI-6025E, PXI-6025E, PCI-6034E, PCI-6035E, PCI-6052E, - PCI-6110, PCI-6111, PCI-6220, PCI-6221, PCI-6224, PXI-6224, PCI-6225, + PCI-6110, PCI-6111, PCI-6220, PCI-6221, PCI-6224, PXI-6224, PCI-6225, PXI-6225, PCI-6229, PCI-6250, PCI-6251, PCIe-6251, PCI-6254, PCI-6259, PCIe-6259, PCI-6280, PCI-6281, PXI-6281, PCI-6284, PCI-6289, PCI-6711, PXI-6711, PCI-6713, PXI-6713, @@ -179,6 +179,7 @@ static DEFINE_PCI_DEVICE_TABLE(ni_pci_table) = { PCI_VENDOR_ID_NATINST, 0x70f2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_NATINST, 0x710d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_NATINST, 0x716c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { + PCI_VENDOR_ID_NATINST, 0x716d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_NATINST, 0x717f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_NATINST, 0x71bc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_NATINST, 0x717d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { @@ -952,6 +953,25 @@ static const struct ni_board_struct ni_boards[] = { .caldac = {caldac_none}, .has_8255 = 0, }, + { + .device_id = 0x716d, + .name = "pxi-6225", + .n_adchan = 80, + .adbits = 16, + .ai_fifo_depth = 4095, + .gainlkup = ai_gain_622x, + .ai_speed = 4000, + .n_aochan = 2, + .aobits = 16, + .ao_fifo_depth = 8191, + .ao_range_table = &range_ni_M_622x_ao, + .reg_type = ni_reg_622x, + .ao_unipolar = 0, + .ao_speed = 1200, + .num_p0_dio_channels = 32, + .caldac = {caldac_none}, + .has_8255 = 0, + }, { .device_id = 0x70aa, .name = "pci-6229", -- cgit v1.2.3 From 3d1c28848ea37bf117940e10826f7a485aa32337 Mon Sep 17 00:00:00 2001 From: Frank Mori Hess Date: Tue, 22 Sep 2009 16:32:55 -0400 Subject: Staging: comedi: pcmcia irq fixes Replaced IRQ_TYPE_EXCLUSIVE with IRQ_TYPE_DYNAMIC_SHARING for pcmcia drivers. Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/cb_das16_cs.c | 2 +- drivers/staging/comedi/drivers/ni_daq_700.c | 2 +- drivers/staging/comedi/drivers/ni_daq_dio24.c | 2 +- drivers/staging/comedi/drivers/ni_labpc_cs.c | 2 +- drivers/staging/comedi/drivers/ni_mio_cs.c | 2 +- drivers/staging/comedi/drivers/quatech_daqp_cs.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/comedi/drivers/cb_das16_cs.c b/drivers/staging/comedi/drivers/cb_das16_cs.c index 12d12b43a6f1..80c0df8656f3 100644 --- a/drivers/staging/comedi/drivers/cb_das16_cs.c +++ b/drivers/staging/comedi/drivers/cb_das16_cs.c @@ -744,7 +744,7 @@ static int das16cs_pcmcia_attach(struct pcmcia_device *link) /* Initialize the pcmcia_device structure */ /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = NULL; diff --git a/drivers/staging/comedi/drivers/ni_daq_700.c b/drivers/staging/comedi/drivers/ni_daq_700.c index 6a7797604c97..ec31a3970664 100644 --- a/drivers/staging/comedi/drivers/ni_daq_700.c +++ b/drivers/staging/comedi/drivers/ni_daq_700.c @@ -520,7 +520,7 @@ static int dio700_cs_attach(struct pcmcia_device *link) link->priv = local; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = NULL; diff --git a/drivers/staging/comedi/drivers/ni_daq_dio24.c b/drivers/staging/comedi/drivers/ni_daq_dio24.c index b06e81c526e8..0700a8bddd1e 100644 --- a/drivers/staging/comedi/drivers/ni_daq_dio24.c +++ b/drivers/staging/comedi/drivers/ni_daq_dio24.c @@ -271,7 +271,7 @@ static int dio24_cs_attach(struct pcmcia_device *link) link->priv = local; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = NULL; diff --git a/drivers/staging/comedi/drivers/ni_labpc_cs.c b/drivers/staging/comedi/drivers/ni_labpc_cs.c index 57aecfa883c7..a3053b8da1c6 100644 --- a/drivers/staging/comedi/drivers/ni_labpc_cs.c +++ b/drivers/staging/comedi/drivers/ni_labpc_cs.c @@ -246,7 +246,7 @@ static int labpc_cs_attach(struct pcmcia_device *link) link->priv = local; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_FORCED_PULSE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FORCED_PULSE; link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_PULSE_ID; link->irq.Handler = NULL; diff --git a/drivers/staging/comedi/drivers/ni_mio_cs.c b/drivers/staging/comedi/drivers/ni_mio_cs.c index b7322963cf78..9aef87fc81dc 100644 --- a/drivers/staging/comedi/drivers/ni_mio_cs.c +++ b/drivers/staging/comedi/drivers/ni_mio_cs.c @@ -273,7 +273,7 @@ static int cs_attach(struct pcmcia_device *link) { link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; link->io.NumPorts1 = 16; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c index f63bdc35cffd..344b82353e08 100644 --- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c +++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c @@ -1079,7 +1079,7 @@ static int daqp_cs_attach(struct pcmcia_device *link) link->priv = local; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = daqp_interrupt; link->irq.Instance = local; -- cgit v1.2.3 From cad9e3c75bdddfb214fd2a5885f5cce3a38ba300 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 8 Oct 2009 10:26:11 -0700 Subject: Staging: comedi: fix build on arches that don't want comedi drivers This should fix the problem of comedi drivers showing up on arches that can not support the comedi subsystem (like s390). Cc: Martin Schwidefsky Cc: Kamalesh Babulal Cc: Ian Abbott Cc: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig index af723cb9d08f..d63c889ce557 100644 --- a/drivers/staging/comedi/Kconfig +++ b/drivers/staging/comedi/Kconfig @@ -1,7 +1,7 @@ config COMEDI tristate "Data acquisition support (comedi)" default N - depends on m + depends on m && (PCI || PCMCIA || PCCARD || USB) ---help--- Enable support a wide range of data acquisition devices for Linux. -- cgit v1.2.3 From 0cbd81a9f6bac734ac3266687bf027af1e395270 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 7 Oct 2009 20:05:04 +0200 Subject: USB: ftdi_sio: remove tty->low_latency Fixes tty_flip_buffer_push being called from hard interrupt context with low_latency set. Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 4f883b1773d0..0ac2c2f540c9 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1234,7 +1234,6 @@ static int set_serial_info(struct tty_struct *tty, (new_serial.flags & ASYNC_FLAGS)); priv->custom_divisor = new_serial.custom_divisor; - tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; write_latency_timer(port); check_and_exit: @@ -1704,9 +1703,6 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) priv->rx_bytes = 0; spin_unlock_irqrestore(&priv->rx_lock, flags); - if (tty) - tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - write_latency_timer(port); /* No error checking for this (will get errors later anyway) */ -- cgit v1.2.3 From 63b0061246b54b849da8f189ae048e8110d8ce7d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 7 Oct 2009 20:05:05 +0200 Subject: USB: ftdi_sio: remove unused rx_byte counter Remove unused rx_byte counter which is never exposed as noted by Alan Cox. Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 0ac2c2f540c9..bfb23d64bc6a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -81,7 +81,6 @@ struct ftdi_private { struct delayed_work rx_work; struct usb_serial_port *port; int rx_processed; - unsigned long rx_bytes; __u16 interface; /* FT2232C, FT2232H or FT4232H port interface (0 for FT232/245) */ @@ -1699,9 +1698,6 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) spin_lock_irqsave(&priv->tx_lock, flags); priv->tx_bytes = 0; spin_unlock_irqrestore(&priv->tx_lock, flags); - spin_lock_irqsave(&priv->rx_lock, flags); - priv->rx_bytes = 0; - spin_unlock_irqrestore(&priv->rx_lock, flags); write_latency_timer(port); @@ -2014,8 +2010,6 @@ static void ftdi_read_bulk_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct tty_struct *tty; struct ftdi_private *priv; - unsigned long countread; - unsigned long flags; int status = urb->status; if (urb->number_of_packets > 0) { @@ -2054,13 +2048,6 @@ static void ftdi_read_bulk_callback(struct urb *urb) goto out; } - /* count data bytes, but not status bytes */ - countread = urb->actual_length; - countread -= 2 * DIV_ROUND_UP(countread, priv->max_packet_size); - spin_lock_irqsave(&priv->rx_lock, flags); - priv->rx_bytes += countread; - spin_unlock_irqrestore(&priv->rx_lock, flags); - ftdi_process_read(&priv->rx_work.work); out: tty_kref_put(tty); -- cgit v1.2.3 From e63e278b4d2d867893962d3c7cd13a3a24ceb3f1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 7 Oct 2009 20:05:06 +0200 Subject: USB: ftdi_sio: clean up read completion handler Remove superfluous error checks in completion handler: - No need to check private data and urb pointers as we check urb-status before dereferencing priv (which is not freed until urb has been killed on close). - No need to check tty as it is checked again when processing. - No need to check urb->number_of_packets on bulk urb. Note that both private data and tty are checked again before processing (possibly from work queue which also is cancelled on close). Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index bfb23d64bc6a..75c84d9b080d 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2008,39 +2008,14 @@ static int ftdi_chars_in_buffer(struct tty_struct *tty) static void ftdi_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty; struct ftdi_private *priv; int status = urb->status; - if (urb->number_of_packets > 0) { - dev_err(&port->dev, "%s transfer_buffer_length %d " - "actual_length %d number of packets %d\n", __func__, - urb->transfer_buffer_length, - urb->actual_length, urb->number_of_packets); - dev_err(&port->dev, "%s transfer_flags %x\n", __func__, - urb->transfer_flags); - } - dbg("%s - port %d", __func__, port->number); if (port->port.count <= 0) return; - tty = tty_port_tty_get(&port->port); - if (!tty) { - dbg("%s - bad tty pointer - exiting", __func__); - return; - } - - priv = usb_get_serial_port_data(port); - if (!priv) { - dbg("%s - bad port private data pointer - exiting", __func__); - goto out; - } - - if (urb != port->read_urb) - dev_err(&port->dev, "%s - Not my urb!\n", __func__); - if (status) { /* This will happen at close every time so it is a dbg not an err */ @@ -2048,9 +2023,8 @@ static void ftdi_read_bulk_callback(struct urb *urb) goto out; } + priv = usb_get_serial_port_data(port); ftdi_process_read(&priv->rx_work.work); -out: - tty_kref_put(tty); } /* ftdi_read_bulk_callback */ -- cgit v1.2.3 From cc01f17d5cb8ac604108515735aeca72e17944c1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 7 Oct 2009 20:05:07 +0200 Subject: USB: ftdi_sio: re-implement read processing - Re-structure read processing. - Kill obsolete work queue and always push to tty in completion handler. - Use tty_insert_flip_string instead of per character push when possible. - Fix stalled-read regression in 2.6.31 by using urb status to determine when port is closed rather than port count. - Fix race with open/close by checking ASYNCB_INITIALIZED in unthrottle. - Kill private rx_flag and lock and use throttle flags in usb_serial_port instead. Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 383 +++++++++++++++--------------------------- 1 file changed, 131 insertions(+), 252 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 75c84d9b080d..9c60d6d4908a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -76,12 +76,7 @@ struct ftdi_private { unsigned long last_dtr_rts; /* saved modem control outputs */ wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ char prev_status, diff_status; /* Used for TIOCMIWAIT */ - __u8 rx_flags; /* receive state flags (throttling) */ - spinlock_t rx_lock; /* spinlock for receive state */ - struct delayed_work rx_work; struct usb_serial_port *port; - int rx_processed; - __u16 interface; /* FT2232C, FT2232H or FT4232H port interface (0 for FT232/245) */ @@ -736,10 +731,6 @@ static const char *ftdi_chip_name[] = { /* Constants for read urb and write urb */ #define BUFSZ 512 -/* rx_flags */ -#define THROTTLED 0x01 -#define ACTUALLY_THROTTLED 0x02 - /* Used for TIOCMIWAIT */ #define FTDI_STATUS_B0_MASK (FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD) #define FTDI_STATUS_B1_MASK (FTDI_RS_BI) @@ -762,7 +753,7 @@ static int ftdi_write_room(struct tty_struct *tty); static int ftdi_chars_in_buffer(struct tty_struct *tty); static void ftdi_write_bulk_callback(struct urb *urb); static void ftdi_read_bulk_callback(struct urb *urb); -static void ftdi_process_read(struct work_struct *work); +static void ftdi_process_read(struct usb_serial_port *port); static void ftdi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int ftdi_tiocmget(struct tty_struct *tty, struct file *file); @@ -1525,7 +1516,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) } kref_init(&priv->kref); - spin_lock_init(&priv->rx_lock); spin_lock_init(&priv->tx_lock); init_waitqueue_head(&priv->delta_msr_wait); /* This will push the characters through immediately rather @@ -1547,7 +1537,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) port->read_urb->transfer_buffer_length = BUFSZ; } - INIT_DELAYED_WORK(&priv->rx_work, ftdi_process_read); priv->port = port; /* Free port's existing write urb and transfer buffer. */ @@ -1684,6 +1673,26 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) return 0; } +static int ftdi_submit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) +{ + struct urb *urb = port->read_urb; + struct usb_serial *serial = port->serial; + int result; + + usb_fill_bulk_urb(urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), + urb->transfer_buffer, + urb->transfer_buffer_length, + ftdi_read_bulk_callback, port); + result = usb_submit_urb(urb, mem_flags); + if (result) + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); + return result; +} + static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) { /* ftdi_open */ struct usb_device *dev = port->serial->dev; @@ -1717,23 +1726,14 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) ftdi_set_termios(tty, port, tty->termios); /* Not throttled */ - spin_lock_irqsave(&priv->rx_lock, flags); - priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); - spin_unlock_irqrestore(&priv->rx_lock, flags); + spin_lock_irqsave(&port->lock, flags); + port->throttled = 0; + port->throttle_req = 0; + spin_unlock_irqrestore(&port->lock, flags); /* Start reading from the device */ - priv->rx_processed = 0; - usb_fill_bulk_urb(port->read_urb, dev, - usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ftdi_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", - __func__, result); - else + result = ftdi_submit_read_urb(port, GFP_KERNEL); + if (!result) kref_get(&priv->kref); return result; @@ -1779,10 +1779,6 @@ static void ftdi_close(struct usb_serial_port *port) dbg("%s", __func__); - - /* cancel any scheduled reading */ - cancel_delayed_work_sync(&priv->rx_work); - /* shutdown our bulk read */ usb_kill_urb(port->read_urb); kref_put(&priv->kref, ftdi_sio_priv_release); @@ -2005,236 +2001,121 @@ static int ftdi_chars_in_buffer(struct tty_struct *tty) return buffered; } -static void ftdi_read_bulk_callback(struct urb *urb) +static int ftdi_process_packet(struct tty_struct *tty, + struct usb_serial_port *port, struct ftdi_private *priv, + char *packet, int len) { - struct usb_serial_port *port = urb->context; - struct ftdi_private *priv; - int status = urb->status; - - dbg("%s - port %d", __func__, port->number); - - if (port->port.count <= 0) - return; - - if (status) { - /* This will happen at close every time so it is a dbg not an - err */ - dbg("(this is ok on close) nonzero read bulk status received: %d", status); - goto out; - } - - priv = usb_get_serial_port_data(port); - ftdi_process_read(&priv->rx_work.work); -} /* ftdi_read_bulk_callback */ - - -static void ftdi_process_read(struct work_struct *work) -{ /* ftdi_process_read */ - struct ftdi_private *priv = - container_of(work, struct ftdi_private, rx_work.work); - struct usb_serial_port *port = priv->port; - struct urb *urb; - struct tty_struct *tty; - char error_flag; - unsigned char *data; - int i; - int result; - int need_flip; - int packet_offset; - unsigned long flags; + char status; + char flag; + char *ch; dbg("%s - port %d", __func__, port->number); - if (port->port.count <= 0) - return; - - tty = tty_port_tty_get(&port->port); - if (!tty) { - dbg("%s - bad tty pointer - exiting", __func__); - return; + if (len < 2) { + dbg("malformed packet"); + return 0; } - priv = usb_get_serial_port_data(port); - if (!priv) { - dbg("%s - bad port private data pointer - exiting", __func__); - goto out; + /* Compare new line status to the old one, signal if different/ + N.B. packet may be processed more than once, but differences + are only processed once. */ + status = packet[0] & FTDI_STATUS_B0_MASK; + if (status != priv->prev_status) { + priv->diff_status |= status ^ priv->prev_status; + wake_up_interruptible(&priv->delta_msr_wait); + priv->prev_status = status; } - urb = port->read_urb; - if (!urb) { - dbg("%s - bad read_urb pointer - exiting", __func__); - goto out; + /* + * Although the device uses a bitmask and hence can have multiple + * errors on a packet - the order here sets the priority the error is + * returned to the tty layer. + */ + flag = TTY_NORMAL; + if (packet[1] & FTDI_RS_OE) { + flag = TTY_OVERRUN; + dbg("OVERRRUN error"); } - - data = urb->transfer_buffer; - - if (priv->rx_processed) { - dbg("%s - already processed: %d bytes, %d remain", __func__, - priv->rx_processed, - urb->actual_length - priv->rx_processed); - } else { - /* The first two bytes of every read packet are status */ - if (urb->actual_length > 2) - usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - else - dbg("Status only: %03oo %03oo", data[0], data[1]); + if (packet[1] & FTDI_RS_BI) { + flag = TTY_BREAK; + dbg("BREAK received"); + usb_serial_handle_break(port); + } + if (packet[1] & FTDI_RS_PE) { + flag = TTY_PARITY; + dbg("PARITY error"); + } + if (packet[1] & FTDI_RS_FE) { + flag = TTY_FRAME; + dbg("FRAMING error"); } - - /* TO DO -- check for hung up line and handle appropriately: */ - /* send hangup */ - /* See acm.c - you do a tty_hangup - eg tty_hangup(tty) */ - /* if CD is dropped and the line is not CLOCAL then we should hangup */ - - need_flip = 0; - for (packet_offset = priv->rx_processed; - packet_offset < urb->actual_length; packet_offset += priv->max_packet_size) { - int length; - - /* Compare new line status to the old one, signal if different/ - N.B. packet may be processed more than once, but differences - are only processed once. */ - char new_status = data[packet_offset + 0] & - FTDI_STATUS_B0_MASK; - if (new_status != priv->prev_status) { - priv->diff_status |= - new_status ^ priv->prev_status; - wake_up_interruptible(&priv->delta_msr_wait); - priv->prev_status = new_status; - } - - length = min_t(u32, priv->max_packet_size, urb->actual_length-packet_offset)-2; - if (length < 0) { - dev_err(&port->dev, "%s - bad packet length: %d\n", - __func__, length+2); - length = 0; - } - - if (priv->rx_flags & THROTTLED) { - dbg("%s - throttled", __func__); - break; - } - if (tty_buffer_request_room(tty, length) < length) { - /* break out & wait for throttling/unthrottling to - happen */ - dbg("%s - receive room low", __func__); - break; + len -= 2; + if (!len) + return 0; /* status only */ + ch = packet + 2; + + if (!(port->console && port->sysrq) && flag == TTY_NORMAL) + tty_insert_flip_string(tty, ch, len); + else { + for (i = 0; i < len; i++, ch++) { + if (!usb_serial_handle_sysrq_char(tty, port, *ch)) + tty_insert_flip_char(tty, *ch, flag); } + } + return len; +} - /* Handle errors and break */ - error_flag = TTY_NORMAL; - /* Although the device uses a bitmask and hence can have - multiple errors on a packet - the order here sets the - priority the error is returned to the tty layer */ +static void ftdi_process_read(struct usb_serial_port *port) +{ + struct urb *urb = port->read_urb; + struct tty_struct *tty; + struct ftdi_private *priv = usb_get_serial_port_data(port); + char *data = (char *)urb->transfer_buffer; + int i; + int len; + int count = 0; - if (data[packet_offset+1] & FTDI_RS_OE) { - error_flag = TTY_OVERRUN; - dbg("OVERRRUN error"); - } - if (data[packet_offset+1] & FTDI_RS_BI) { - error_flag = TTY_BREAK; - dbg("BREAK received"); - usb_serial_handle_break(port); - } - if (data[packet_offset+1] & FTDI_RS_PE) { - error_flag = TTY_PARITY; - dbg("PARITY error"); - } - if (data[packet_offset+1] & FTDI_RS_FE) { - error_flag = TTY_FRAME; - dbg("FRAMING error"); - } - if (length > 0) { - for (i = 2; i < length+2; i++) { - /* Note that the error flag is duplicated for - every character received since we don't know - which character it applied to */ - if (!usb_serial_handle_sysrq_char(tty, port, - data[packet_offset + i])) - tty_insert_flip_char(tty, - data[packet_offset + i], - error_flag); - } - need_flip = 1; - } + tty = tty_port_tty_get(&port->port); + if (!tty) + return; -#ifdef NOT_CORRECT_BUT_KEEPING_IT_FOR_NOW - /* if a parity error is detected you get status packets forever - until a character is sent without a parity error. - This doesn't work well since the application receives a - never ending stream of bad data - even though new data - hasn't been sent. Therefore I (bill) have taken this out. - However - this might make sense for framing errors and so on - so I am leaving the code in for now. - */ - else { - if (error_flag != TTY_NORMAL) { - dbg("error_flag is not normal"); - /* In this case it is just status - if that is - an error send a bad character */ - if (tty->flip.count >= TTY_FLIPBUF_SIZE) - tty_flip_buffer_push(tty); - tty_insert_flip_char(tty, 0xff, error_flag); - need_flip = 1; - } - } -#endif - } /* "for(packet_offset=0..." */ + for (i = 0; i < urb->actual_length; i += priv->max_packet_size) { + len = min_t(int, urb->actual_length - i, priv->max_packet_size); + count += ftdi_process_packet(tty, port, priv, &data[i], len); + } - /* Low latency */ - if (need_flip) + if (count) tty_flip_buffer_push(tty); + tty_kref_put(tty); +} - if (packet_offset < urb->actual_length) { - /* not completely processed - record progress */ - priv->rx_processed = packet_offset; - dbg("%s - incomplete, %d bytes processed, %d remain", - __func__, packet_offset, - urb->actual_length - packet_offset); - /* check if we were throttled while processing */ - spin_lock_irqsave(&priv->rx_lock, flags); - if (priv->rx_flags & THROTTLED) { - priv->rx_flags |= ACTUALLY_THROTTLED; - spin_unlock_irqrestore(&priv->rx_lock, flags); - dbg("%s - deferring remainder until unthrottled", - __func__); - goto out; - } - spin_unlock_irqrestore(&priv->rx_lock, flags); - /* if the port is closed stop trying to read */ - if (port->port.count > 0) - /* delay processing of remainder */ - schedule_delayed_work(&priv->rx_work, 1); - else - dbg("%s - port is closed", __func__); - goto out; - } - - /* urb is completely processed */ - priv->rx_processed = 0; +static void ftdi_read_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + unsigned long flags; - /* if the port is closed stop trying to read */ - if (port->port.count > 0) { - /* Continue trying to always read */ - usb_fill_bulk_urb(port->read_urb, port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ftdi_read_bulk_callback, port); + dbg("%s - port %d", __func__, port->number); - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); + if (urb->status) { + dbg("%s - nonzero read bulk status received: %d", + __func__, urb->status); + return; } -out: - tty_kref_put(tty); -} /* ftdi_process_read */ + usb_serial_debug_data(debug, &port->dev, __func__, + urb->actual_length, urb->transfer_buffer); + ftdi_process_read(port); + + spin_lock_irqsave(&port->lock, flags); + port->throttled = port->throttle_req; + if (!port->throttled) { + spin_unlock_irqrestore(&port->lock, flags); + ftdi_submit_read_urb(port, GFP_ATOMIC); + } else + spin_unlock_irqrestore(&port->lock, flags); +} static void ftdi_break_ctl(struct tty_struct *tty, int break_state) { @@ -2566,33 +2447,31 @@ static int ftdi_ioctl(struct tty_struct *tty, struct file *file, static void ftdi_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct ftdi_private *priv = usb_get_serial_port_data(port); unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->rx_lock, flags); - priv->rx_flags |= THROTTLED; - spin_unlock_irqrestore(&priv->rx_lock, flags); + spin_lock_irqsave(&port->lock, flags); + port->throttle_req = 1; + spin_unlock_irqrestore(&port->lock, flags); } - -static void ftdi_unthrottle(struct tty_struct *tty) +void ftdi_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - struct ftdi_private *priv = usb_get_serial_port_data(port); - int actually_throttled; + int was_throttled; unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->rx_lock, flags); - actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; - priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); - spin_unlock_irqrestore(&priv->rx_lock, flags); + spin_lock_irqsave(&port->lock, flags); + was_throttled = port->throttled; + port->throttled = port->throttle_req = 0; + spin_unlock_irqrestore(&port->lock, flags); - if (actually_throttled) - schedule_delayed_work(&priv->rx_work, 0); + /* Resubmit urb if throttled and open. */ + if (was_throttled && test_bit(ASYNCB_INITIALIZED, &port->port.flags)) + ftdi_submit_read_urb(port, GFP_KERNEL); } static int __init ftdi_init(void) -- cgit v1.2.3 From 9388e2e71a51fab0aa2309bbb45e8a23d89a95a9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 8 Oct 2009 11:36:46 +0200 Subject: USB: pl2303: fix error characters not being reported to ldisc Fix regression introduced by commit d4fc4a7bfc2dee626f4fec1e209e58eaa4312de6 (tty: Fix the PL2303 private methods for sysrq). Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1128e01525b1..9ec1a49e2362 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -1046,13 +1046,15 @@ static void pl2303_push_data(struct tty_struct *tty, /* overrun is special, not associated with a char */ if (line_status & UART_OVERRUN_ERROR) tty_insert_flip_char(tty, 0, TTY_OVERRUN); - if (port->console && port->sysrq) { + + if (tty_flag == TTY_NORMAL && !(port->console && port->sysrq)) + tty_insert_flip_string(tty, data, urb->actual_length); + else { int i; for (i = 0; i < urb->actual_length; ++i) if (!usb_serial_handle_sysrq_char(tty, port, data[i])) tty_insert_flip_char(tty, data[i], tty_flag); - } else - tty_insert_flip_string(tty, data, urb->actual_length); + } tty_flip_buffer_push(tty); } -- cgit v1.2.3 From ba6b702f85a61561d329c4c11d3ed95604924f9a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 29 Sep 2009 12:39:23 +0200 Subject: USB: digi_acceleport: Fix broken unthrottle. This patch fixes a regression introduced in 39892da44b21b5362eb848ca424d73a25ccc488f. Signed-off-by: Johan Hovold Acked-by: Oliver Neukum Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/digi_acceleport.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index ab3dd991586b..68e80be6b9e1 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -898,16 +898,16 @@ static void digi_rx_unthrottle(struct tty_struct *tty) spin_lock_irqsave(&priv->dp_port_lock, flags); - /* turn throttle off */ - priv->dp_throttled = 0; - priv->dp_throttle_restart = 0; - /* restart read chain */ if (priv->dp_throttle_restart) { port->read_urb->dev = port->serial->dev; ret = usb_submit_urb(port->read_urb, GFP_ATOMIC); } + /* turn throttle off */ + priv->dp_throttled = 0; + priv->dp_throttle_restart = 0; + spin_unlock_irqrestore(&priv->dp_port_lock, flags); if (ret) -- cgit v1.2.3 From a4720c650b68a5fe7faed2edeb0ad12645f7ae63 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 9 Oct 2009 12:43:12 -0400 Subject: USB: serial: don't call release without attach This patch (as1295) fixes a recently-added bug in the USB serial core. If certain kinds of errors occur during probing, the core may call a serial driver's release method without previously calling the attach method. This causes some drivers (io_ti in particular) to perform an invalid memory access. The patch adds a new flag to keep track of whether or not attach has been called. Signed-off-by: Alan Stern Tested-by: Jean-Denis Girard CC: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 6 +++++- include/linux/usb/serial.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index aa6b2ae951ae..2d0f75d63ff0 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -156,7 +156,8 @@ static void destroy_serial(struct kref *kref) if (serial->minor != SERIAL_TTY_NO_MINOR) return_serial(serial); - serial->type->release(serial); + if (serial->attached) + serial->type->release(serial); /* Now that nothing is using the ports, they can be freed */ for (i = 0; i < serial->num_port_pointers; ++i) { @@ -1059,12 +1060,15 @@ int usb_serial_probe(struct usb_interface *interface, module_put(type->driver.owner); if (retval < 0) goto probe_error; + serial->attached = 1; if (retval > 0) { /* quietly accept this device, but don't bind to a serial port as it's about to disappear */ serial->num_ports = 0; goto exit; } + } else { + serial->attached = 1; } if (get_free_serial(serial, num_ports, &minor) == NULL) { diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index c17eb64d7213..ce911ebf91e8 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -150,6 +150,7 @@ struct usb_serial { struct usb_interface *interface; unsigned char disconnected:1; unsigned char suspending:1; + unsigned char attached:1; unsigned char minor; unsigned char num_ports; unsigned char num_port_pointers; -- cgit v1.2.3 From 0f0ba794dcd14c9154afdf36df6a7e18cceec121 Mon Sep 17 00:00:00 2001 From: Elina Pasheva Date: Thu, 17 Sep 2009 15:26:20 -0700 Subject: USB: serial: sierra driver version change to 1.3.8 Updated sierra driver version from 1.3.7 to 1.3.8 now that the autosuspend capabilities were added to the driver. Signed-off-by: Elina Pasheva Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/sierra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 8c075b2416bb..45883988a005 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -17,7 +17,7 @@ Whom based his on the Keyspan driver by Hugh Blemings */ -#define DRIVER_VERSION "v.1.3.7" +#define DRIVER_VERSION "v.1.3.8" #define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer" #define DRIVER_DESC "USB Driver for Sierra Wireless USB modems" -- cgit v1.2.3 From 696a4ace98b6b9c05c642a6840be98d6fde57655 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 23 Sep 2009 16:09:56 +0200 Subject: USB: usblcd, fix memory leak Stanse found a memory leak in lcd_probe. Instead of returning without releasing the memory, jump to the error label which frees it. http://stanse.fi.muni.cz/ Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usblcd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 29092b8e59ce..4fb120357c55 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -313,7 +313,8 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id if (le16_to_cpu(dev->udev->descriptor.idProduct) != 0x0001) { dev_warn(&interface->dev, "USBLCD model not supported.\n"); - return -ENODEV; + retval = -ENODEV; + goto error; } /* set up the endpoint information */ -- cgit v1.2.3 From 75f47214f90e996eb184eb6e6b0e8b817999c8f7 Mon Sep 17 00:00:00 2001 From: Peter Magdina Date: Wed, 7 Oct 2009 16:22:17 +0200 Subject: USB: option: Toshiba G450 device id Signed-off-by: Peter Magdina Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index f66e39883218..32702480b526 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -318,6 +318,7 @@ static int option_resume(struct usb_serial *serial); /* TOSHIBA PRODUCTS */ #define TOSHIBA_VENDOR_ID 0x0930 #define TOSHIBA_PRODUCT_HSDPA_MINICARD 0x1302 +#define TOSHIBA_PRODUCT_G450 0x0d45 #define ALINK_VENDOR_ID 0x1e0e #define ALINK_PRODUCT_3GU 0x9200 @@ -581,6 +582,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4523) }, { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4515) }, { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4519) }, + { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_G450) }, { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_HSDPA_MINICARD ) }, /* Toshiba 3G HSDPA == Novatel Expedite EU870D MiniCard */ { USB_DEVICE(ALINK_VENDOR_ID, 0x9000) }, { USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) }, -- cgit v1.2.3 From 0ee3a33a0481c8f5c9edb7a5a02f3c76496d9551 Mon Sep 17 00:00:00 2001 From: Ronnie Furuskog Date: Mon, 21 Sep 2009 21:20:55 +0200 Subject: USB: option: Patch for Huawei Mobile Broadband E270+ Modem Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 32702480b526..43c227027560 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -165,6 +165,7 @@ static int option_resume(struct usb_serial *serial); #define HUAWEI_PRODUCT_E143D 0x143D #define HUAWEI_PRODUCT_E143E 0x143E #define HUAWEI_PRODUCT_E143F 0x143F +#define HUAWEI_PRODUCT_E14AC 0x14AC #define QUANTA_VENDOR_ID 0x0408 #define QUANTA_PRODUCT_Q101 0xEA02 @@ -425,6 +426,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) }, + { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC) }, { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_9508) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, /* Novatel Merlin V640/XV620 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, /* Novatel Merlin V620/S620 */ -- cgit v1.2.3 From 35f76e897d67fb62b4ec0be01fc0caaeb7f90108 Mon Sep 17 00:00:00 2001 From: Gergely Imreh Date: Tue, 15 Sep 2009 16:03:31 +0800 Subject: USB: usbtmc: fix timeout increase The current 10ms timeout is too short for some normal USBTMC device operation, increase it to a value which was tested with previously affected Tektronix oscilloscopes. Signed-off-by: Gergely Imreh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 864f0ba6a344..2473cf0c6b1d 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -39,7 +39,7 @@ #define USBTMC_SIZE_IOBUFFER 2048 /* Default USB timeout (in milliseconds) */ -#define USBTMC_TIMEOUT 10 +#define USBTMC_TIMEOUT 5000 /* * Maximum number of read cycles to empty bulk in endpoint during CLEAR and -- cgit v1.2.3 From e4ab05df573834b8c70d19db426b7d6286782c1d Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 16 Sep 2009 16:42:30 -0700 Subject: USB: xhci: Stop debugging polling loop when HC dies. If the host controller card is removed from the system, stop the timer function to debug the xHCI rings. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hcd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 99911e727e0b..8719a3f6851d 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -335,6 +335,12 @@ void xhci_event_ring_work(unsigned long arg) spin_lock_irqsave(&xhci->lock, flags); temp = xhci_readl(xhci, &xhci->op_regs->status); xhci_dbg(xhci, "op reg status = 0x%x\n", temp); + if (temp == 0xffffffff) { + xhci_dbg(xhci, "HW died, polling stopped.\n"); + spin_unlock_irqrestore(&xhci->lock, flags); + return; + } + temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); xhci_dbg(xhci, "ir_set 0 pending = 0x%x\n", temp); xhci_dbg(xhci, "No-op commands handled = %d\n", xhci->noops_handled); -- cgit v1.2.3 From e34b2fbf28741310d1d59a217d34e050ce7867e8 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 28 Sep 2009 17:21:37 -0700 Subject: USB: xhci: Handle canceled URBs when HC dies. When the host controller dies (e.g. it is removed from a PCI card slot), the xHCI driver cannot expect commands to complete. The buggy code this patch fixes would mark an URB as canceled and then expect the URB to be completed when the stop endpoint command completed. That would never happen if the host controller was dead, so the USB core would just hang in the disconnect code. If the host controller died, and the driver asks to cancel an URB, free any structures associated with that URB and immediately give it back. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hcd.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 8719a3f6851d..592e742c5f35 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -782,6 +782,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { unsigned long flags; int ret; + u32 temp; struct xhci_hcd *xhci; struct xhci_td *td; unsigned int ep_index; @@ -794,6 +795,17 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ret = usb_hcd_check_unlink_urb(hcd, urb, status); if (ret || !urb->hcpriv) goto done; + temp = xhci_readl(xhci, &xhci->op_regs->status); + if (temp == 0xffffffff) { + xhci_dbg(xhci, "HW died, freeing TD.\n"); + td = (struct xhci_td *) urb->hcpriv; + + usb_hcd_unlink_urb_from_ep(hcd, urb); + spin_unlock_irqrestore(&xhci->lock, flags); + usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, -ESHUTDOWN); + kfree(td); + return ret; + } xhci_dbg(xhci, "Cancel URB %p\n", urb); xhci_dbg(xhci, "Event ring:\n"); -- cgit v1.2.3 From c526d0d4fc9707816b407d2d3336267d3271db2b Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 16 Sep 2009 16:42:39 -0700 Subject: USB: xhci: Don't wait for a disable slot cmd when HC dies. When the host controller dies or is removed while a device is plugged in, the USB core will attempt to deallocate the struct usb_device. That will call into xhci_free_dev(). This function used to attempt to submit a disable slot command to the host controller and clean up the device structures when that command returned. Change xhci_free_dev() to skip the command submission and just free the memory if the host controller died. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hcd.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 592e742c5f35..d61c49f90f4a 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -1428,11 +1428,20 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); unsigned long flags; + u32 state; if (udev->slot_id == 0) return; spin_lock_irqsave(&xhci->lock, flags); + /* Don't disable the slot if the host controller is dead. */ + state = xhci_readl(xhci, &xhci->op_regs->status); + if (state == 0xffffffff) { + xhci_free_virt_device(xhci, udev->slot_id); + spin_unlock_irqrestore(&xhci->lock, flags); + return; + } + if (xhci_queue_slot_control(xhci, TRB_DISABLE_SLOT, udev->slot_id)) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_dbg(xhci, "FIXME: allocate a command ring segment\n"); -- cgit v1.2.3 From 0a023c6cf10c63d2ce68a2816d90c2f0f1ad2763 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 18 Sep 2009 08:55:12 -0700 Subject: USB: xhci: Fix dropping endpoints from the xHC schedule. When an endpoint is to be dropped from the hardware bandwidth schedule, we want to clear its add flag. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index d61c49f90f4a..932f99938481 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -895,7 +895,7 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, ctrl_ctx->drop_flags |= drop_flag; new_drop_flags = ctrl_ctx->drop_flags; - ctrl_ctx->add_flags = ~drop_flag; + ctrl_ctx->add_flags &= ~drop_flag; new_add_flags = ctrl_ctx->add_flags; last_ctx = xhci_last_valid_endpoint(ctrl_ctx->add_flags); -- cgit v1.2.3 From b20cf90650badaa5e6ec1bdbe61a63528818e8ce Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 16 Sep 2009 21:10:53 -0400 Subject: USB: musb: make HAVE_CLK support optional The Blackfin port doesn't support HAVE_CLK and the musb driver works fine with support stubbed out, so take the existing Blackfin clk stubs and move them to common musb code so we can drop the Kconfig dependency. Signed-off-by: Bryan Wu Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 2 +- drivers/usb/musb/blackfin.c | 1 - drivers/usb/musb/musb_core.h | 7 +++++++ drivers/usb/musb/musb_regs.h | 9 --------- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 803adcb5ac1d..760e7271d17b 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -8,7 +8,7 @@ comment "Enable Host or Gadget support to see Inventra options" # (M)HDRC = (Multipoint) Highspeed Dual-Role Controller config USB_MUSB_HDRC - depends on (USB || USB_GADGET) && HAVE_CLK + depends on (USB || USB_GADGET) depends on !SUPERH select NOP_USB_XCEIV if ARCH_DAVINCI select TWL4030_USB if MACH_OMAP_3430SDP diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index f2f66ebc7362..fcec87ea709e 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 381d648a36b8..6aa5f22e5274 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -95,6 +95,13 @@ struct musb_ep; #endif #endif /* need MUSB gadget selection */ +#ifndef CONFIG_HAVE_CLK +/* Dummy stub for clk framework */ +#define clk_get(dev, id) NULL +#define clk_put(clock) do {} while (0) +#define clk_enable(clock) do {} while (0) +#define clk_disable(clock) do {} while (0) +#endif #ifdef CONFIG_PROC_FS #include diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index fbfd3fd9ce1f..cc1d71b57d3c 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -439,15 +439,6 @@ static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum, /* Not implemented - HW has seperate Tx/Rx FIFO */ #define MUSB_TXCSR_MODE 0x0000 -/* - * Dummy stub for clk framework, it will be removed - * until Blackfin supports clk framework - */ -#define clk_get(dev, id) NULL -#define clk_put(clock) do {} while (0) -#define clk_enable(clock) do {} while (0) -#define clk_disable(clock) do {} while (0) - static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) { } -- cgit v1.2.3 From 2f13612a86dd785d581e29aed4db2fb98ba507ca Mon Sep 17 00:00:00 2001 From: Sergey Pinaev Date: Thu, 17 Sep 2009 17:26:50 +0400 Subject: USB: storage: iRiver P7 UNUSUAL_DEV patch Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 079ae0f7bec1..d4f034ebaa8a 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1823,6 +1823,13 @@ UNUSUAL_DEV( 0x4102, 0x1020, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), +/* Reported by Sergey Pinaev */ +UNUSUAL_DEV( 0x4102, 0x1059, 0x0000, 0x0000, + "iRiver", + "P7K", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + /* * David Härdeman * The key makes the SCSI stack print confusing (but harmless) messages -- cgit v1.2.3 From d86a83f4acbded4095a632e861183d117ec7405a Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 18 Sep 2009 09:14:46 +0200 Subject: USB: gadget: imx_udc: Use resource size Use the resource_size function instead of manually calculating the resource size. This reduces the chance of introducing off-by-one errors. Signed-off-by: Tobias Klauser Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/imx_udc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index c52a681f376c..01ee0b9bc957 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1402,7 +1402,8 @@ static int __init imx_udc_probe(struct platform_device *pdev) struct clk *clk; void __iomem *base; int ret = 0; - int i, res_size; + int i; + resource_size_t res_size; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -1416,7 +1417,7 @@ static int __init imx_udc_probe(struct platform_device *pdev) return -ENODEV; } - res_size = res->end - res->start + 1; + res_size = resource_size(res); if (!request_mem_region(res->start, res_size, res->name)) { dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", res_size, res->start); @@ -1527,8 +1528,7 @@ static int __exit imx_udc_remove(struct platform_device *pdev) clk_disable(imx_usb->clk); iounmap(imx_usb->base); - release_mem_region(imx_usb->res->start, - imx_usb->res->end - imx_usb->res->start + 1); + release_mem_region(imx_usb->res->start, resource_size(imx_usb->res)); if (pdata->exit) pdata->exit(&pdev->dev); -- cgit v1.2.3 From b0a9cf297e587219332ee4acca243627702c2cc9 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 7 Oct 2009 04:29:31 -0400 Subject: USB: isp1362: fix build warnings on 64-bit systems A bunch of places assumed pointers were 32-bits in size (bit checking and debug output), but none of these affected runtime functionality. Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362-hcd.c | 18 +++++++++--------- drivers/usb/host/isp1362.h | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index e35d82808bab..5c774ab98252 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2284,10 +2284,10 @@ static int isp1362_mem_config(struct usb_hcd *hcd) dev_info(hcd->self.controller, "ISP1362 Memory usage:\n"); dev_info(hcd->self.controller, " ISTL: 2 * %4d: %4d @ $%04x:$%04x\n", istl_size / 2, istl_size, 0, istl_size / 2); - dev_info(hcd->self.controller, " INTL: %4d * (%3u+8): %4d @ $%04x\n", + dev_info(hcd->self.controller, " INTL: %4d * (%3lu+8): %4d @ $%04x\n", ISP1362_INTL_BUFFERS, intl_blksize - PTD_HEADER_SIZE, intl_size, istl_size); - dev_info(hcd->self.controller, " ATL : %4d * (%3u+8): %4d @ $%04x\n", + dev_info(hcd->self.controller, " ATL : %4d * (%3lu+8): %4d @ $%04x\n", atl_buffers, atl_blksize - PTD_HEADER_SIZE, atl_size, istl_size + intl_size); dev_info(hcd->self.controller, " USED/FREE: %4d %4d\n", total, @@ -2677,12 +2677,12 @@ static int __devexit isp1362_remove(struct platform_device *pdev) DBG(0, "%s: Removing HCD\n", __func__); usb_remove_hcd(hcd); - DBG(0, "%s: Unmapping data_reg @ %08x\n", __func__, - (u32)isp1362_hcd->data_reg); + DBG(0, "%s: Unmapping data_reg @ %p\n", __func__, + isp1362_hcd->data_reg); iounmap(isp1362_hcd->data_reg); - DBG(0, "%s: Unmapping addr_reg @ %08x\n", __func__, - (u32)isp1362_hcd->addr_reg); + DBG(0, "%s: Unmapping addr_reg @ %p\n", __func__, + isp1362_hcd->addr_reg); iounmap(isp1362_hcd->addr_reg); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); @@ -2810,16 +2810,16 @@ static int __init isp1362_probe(struct platform_device *pdev) return 0; err6: - DBG(0, "%s: Freeing dev %08x\n", __func__, (u32)isp1362_hcd); + DBG(0, "%s: Freeing dev %p\n", __func__, isp1362_hcd); usb_put_hcd(hcd); err5: - DBG(0, "%s: Unmapping data_reg @ %08x\n", __func__, (u32)data_reg); + DBG(0, "%s: Unmapping data_reg @ %p\n", __func__, data_reg); iounmap(data_reg); err4: DBG(0, "%s: Releasing mem region %08lx\n", __func__, (long unsigned int)data->start); release_mem_region(data->start, resource_len(data)); err3: - DBG(0, "%s: Unmapping addr_reg @ %08x\n", __func__, (u32)addr_reg); + DBG(0, "%s: Unmapping addr_reg @ %p\n", __func__, addr_reg); iounmap(addr_reg); err2: DBG(0, "%s: Releasing mem region %08lx\n", __func__, (long unsigned int)addr->start); diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h index fe60f62a32f3..1a253ebf7e50 100644 --- a/drivers/usb/host/isp1362.h +++ b/drivers/usb/host/isp1362.h @@ -580,7 +580,7 @@ static inline const char *ISP1362_INT_NAME(int n) static inline void ALIGNSTAT(struct isp1362_hcd *isp1362_hcd, void *ptr) { - unsigned p = (unsigned)ptr; + unsigned long p = (unsigned long)ptr; if (!(p & 0xf)) isp1362_hcd->stat16++; else if (!(p & 0x7)) @@ -770,7 +770,7 @@ static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 l if (!len) return; - if ((unsigned)dp & 0x1) { + if ((unsigned long)dp & 0x1) { /* not aligned */ for (; len > 1; len -= 2) { data = *dp++; @@ -962,8 +962,8 @@ static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 isp1362_write_diraddr(isp1362_hcd, offset, len); - DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %08x\n", __func__, - len, offset, (u32)buf); + DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %p\n", + __func__, len, offset, buf); isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); @@ -982,8 +982,8 @@ static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 isp1362_write_diraddr(isp1362_hcd, offset, len); - DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %08x\n", __func__, - len, offset, (u32)buf); + DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %p\n", + __func__, len, offset, buf); isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT)); -- cgit v1.2.3 From 06bad89da686f6323e95cf925105e8cf88d87caf Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 5 Oct 2009 15:53:58 -0400 Subject: USB: ipaq: fix oops when device is plugged in This patch (as1293) fixes a problem with the ipaq serial driver. It tries to bind to all the interfaces, even those that don't have enough endpoints. The symptom is an invalid memory reference and oops when the device is plugged in. Signed-off-by: Alan Stern CC: stable Tested-by: Matthias Geissert Tested-by: Tilman Schmidt Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ipaq.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 24fcc64b837d..d6231c38813e 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -966,6 +966,15 @@ static int ipaq_calc_num_ports(struct usb_serial *serial) static int ipaq_startup(struct usb_serial *serial) { dbg("%s", __func__); + + /* Some of the devices in ipaq_id_table[] are composite, and we + * shouldn't bind to all the interfaces. This test will rule out + * some obviously invalid possibilities. + */ + if (serial->num_bulk_in < serial->num_ports || + serial->num_bulk_out < serial->num_ports) + return -ENODEV; + if (serial->dev->actconfig->desc.bConfigurationValue != 1) { /* * FIXME: HP iPaq rx3715, possibly others, have 1 config that -- cgit v1.2.3 From 6f88139eb9eae8003683689f93402264a73fb754 Mon Sep 17 00:00:00 2001 From: Éric Piel Date: Sun, 4 Oct 2009 13:45:07 +0200 Subject: USB: cp210x: Add support for the DW700 UART MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the Dell inspiron mini 10, the GPS is connected via a cp2102. This patch adds detection of this USB device. (I haven't managed to use the GPS under Linux yet, though) Signed-off-by: Éric Piel Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 4a208fe85bc9..698252a4dc5d 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -113,6 +113,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */ { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ + { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ { } /* Terminating Entry */ }; -- cgit v1.2.3 From 63a9609513007537a0b23ac511fd73f9bd609ea0 Mon Sep 17 00:00:00 2001 From: Joris van Rantwijk Date: Thu, 24 Sep 2009 20:20:20 +0200 Subject: USB: Fix throttling in generic usbserial driver The generic usbserial driver in Linux 2.6.31 halts its receiving channel in response to throttle requests from the line discipline. Unfortunately it drops the contents of the first URB received after throttling takes effect. This patch corrects that problem. Signed-off-by: Joris van Rantwijk Acked-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index deba08c7a015..bbe005cefcfb 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -546,7 +546,7 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) if (was_throttled) { /* Resume reading from device */ - usb_serial_generic_resubmit_read_urb(port, GFP_KERNEL); + flush_and_resubmit_read_urb(port); } } -- cgit v1.2.3 From 4c9fde9b860ccb27a7b026844eb3ef64c77e5a49 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 1 Oct 2009 14:54:46 +0200 Subject: USB: visor: fix trivial accounting bug in visor driver usb:usbserial:visor: fix accounting in error case data not pushed to the tty layer due to an error mustn't be counted Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/visor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 1aa5d20a5d99..8e2d6dd9add0 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -513,7 +513,8 @@ static void visor_read_bulk_callback(struct urb *urb) tty_kref_put(tty); } spin_lock(&priv->lock); - priv->bytes_in += available_room; + if (tty) + priv->bytes_in += available_room; } else { spin_lock(&priv->lock); -- cgit v1.2.3 From a5f6005d7b1821d2085d9749b56500a8f2610924 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 1 Oct 2009 15:01:17 +0200 Subject: USB: small fix in error case of suspend in generic usbserial code usb:usbserial: fix flags in error case of suspension suspended flag must be reset in error case Signed-off-by: Oliver Neukum Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 2d0f75d63ff0..bd3fa7ff15b1 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1168,8 +1168,10 @@ int usb_serial_suspend(struct usb_interface *intf, pm_message_t message) if (serial->type->suspend) { r = serial->type->suspend(serial, message); - if (r < 0) + if (r < 0) { + serial->suspending = 0; goto err_out; + } } for (i = 0; i < serial->num_ports; ++i) { -- cgit v1.2.3 From f1a0743bc0e7a30c032b1eb78f6a2b0f805b4597 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 6 Oct 2009 14:07:57 -0400 Subject: USB: storage: When a device returns no sense data, call it a Hardware Error This patch (as1294) fixes a problem that has plagued users for several kernel releases. Some USB mass-storage devices don't return any sense data when they encounter certain kinds of errors. The SCSI layer interprets this to mean that the operation should be retried, and the same thing happens -- over and over again with no limit. In some circumstances (such as when a bus reset occurs) that is the right thing to do, but not here. The patch checks for this condition (a transport failure with no sense data) and changes the result code to DID_ERROR and the sense code to Hardware Error. This does get only a limited number of retries, and so the command will fail relatively quickly instead of getting stuck in an infinite loop. This fixes a large part of Bugzilla #14118. Signed-off-by: Alan Stern Tested-by: Mantas Mikulenas CC: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/transport.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index e20dc525d177..3a4fb023af72 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -768,17 +768,32 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) /* set the result so the higher layers expect this data */ srb->result = SAM_STAT_CHECK_CONDITION; - /* If things are really okay, then let's show that. Zero - * out the sense buffer so the higher layers won't realize - * we did an unsolicited auto-sense. */ - if (result == USB_STOR_TRANSPORT_GOOD && - /* Filemark 0, ignore EOM, ILI 0, no sense */ + /* We often get empty sense data. This could indicate that + * everything worked or that there was an unspecified + * problem. We have to decide which. + */ + if ( /* Filemark 0, ignore EOM, ILI 0, no sense */ (srb->sense_buffer[2] & 0xaf) == 0 && /* No ASC or ASCQ */ srb->sense_buffer[12] == 0 && srb->sense_buffer[13] == 0) { - srb->result = SAM_STAT_GOOD; - srb->sense_buffer[0] = 0x0; + + /* If things are really okay, then let's show that. + * Zero out the sense buffer so the higher layers + * won't realize we did an unsolicited auto-sense. + */ + if (result == USB_STOR_TRANSPORT_GOOD) { + srb->result = SAM_STAT_GOOD; + srb->sense_buffer[0] = 0x0; + + /* If there was a problem, report an unspecified + * hardware error to prevent the higher layers from + * entering an infinite retry loop. + */ + } else { + srb->result = DID_ERROR << 16; + srb->sense_buffer[2] = HARDWARE_ERROR; + } } } -- cgit v1.2.3 From d55500941fe6db4d7424c744522ee2451ac1ceda Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 6 Oct 2009 13:45:59 -0700 Subject: USB: ehci: Fix isoc scheduling boundary checking. The EHCI driver does some bounds checking when it's scheduling an iTD for an active endpoint. It sets the local variable start to stream->next_uframe and moves that variable further in the schedule if necessary. However, the driver fails to do anything with start before jumping to the ready label and setting the URB's starting frame to stream->next_uframe. Alan Stern confirms the EHCI driver should set stream->next_uframe to start before jumping. Signed-off-by: Sarah Sharp Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sched.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 3ea05936851f..3efa59b18044 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1425,6 +1425,7 @@ iso_stream_schedule ( status = -EFBIG; goto fail; } + stream->next_uframe = start; goto ready; } -- cgit v1.2.3 From 88fa6590b30ef8c4d41923449ada104f915d8df8 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 7 Oct 2009 09:25:10 +0200 Subject: USB: serial: fix race between unthrottle and completion handler in opticon usb:usbserial:opticon: fix race between unthrottle and completion handler opticon_unthrottle() mustn't resubmit the URB unconditionally as the URB may still be running. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/opticon.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 1085a577c5c1..80f59b6350cb 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -314,21 +314,24 @@ static void opticon_unthrottle(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct opticon_private *priv = usb_get_serial_data(port->serial); unsigned long flags; - int result; + int result, was_throttled; dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); priv->throttled = false; + was_throttled = priv->actually_throttled; priv->actually_throttled = false; spin_unlock_irqrestore(&priv->lock, flags); priv->bulk_read_urb->dev = port->serial->dev; - result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", + if (was_throttled) { + result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", __func__, result); + } } static int opticon_tiocmget(struct tty_struct *tty, struct file *file) -- cgit v1.2.3 From b2a5cf1bdc011f5474c72543f9d8116c0f07f452 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 7 Oct 2009 09:30:49 +0200 Subject: USB: serial: fix race between unthrottle and completion handler in symbolserial usb:usbserial:symbolserial: fix race between unthrottle and completion handler symbol_unthrottle() mustn't resubmit the URB unconditionally as the URB may still be running. the same bug as opticon. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/symbolserial.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index cb7e95f9fcbf..f25e54526843 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -179,20 +179,24 @@ static void symbol_unthrottle(struct tty_struct *tty) struct symbol_private *priv = usb_get_serial_data(port->serial); unsigned long flags; int result; + bool was_throttled; dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); priv->throttled = false; + was_throttled = priv->actually_throttled; priv->actually_throttled = false; spin_unlock_irqrestore(&priv->lock, flags); priv->int_urb->dev = port->serial->dev; - result = usb_submit_urb(priv->int_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", + if (was_throttled) { + result = usb_submit_urb(priv->int_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", __func__, result); + } } static int symbol_startup(struct usb_serial *serial) -- cgit v1.2.3 From 638325154572ba2113a18669fe3b299caa2dabd9 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 7 Oct 2009 10:50:23 +0200 Subject: USB: serial: fix assumption that throttle/unthrottle cannot sleep many serial subdrivers are clearly written as if throttle/unthrottle cannot sleep. This leads to unneeded atomic submissions. This patch converts affected drivers in a way to makes very clear that throttle/unthrottle can sleep. Thus future misdesigns can be avoided and efficiency and reliability improved. This removes any such assumption using GFP_KERNEL and spin_lock_irq() Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/aircable.c | 10 ++++------ drivers/usb/serial/cypress_m8.c | 12 +++++------- drivers/usb/serial/empeg.c | 2 +- drivers/usb/serial/garmin_gps.c | 12 +++++------- drivers/usb/serial/keyspan_pda.c | 2 +- drivers/usb/serial/kl5kusb105.c | 2 +- drivers/usb/serial/mct_u232.c | 14 ++++++-------- drivers/usb/serial/symbolserial.c | 12 +++++------- drivers/usb/serial/visor.c | 12 +++++------- drivers/usb/serial/whiteheat.c | 10 ++++------ 10 files changed, 37 insertions(+), 51 deletions(-) diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 2cbfab3716e5..b10ac8409411 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -554,13 +554,12 @@ static void aircable_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->rx_lock, flags); + spin_lock_irq(&priv->rx_lock); priv->rx_flags |= THROTTLED; - spin_unlock_irqrestore(&priv->rx_lock, flags); + spin_unlock_irq(&priv->rx_lock); } /* Based on ftdi_sio.c unthrottle */ @@ -569,14 +568,13 @@ static void aircable_unthrottle(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct aircable_private *priv = usb_get_serial_port_data(port); int actually_throttled; - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->rx_lock, flags); + spin_lock_irq(&priv->rx_lock); actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); - spin_unlock_irqrestore(&priv->rx_lock, flags); + spin_unlock_irq(&priv->rx_lock); if (actually_throttled) schedule_work(&priv->rx_work); diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index e0a8b715f2f2..a591ebec0f89 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1155,13 +1155,12 @@ static void cypress_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->rx_flags = THROTTLED; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } @@ -1170,14 +1169,13 @@ static void cypress_unthrottle(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct cypress_private *priv = usb_get_serial_port_data(port); int actually_throttled, result; - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; priv->rx_flags = 0; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); if (!priv->comm_is_ok) return; @@ -1185,7 +1183,7 @@ static void cypress_unthrottle(struct tty_struct *tty) if (actually_throttled) { port->interrupt_in_urb->dev = port->serial->dev; - result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result) { dev_err(&port->dev, "%s - failed submitting read urb, " "error %d\n", __func__, result); diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 33c9e9cf9eb2..7dd0e3eadbe6 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -391,7 +391,7 @@ static void empeg_unthrottle(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 20432d345529..5ac900e5a50e 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1390,14 +1390,13 @@ static void garmin_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); /* set flag, data received will be put into a queue for later processing */ - spin_lock_irqsave(&garmin_data_p->lock, flags); + spin_lock_irq(&garmin_data_p->lock); garmin_data_p->flags |= FLAGS_QUEUING|FLAGS_THROTTLED; - spin_unlock_irqrestore(&garmin_data_p->lock, flags); + spin_unlock_irq(&garmin_data_p->lock); } @@ -1405,13 +1404,12 @@ static void garmin_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); - unsigned long flags; int status; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&garmin_data_p->lock, flags); + spin_lock_irq(&garmin_data_p->lock); garmin_data_p->flags &= ~FLAGS_THROTTLED; - spin_unlock_irqrestore(&garmin_data_p->lock, flags); + spin_unlock_irq(&garmin_data_p->lock); /* in native mode send queued data to tty, in serial mode nothing needs to be done here */ @@ -1419,7 +1417,7 @@ static void garmin_unthrottle(struct tty_struct *tty) garmin_flush_queue(garmin_data_p); if (0 != (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) { - status = usb_submit_urb(port->read_urb, GFP_ATOMIC); + status = usb_submit_urb(port->read_urb, GFP_KERNEL); if (status) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 257c16cc6b2a..1296a097f5c3 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -290,7 +290,7 @@ static void keyspan_pda_rx_unthrottle(struct tty_struct *tty) /* just restart the receive interrupt URB */ dbg("keyspan_pda_rx_unthrottle port %d", port->number); port->interrupt_in_urb->dev = port->serial->dev; - if (usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC)) + if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL)) dbg(" usb_submit_urb(read urb) failed"); return; } diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index f7373371b137..3a7873806f46 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -951,7 +951,7 @@ static void klsi_105_unthrottle(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index ad4998bbf16f..cd009cb280a5 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -777,20 +777,19 @@ static void mct_u232_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); - unsigned long flags; unsigned int control_state; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->rx_flags |= THROTTLED; if (C_CRTSCTS(tty)) { priv->control_state &= ~TIOCM_RTS; control_state = priv->control_state; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); (void) mct_u232_set_modem_ctrl(port->serial, control_state); } else { - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } } @@ -799,20 +798,19 @@ static void mct_u232_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); - unsigned long flags; unsigned int control_state; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) { priv->rx_flags &= ~THROTTLED; priv->control_state |= TIOCM_RTS; control_state = priv->control_state; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); (void) mct_u232_set_modem_ctrl(port->serial, control_state); } else { - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } } diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index f25e54526843..b282c0f2d8e5 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -165,33 +165,31 @@ static void symbol_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct symbol_private *priv = usb_get_serial_data(port->serial); - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->throttled = true; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } static void symbol_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct symbol_private *priv = usb_get_serial_data(port->serial); - unsigned long flags; int result; bool was_throttled; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->throttled = false; was_throttled = priv->actually_throttled; priv->actually_throttled = false; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); priv->int_urb->dev = port->serial->dev; if (was_throttled) { - result = usb_submit_urb(priv->int_urb, GFP_ATOMIC); + result = usb_submit_urb(priv->int_urb, GFP_KERNEL); if (result) dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 8e2d6dd9add0..b9341f0e452b 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -583,12 +583,11 @@ static void visor_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->throttled = 1; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } @@ -596,17 +595,16 @@ static void visor_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); - unsigned long flags; int result; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); priv->throttled = 0; priv->actually_throttled = 0; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 62424eec33ec..1093d2eb046a 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -949,13 +949,12 @@ static void whiteheat_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&info->lock, flags); + spin_lock_irq(&info->lock); info->flags |= THROTTLED; - spin_unlock_irqrestore(&info->lock, flags); + spin_unlock_irq(&info->lock); return; } @@ -966,14 +965,13 @@ static void whiteheat_unthrottle(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct whiteheat_private *info = usb_get_serial_port_data(port); int actually_throttled; - unsigned long flags; dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&info->lock, flags); + spin_lock_irq(&info->lock); actually_throttled = info->flags & ACTUALLY_THROTTLED; info->flags &= ~(THROTTLED | ACTUALLY_THROTTLED); - spin_unlock_irqrestore(&info->lock, flags); + spin_unlock_irq(&info->lock); if (actually_throttled) rx_data_softint(&info->rx_work); -- cgit v1.2.3 From a1c33952b729eba4cedb8dbe54765921f2b3062f Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 7 Oct 2009 11:01:38 +0200 Subject: USB: serial: fix race between unthrottle and completion handler in visor usb:usbserial:visor: fix race between unthrottle and completion handler visor_unthrottle() mustn't resubmit the URB unconditionally as the URB may still be running. the same bug as opticon. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/visor.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index b9341f0e452b..ad1f9232292d 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -595,20 +595,23 @@ static void visor_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct visor_private *priv = usb_get_serial_port_data(port); - int result; + int result, was_throttled; dbg("%s - port %d", __func__, port->number); spin_lock_irq(&priv->lock); priv->throttled = 0; + was_throttled = priv->actually_throttled; priv->actually_throttled = 0; spin_unlock_irq(&priv->lock); - port->read_urb->dev = port->serial->dev; - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, - "%s - failed submitting read urb, error %d\n", + if (was_throttled) { + port->read_urb->dev = port->serial->dev; + result = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (result) + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", __func__, result); + } } static int palm_os_3_probe(struct usb_serial *serial, -- cgit v1.2.3 From 40d28582316d8dcb535c359a14b71cb910ad6e73 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 7 Oct 2009 18:07:10 +0200 Subject: USB: serial: no unnecessary GFP_ATOMIC in oti6858 GFP_ATOMIC without good cause is evil. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/oti6858.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 0f4a70ce3823..c644e26394b4 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -288,7 +288,7 @@ static void setup_line(struct work_struct *work) dbg("%s(): submitting interrupt urb", __func__); port->interrupt_in_urb->dev = port->serial->dev; - result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __func__, result); @@ -335,7 +335,7 @@ void send_data(struct work_struct *work) dbg("%s(): submitting interrupt urb", __func__); port->interrupt_in_urb->dev = port->serial->dev; - result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); + result = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __func__, result); @@ -349,7 +349,7 @@ void send_data(struct work_struct *work) port->write_urb->transfer_buffer_length = count; port->write_urb->dev = port->serial->dev; - result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + result = usb_submit_urb(port->write_urb, GFP_NOIO); if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __func__, result); -- cgit v1.2.3 From 03d38418a5a3361a757336cd87d64b35c1c7d658 Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Wed, 7 Oct 2009 23:43:26 +0100 Subject: ARM: 5753/1: ep93xx: remove old EP93XX_GPIO_* defines Most of the EP93XX_GPIO_*_INT_* register defines in ep93xx-regs.h not required due to how the ep93xx core and gpiolib support handle gpio interrupts. Remove the defines to prevent future confusion. Signed-off-by: H Hartley Sweeten Acked-by: Ryan Mallon Signed-off-by: Russell King --- arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h index b07338c0a01f..aae72b8ab236 100644 --- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h @@ -114,20 +114,8 @@ #define EP93XX_GPIO_BASE EP93XX_APB_IOMEM(0x00040000) #define EP93XX_GPIO_REG(x) (EP93XX_GPIO_BASE + (x)) -#define EP93XX_GPIO_F_INT_TYPE1 EP93XX_GPIO_REG(0x4c) -#define EP93XX_GPIO_F_INT_TYPE2 EP93XX_GPIO_REG(0x50) -#define EP93XX_GPIO_F_INT_ACK EP93XX_GPIO_REG(0x54) -#define EP93XX_GPIO_F_INT_ENABLE EP93XX_GPIO_REG(0x58) #define EP93XX_GPIO_F_INT_STATUS EP93XX_GPIO_REG(0x5c) -#define EP93XX_GPIO_A_INT_TYPE1 EP93XX_GPIO_REG(0x90) -#define EP93XX_GPIO_A_INT_TYPE2 EP93XX_GPIO_REG(0x94) -#define EP93XX_GPIO_A_INT_ACK EP93XX_GPIO_REG(0x98) -#define EP93XX_GPIO_A_INT_ENABLE EP93XX_GPIO_REG(0x9c) #define EP93XX_GPIO_A_INT_STATUS EP93XX_GPIO_REG(0xa0) -#define EP93XX_GPIO_B_INT_TYPE1 EP93XX_GPIO_REG(0xac) -#define EP93XX_GPIO_B_INT_TYPE2 EP93XX_GPIO_REG(0xb0) -#define EP93XX_GPIO_B_INT_ACK EP93XX_GPIO_REG(0xb4) -#define EP93XX_GPIO_B_INT_ENABLE EP93XX_GPIO_REG(0xb8) #define EP93XX_GPIO_B_INT_STATUS EP93XX_GPIO_REG(0xbc) #define EP93XX_AAC_BASE EP93XX_APB_IOMEM(0x00080000) -- cgit v1.2.3 From 6531a991f95f2f34fc00cf0a030b1cdd5e19dc02 Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Thu, 8 Oct 2009 00:45:00 +0100 Subject: ARM: 5754/1: ep93xx: update i2c support Update the ep93xx i2c support: 1) The platform init code passes the configuration data for the i2c-gpio driver. This allows any gpio pin do be used for the sda and scl pins. It also allows the platform to specify the udelay and timeout. 2) Program the gpio configuration register to enable/disable the open drain drivers. Note that this really only works if the sda and scl pins are set to EP93XX_GPIO_LINE_EEDAT and EP93XX_GPIO_LINE_EECLK. 3) Update the edb93xx.c platform init to use the new support. Signed-off-by: H Hartley Sweeten Acked-by: Ryan Mallon Signed-off-by: Russell King --- arch/arm/mach-ep93xx/core.c | 31 ++++++++++++++++++------- arch/arm/mach-ep93xx/edb93xx.c | 31 +++++++++++++++++++------ arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 1 + arch/arm/mach-ep93xx/include/mach/platform.h | 4 +++- 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index f7ebed942f66..f95dc160c34b 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -550,13 +550,11 @@ void __init ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr) platform_device_register(&ep93xx_eth_device); } -static struct i2c_gpio_platform_data ep93xx_i2c_data = { - .sda_pin = EP93XX_GPIO_LINE_EEDAT, - .sda_is_open_drain = 0, - .scl_pin = EP93XX_GPIO_LINE_EECLK, - .scl_is_open_drain = 0, - .udelay = 2, -}; + +/************************************************************************* + * EP93xx i2c peripheral handling + *************************************************************************/ +static struct i2c_gpio_platform_data ep93xx_i2c_data; static struct platform_device ep93xx_i2c_device = { .name = "i2c-gpio", @@ -564,8 +562,25 @@ static struct platform_device ep93xx_i2c_device = { .dev.platform_data = &ep93xx_i2c_data, }; -void __init ep93xx_register_i2c(struct i2c_board_info *devices, int num) +void __init ep93xx_register_i2c(struct i2c_gpio_platform_data *data, + struct i2c_board_info *devices, int num) { + /* + * Set the EEPROM interface pin drive type control. + * Defines the driver type for the EECLK and EEDAT pins as either + * open drain, which will require an external pull-up, or a normal + * CMOS driver. + */ + if (data->sda_is_open_drain && data->sda_pin != EP93XX_GPIO_LINE_EEDAT) + pr_warning("ep93xx: sda != EEDAT, open drain has no effect\n"); + if (data->scl_is_open_drain && data->scl_pin != EP93XX_GPIO_LINE_EECLK) + pr_warning("ep93xx: scl != EECLK, open drain has no effect\n"); + + __raw_writel((data->sda_is_open_drain << 1) | + (data->scl_is_open_drain << 0), + EP93XX_GPIO_EEDRIVE); + + ep93xx_i2c_data = *data; i2c_register_board_info(0, devices, num); platform_device_register(&ep93xx_i2c_device); } diff --git a/arch/arm/mach-ep93xx/edb93xx.c b/arch/arm/mach-ep93xx/edb93xx.c index 73145ae5d3fa..ca71cf1a72a0 100644 --- a/arch/arm/mach-ep93xx/edb93xx.c +++ b/arch/arm/mach-ep93xx/edb93xx.c @@ -27,8 +27,10 @@ #include #include #include -#include #include +#include +#include +#include #include @@ -76,13 +78,26 @@ static struct ep93xx_eth_data edb93xx_eth_data = { .phy_id = 1, }; -static struct i2c_board_info __initdata edb93xxa_i2c_data[] = { + +/************************************************************************* + * EDB93xx i2c peripheral handling + *************************************************************************/ +static struct i2c_gpio_platform_data edb93xx_i2c_gpio_data = { + .sda_pin = EP93XX_GPIO_LINE_EEDAT, + .sda_is_open_drain = 0, + .scl_pin = EP93XX_GPIO_LINE_EECLK, + .scl_is_open_drain = 0, + .udelay = 0, /* default to 100 kHz */ + .timeout = 0, /* default to 100 ms */ +}; + +static struct i2c_board_info __initdata edb93xxa_i2c_board_info[] = { { I2C_BOARD_INFO("isl1208", 0x6f), }, }; -static struct i2c_board_info __initdata edb93xx_i2c_data[] = { +static struct i2c_board_info __initdata edb93xx_i2c_board_info[] = { { I2C_BOARD_INFO("ds1337", 0x68), }, @@ -92,12 +107,14 @@ static void __init edb93xx_register_i2c(void) { if (machine_is_edb9302a() || machine_is_edb9307a() || machine_is_edb9315a()) { - ep93xx_register_i2c(edb93xxa_i2c_data, - ARRAY_SIZE(edb93xxa_i2c_data)); + ep93xx_register_i2c(&edb93xx_i2c_gpio_data, + edb93xxa_i2c_board_info, + ARRAY_SIZE(edb93xxa_i2c_board_info)); } else if (machine_is_edb9307() || machine_is_edb9312() || machine_is_edb9315()) { - ep93xx_register_i2c(edb93xx_i2c_data, - ARRAY_SIZE(edb93xx_i2c_data)); + ep93xx_register_i2c(&edb93xx_i2c_gpio_data + edb93xx_i2c_board_info, + ARRAY_SIZE(edb93xx_i2c_board_info)); } } diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h index aae72b8ab236..b1f937eda29c 100644 --- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h @@ -117,6 +117,7 @@ #define EP93XX_GPIO_F_INT_STATUS EP93XX_GPIO_REG(0x5c) #define EP93XX_GPIO_A_INT_STATUS EP93XX_GPIO_REG(0xa0) #define EP93XX_GPIO_B_INT_STATUS EP93XX_GPIO_REG(0xbc) +#define EP93XX_GPIO_EEDRIVE EP93XX_GPIO_REG(0xc8) #define EP93XX_AAC_BASE EP93XX_APB_IOMEM(0x00080000) diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h index 01a0f0838e5b..a3ec33fd79d4 100644 --- a/arch/arm/mach-ep93xx/include/mach/platform.h +++ b/arch/arm/mach-ep93xx/include/mach/platform.h @@ -4,6 +4,7 @@ #ifndef __ASSEMBLY__ +struct i2c_gpio_platform_data; struct i2c_board_info; struct platform_device; struct ep93xxfb_mach_info; @@ -33,7 +34,8 @@ static inline void ep93xx_devcfg_clear_bits(unsigned int bits) } void ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr); -void ep93xx_register_i2c(struct i2c_board_info *devices, int num); +void ep93xx_register_i2c(struct i2c_gpio_platform_data *data, + struct i2c_board_info *devices, int num); void ep93xx_register_fb(struct ep93xxfb_mach_info *data); void ep93xx_register_pwm(int pwm0, int pwm1); int ep93xx_pwm_acquire_gpio(struct platform_device *pdev); -- cgit v1.2.3 From ebd00c08e28a0ab4dcb715d222214625fff6d62a Mon Sep 17 00:00:00 2001 From: Hartley Sweeten Date: Thu, 8 Oct 2009 23:44:41 +0100 Subject: ARM: 5756/1: ep93xx: introduce clk parent The clock generation system in the ep93xx uses two external oscillator's and two internal PLLs to derive all the internal clocks. Many of these internal clocks can be stopped to save power. This introduces a "parent" hierarchy for the clocks so that the users count can be correctly tracked for power management. The "parent" for the video clock can either be one of the PLL outputs or the external oscillator. In order to correctly track the "parent" for the video clock calc_clk_div() needed to be modified. It now returns an error code if the desired rate cannot be generated. Signed-off-by: H Hartley Sweeten Acked-by: Ryan Mallon Signed-off-by: Russell King --- arch/arm/mach-ep93xx/clock.c | 166 +++++++++++++++++++++++++++++++------------ 1 file changed, 119 insertions(+), 47 deletions(-) diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c index dda19cd76194..1d0f9d8aff2e 100644 --- a/arch/arm/mach-ep93xx/clock.c +++ b/arch/arm/mach-ep93xx/clock.c @@ -16,13 +16,16 @@ #include #include #include +#include + +#include #include #include -#include struct clk { + struct clk *parent; unsigned long rate; int users; int sw_locked; @@ -39,40 +42,60 @@ static unsigned long get_uart_rate(struct clk *clk); static int set_keytchclk_rate(struct clk *clk, unsigned long rate); static int set_div_rate(struct clk *clk, unsigned long rate); + +static struct clk clk_xtali = { + .rate = EP93XX_EXT_CLK_RATE, +}; static struct clk clk_uart1 = { + .parent = &clk_xtali, .sw_locked = 1, .enable_reg = EP93XX_SYSCON_DEVCFG, .enable_mask = EP93XX_SYSCON_DEVCFG_U1EN, .get_rate = get_uart_rate, }; static struct clk clk_uart2 = { + .parent = &clk_xtali, .sw_locked = 1, .enable_reg = EP93XX_SYSCON_DEVCFG, .enable_mask = EP93XX_SYSCON_DEVCFG_U2EN, .get_rate = get_uart_rate, }; static struct clk clk_uart3 = { + .parent = &clk_xtali, .sw_locked = 1, .enable_reg = EP93XX_SYSCON_DEVCFG, .enable_mask = EP93XX_SYSCON_DEVCFG_U3EN, .get_rate = get_uart_rate, }; -static struct clk clk_pll1; -static struct clk clk_f; -static struct clk clk_h; -static struct clk clk_p; -static struct clk clk_pll2; +static struct clk clk_pll1 = { + .parent = &clk_xtali, +}; +static struct clk clk_f = { + .parent = &clk_pll1, +}; +static struct clk clk_h = { + .parent = &clk_pll1, +}; +static struct clk clk_p = { + .parent = &clk_pll1, +}; +static struct clk clk_pll2 = { + .parent = &clk_xtali, +}; static struct clk clk_usb_host = { + .parent = &clk_pll2, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_USH_EN, }; static struct clk clk_keypad = { + .parent = &clk_xtali, .sw_locked = 1, .enable_reg = EP93XX_SYSCON_KEYTCHCLKDIV, .enable_mask = EP93XX_SYSCON_KEYTCHCLKDIV_KEN, .set_rate = set_keytchclk_rate, }; static struct clk clk_pwm = { + .parent = &clk_xtali, .rate = EP93XX_EXT_CLK_RATE, }; @@ -85,50 +108,62 @@ static struct clk clk_video = { /* DMA Clocks */ static struct clk clk_m2p0 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P0, }; static struct clk clk_m2p1 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P1, }; static struct clk clk_m2p2 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P2, }; static struct clk clk_m2p3 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P3, }; static struct clk clk_m2p4 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P4, }; static struct clk clk_m2p5 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P5, }; static struct clk clk_m2p6 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P6, }; static struct clk clk_m2p7 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P7, }; static struct clk clk_m2p8 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P8, }; static struct clk clk_m2p9 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P9, }; static struct clk clk_m2m0 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2M0, }; static struct clk clk_m2m1 = { + .parent = &clk_h, .enable_reg = EP93XX_SYSCON_PWRCNT, .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2M1, }; @@ -137,6 +172,7 @@ static struct clk clk_m2m1 = { { .dev_id = dev, .con_id = con, .clk = ck } static struct clk_lookup clocks[] = { + INIT_CK(NULL, "xtali", &clk_xtali), INIT_CK("apb:uart1", NULL, &clk_uart1), INIT_CK("apb:uart2", NULL, &clk_uart2), INIT_CK("apb:uart3", NULL, &clk_uart3), @@ -163,48 +199,84 @@ static struct clk_lookup clocks[] = { INIT_CK(NULL, "m2m1", &clk_m2m1), }; +static DEFINE_SPINLOCK(clk_lock); + +static void __clk_enable(struct clk *clk) +{ + if (!clk->users++) { + if (clk->parent) + __clk_enable(clk->parent); + + if (clk->enable_reg) { + u32 v; + + v = __raw_readl(clk->enable_reg); + v |= clk->enable_mask; + if (clk->sw_locked) + ep93xx_syscon_swlocked_write(v, clk->enable_reg); + else + __raw_writel(v, clk->enable_reg); + } + } +} int clk_enable(struct clk *clk) { - if (!clk->users++ && clk->enable_reg) { - u32 value; + unsigned long flags; - value = __raw_readl(clk->enable_reg); - value |= clk->enable_mask; - if (clk->sw_locked) - ep93xx_syscon_swlocked_write(value, clk->enable_reg); - else - __raw_writel(value, clk->enable_reg); - } + if (!clk) + return -EINVAL; + + spin_lock_irqsave(&clk_lock, flags); + __clk_enable(clk); + spin_unlock_irqrestore(&clk_lock, flags); return 0; } EXPORT_SYMBOL(clk_enable); -void clk_disable(struct clk *clk) +static void __clk_disable(struct clk *clk) { - if (!--clk->users && clk->enable_reg) { - u32 value; + if (!--clk->users) { + if (clk->enable_reg) { + u32 v; + + v = __raw_readl(clk->enable_reg); + v &= ~clk->enable_mask; + if (clk->sw_locked) + ep93xx_syscon_swlocked_write(v, clk->enable_reg); + else + __raw_writel(v, clk->enable_reg); + } - value = __raw_readl(clk->enable_reg); - value &= ~clk->enable_mask; - if (clk->sw_locked) - ep93xx_syscon_swlocked_write(value, clk->enable_reg); - else - __raw_writel(value, clk->enable_reg); + if (clk->parent) + __clk_disable(clk->parent); } } + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + if (!clk) + return; + + spin_lock_irqsave(&clk_lock, flags); + __clk_disable(clk); + spin_unlock_irqrestore(&clk_lock, flags); +} EXPORT_SYMBOL(clk_disable); static unsigned long get_uart_rate(struct clk *clk) { + unsigned long rate = clk_get_rate(clk->parent); u32 value; value = __raw_readl(EP93XX_SYSCON_PWRCNT); if (value & EP93XX_SYSCON_PWRCNT_UARTBAUD) - return EP93XX_EXT_CLK_RATE; + return rate; else - return EP93XX_EXT_CLK_RATE / 2; + return rate / 2; } unsigned long clk_get_rate(struct clk *clk) @@ -244,16 +316,16 @@ static int set_keytchclk_rate(struct clk *clk, unsigned long rate) return 0; } -static unsigned long calc_clk_div(unsigned long rate, int *psel, int *esel, - int *pdiv, int *div) +static int calc_clk_div(struct clk *clk, unsigned long rate, + int *psel, int *esel, int *pdiv, int *div) { - unsigned long max_rate, best_rate = 0, - actual_rate = 0, mclk_rate = 0, rate_err = -1; + struct clk *mclk; + unsigned long max_rate, actual_rate, mclk_rate, rate_err = -1; int i, found = 0, __div = 0, __pdiv = 0; /* Don't exceed the maximum rate */ max_rate = max(max(clk_pll1.rate / 4, clk_pll2.rate / 4), - (unsigned long)EP93XX_EXT_CLK_RATE / 4); + clk_xtali.rate / 4); rate = min(rate, max_rate); /* @@ -267,11 +339,12 @@ static unsigned long calc_clk_div(unsigned long rate, int *psel, int *esel, */ for (i = 0; i < 3; i++) { if (i == 0) - mclk_rate = EP93XX_EXT_CLK_RATE * 2; + mclk = &clk_xtali; else if (i == 1) - mclk_rate = clk_pll1.rate * 2; - else if (i == 2) - mclk_rate = clk_pll2.rate * 2; + mclk = &clk_pll1; + else + mclk = &clk_pll2; + mclk_rate = mclk->rate * 2; /* Try each predivider value */ for (__pdiv = 4; __pdiv <= 6; __pdiv++) { @@ -286,7 +359,8 @@ static unsigned long calc_clk_div(unsigned long rate, int *psel, int *esel, *div = __div; *psel = (i == 2); *esel = (i != 0); - best_rate = actual_rate; + clk->parent = mclk; + clk->rate = actual_rate; rate_err = abs(actual_rate - rate); found = 1; } @@ -294,21 +368,19 @@ static unsigned long calc_clk_div(unsigned long rate, int *psel, int *esel, } if (!found) - return 0; + return -EINVAL; - return best_rate; + return 0; } static int set_div_rate(struct clk *clk, unsigned long rate) { - unsigned long actual_rate; - int psel = 0, esel = 0, pdiv = 0, div = 0; + int err, psel = 0, esel = 0, pdiv = 0, div = 0; u32 val; - actual_rate = calc_clk_div(rate, &psel, &esel, &pdiv, &div); - if (actual_rate == 0) - return -EINVAL; - clk->rate = actual_rate; + err = calc_clk_div(clk, rate, &psel, &esel, &pdiv, &div); + if (err) + return err; /* Clear the esel, psel, pdiv and div bits */ val = __raw_readl(clk->enable_reg); @@ -344,7 +416,7 @@ static unsigned long calc_pll_rate(u32 config_word) unsigned long long rate; int i; - rate = EP93XX_EXT_CLK_RATE; + rate = clk_xtali.rate; rate *= ((config_word >> 11) & 0x1f) + 1; /* X1FBD */ rate *= ((config_word >> 5) & 0x3f) + 1; /* X2FBD */ do_div(rate, (config_word & 0x1f) + 1); /* X2IPD */ @@ -377,7 +449,7 @@ static int __init ep93xx_clock_init(void) value = __raw_readl(EP93XX_SYSCON_CLOCK_SET1); if (!(value & 0x00800000)) { /* PLL1 bypassed? */ - clk_pll1.rate = EP93XX_EXT_CLK_RATE; + clk_pll1.rate = clk_xtali.rate; } else { clk_pll1.rate = calc_pll_rate(value); } @@ -388,7 +460,7 @@ static int __init ep93xx_clock_init(void) value = __raw_readl(EP93XX_SYSCON_CLOCK_SET2); if (!(value & 0x00080000)) { /* PLL2 bypassed? */ - clk_pll2.rate = EP93XX_EXT_CLK_RATE; + clk_pll2.rate = clk_xtali.rate; } else if (value & 0x00040000) { /* PLL2 enabled? */ clk_pll2.rate = calc_pll_rate(value); } else { -- cgit v1.2.3 From d191fe093f4494e0220f11f4ef2068b9581357b5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 11 Oct 2009 15:03:11 +0100 Subject: ARM: Dump memory and backtrace as one printk per line dump_mem and dump_backtrace were both using multiple printk statements to print each line. With DEBUG_LL enabled, this causes OOPS to become very difficult to read. Solve this by only using one printk per line. Signed-off-by: Russell King --- arch/arm/kernel/traps.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 467b69ed1021..e768fb59b674 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -50,10 +50,10 @@ static void dump_mem(const char *str, unsigned long bottom, unsigned long top); void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame) { #ifdef CONFIG_KALLSYMS - printk("[<%08lx>] ", where); - print_symbol("(%s) ", where); - printk("from [<%08lx>] ", from); - print_symbol("(%s)\n", from); + char sym1[KSYM_SYMBOL_LEN], sym2[KSYM_SYMBOL_LEN]; + sprint_symbol(sym1, where); + sprint_symbol(sym2, from); + printk("[<%08lx>] (%s) from [<%08lx>] (%s)\n", where, sym1, from, sym2); #else printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from); #endif @@ -83,7 +83,7 @@ static int verify_stack(unsigned long sp) */ static void dump_mem(const char *str, unsigned long bottom, unsigned long top) { - unsigned long p = bottom & ~31; + unsigned long first; mm_segment_t fs; int i; @@ -97,20 +97,23 @@ static void dump_mem(const char *str, unsigned long bottom, unsigned long top) printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); - for (p = bottom & ~31; p < top;) { - printk("%04lx: ", p & 0xffff); + for (first = bottom & ~31; first < top; first += 32) { + unsigned long p; + char str[sizeof(" 12345678") * 8 + 1]; - for (i = 0; i < 8; i++, p += 4) { - unsigned int val; + memset(str, ' ', sizeof(str)); + str[sizeof(str) - 1] = '\0'; - if (p < bottom || p >= top) - printk(" "); - else { - __get_user(val, (unsigned long *)p); - printk("%08x ", val); + for (p = first, i = 0; i < 8 && p < top; i++, p += 4) { + if (p >= bottom && p < top) { + unsigned long val; + if (__get_user(val, (unsigned long *)p) == 0) + sprintf(str + i * 9, " %08lx", val); + else + sprintf(str + i * 9, " ????????"); } } - printk ("\n"); + printk("%04lx:%s\n", first & 0xffff, str); } set_fs(fs); @@ -122,6 +125,7 @@ static void dump_instr(struct pt_regs *regs) const int thumb = thumb_mode(regs); const int width = thumb ? 4 : 8; mm_segment_t fs; + char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str; int i; /* @@ -132,7 +136,6 @@ static void dump_instr(struct pt_regs *regs) fs = get_fs(); set_fs(KERNEL_DS); - printk("Code: "); for (i = -4; i < 1; i++) { unsigned int val, bad; @@ -142,13 +145,14 @@ static void dump_instr(struct pt_regs *regs) bad = __get_user(val, &((u32 *)addr)[i]); if (!bad) - printk(i == 0 ? "(%0*x) " : "%0*x ", width, val); + p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ", + width, val); else { - printk("bad PC value."); + p += sprintf(p, "bad PC value"); break; } } - printk("\n"); + printk("Code: %s\n", str); set_fs(fs); } -- cgit v1.2.3 From e40c2ec6761d11043ad9f6352679049dbee836eb Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 11 Oct 2009 15:17:53 +0100 Subject: ARM: Dump code/mem oops lines with the appropriate log level Signed-off-by: Russell King --- arch/arm/kernel/traps.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index e768fb59b674..b551b6504966 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -45,7 +45,7 @@ static int __init user_debug_setup(char *str) __setup("user_debug=", user_debug_setup); #endif -static void dump_mem(const char *str, unsigned long bottom, unsigned long top); +static void dump_mem(const char *, const char *, unsigned long, unsigned long); void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame) { @@ -59,7 +59,7 @@ void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long #endif if (in_exception_text(where)) - dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); + dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); } #ifndef CONFIG_ARM_UNWIND @@ -81,7 +81,8 @@ static int verify_stack(unsigned long sp) /* * Dump out the contents of some memory nicely... */ -static void dump_mem(const char *str, unsigned long bottom, unsigned long top) +static void dump_mem(const char *lvl, const char *str, unsigned long bottom, + unsigned long top) { unsigned long first; mm_segment_t fs; @@ -95,7 +96,7 @@ static void dump_mem(const char *str, unsigned long bottom, unsigned long top) fs = get_fs(); set_fs(KERNEL_DS); - printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); + printk("%s%s(0x%08lx to 0x%08lx)\n", lvl, str, bottom, top); for (first = bottom & ~31; first < top; first += 32) { unsigned long p; @@ -113,13 +114,13 @@ static void dump_mem(const char *str, unsigned long bottom, unsigned long top) sprintf(str + i * 9, " ????????"); } } - printk("%04lx:%s\n", first & 0xffff, str); + printk("%s%04lx:%s\n", lvl, first & 0xffff, str); } set_fs(fs); } -static void dump_instr(struct pt_regs *regs) +static void dump_instr(const char *lvl, struct pt_regs *regs) { unsigned long addr = instruction_pointer(regs); const int thumb = thumb_mode(regs); @@ -152,7 +153,7 @@ static void dump_instr(struct pt_regs *regs) break; } } - printk("Code: %s\n", str); + printk("%sCode: %s\n", lvl, str); set_fs(fs); } @@ -228,18 +229,18 @@ static void __die(const char *str, int err, struct thread_info *thread, struct p struct task_struct *tsk = thread->task; static int die_counter; - printk("Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n", + printk(KERN_EMERG "Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n", str, err, ++die_counter); print_modules(); __show_regs(regs); - printk("Process %s (pid: %d, stack limit = 0x%p)\n", - tsk->comm, task_pid_nr(tsk), thread + 1); + printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n", + TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1); if (!user_mode(regs) || in_interrupt()) { - dump_mem("Stack: ", regs->ARM_sp, + dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp, THREAD_SIZE + (unsigned long)task_stack_page(tsk)); dump_backtrace(regs, tsk); - dump_instr(regs); + dump_instr(KERN_EMERG, regs); } } @@ -353,7 +354,7 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs) if (user_debug & UDBG_UNDEFINED) { printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n", current->comm, task_pid_nr(current), pc); - dump_instr(regs); + dump_instr(KERN_INFO, regs); } #endif @@ -404,7 +405,7 @@ static int bad_syscall(int n, struct pt_regs *regs) if (user_debug & UDBG_SYSCALL) { printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n", task_pid_nr(current), current->comm, n); - dump_instr(regs); + dump_instr(KERN_ERR, regs); } #endif @@ -583,7 +584,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) if (user_debug & UDBG_SYSCALL) { printk("[%d] %s: arm syscall %d\n", task_pid_nr(current), current->comm, no); - dump_instr(regs); + dump_instr("", regs); if (user_mode(regs)) { __show_regs(regs); c_backtrace(regs->ARM_fp, processor_mode(regs)); @@ -660,7 +661,7 @@ baddataabort(int code, unsigned long instr, struct pt_regs *regs) if (user_debug & UDBG_BADABORT) { printk(KERN_ERR "[%d] %s: bad data abort: code %d instr 0x%08lx\n", task_pid_nr(current), current->comm, code, instr); - dump_instr(regs); + dump_instr(KERN_ERR, regs); show_pte(current->mm, addr); } #endif -- cgit v1.2.3 From 03a6e5bd5ba7d2a37b0bf4327b83d7c83311b0a1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 11 Oct 2009 15:25:05 +0100 Subject: ARM: update die() output Make die() better match x86: - add printing of the last accessed sysfs file - ensure console_verbose() is called under the lock - ensure we panic outside of oops_exit() Signed-off-by: Russell King --- arch/arm/kernel/traps.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index b551b6504966..f838f36eb702 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -231,6 +231,7 @@ static void __die(const char *str, int err, struct thread_info *thread, struct p printk(KERN_EMERG "Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n", str, err, ++die_counter); + sysfs_printk_last_file(); print_modules(); __show_regs(regs); printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n", @@ -255,13 +256,14 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) oops_enter(); - console_verbose(); spin_lock_irq(&die_lock); + console_verbose(); bust_spinlocks(1); __die(str, err, thread, regs); bust_spinlocks(0); add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); + oops_exit(); if (in_interrupt()) panic("Fatal exception in interrupt"); @@ -269,7 +271,6 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) if (panic_on_oops) panic("Fatal exception"); - oops_exit(); do_exit(SIGSEGV); } -- cgit v1.2.3 From e9ac829185c5d17787d78c13c05a40c39d660239 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Tue, 21 Jul 2009 17:08:28 +0200 Subject: ARM: boolean bit testing Bit testing (test, testset, testclear, testchange) for bit numbers known at compile time returns a word with the tested-for bit set. Change it to return a true boolean value so to make it consistent with the out-of-line path and all the other bitops implementations. Signed-off-by: Johannes Weiner Signed-off-by: Russell King --- arch/arm/include/asm/bitops.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/bitops.h b/arch/arm/include/asm/bitops.h index 63a481fbbed4..338ff19ae447 100644 --- a/arch/arm/include/asm/bitops.h +++ b/arch/arm/include/asm/bitops.h @@ -84,7 +84,7 @@ ____atomic_test_and_set_bit(unsigned int bit, volatile unsigned long *p) *p = res | mask; raw_local_irq_restore(flags); - return res & mask; + return (res & mask) != 0; } static inline int @@ -101,7 +101,7 @@ ____atomic_test_and_clear_bit(unsigned int bit, volatile unsigned long *p) *p = res & ~mask; raw_local_irq_restore(flags); - return res & mask; + return (res & mask) != 0; } static inline int @@ -118,7 +118,7 @@ ____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p) *p = res ^ mask; raw_local_irq_restore(flags); - return res & mask; + return (res & mask) != 0; } #include -- cgit v1.2.3 From 6a5e293f1b34920c69a932ce37b4a4714a428dc7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 11 Oct 2009 16:29:48 +0100 Subject: ARM: Add kmap_atomic type debugging Seemingly this support was missed when highmem was added, so DEBUG_HIGHMEM wouldn't have checked the kmap_atomic type. Signed-off-by: Russell King --- arch/arm/mm/highmem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mm/highmem.c b/arch/arm/mm/highmem.c index 73cae57fa707..30f82fb5918c 100644 --- a/arch/arm/mm/highmem.c +++ b/arch/arm/mm/highmem.c @@ -46,6 +46,8 @@ void *kmap_atomic(struct page *page, enum km_type type) if (!PageHighMem(page)) return page_address(page); + debug_kmap_atomic(type); + kmap = kmap_high_get(page); if (kmap) return kmap; -- cgit v1.2.3 From d43c36dc6b357fa1806800f18aa30123c747a6d1 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 7 Oct 2009 17:09:06 +0400 Subject: headers: remove sched.h from interrupt.h After m68k's task_thread_info() doesn't refer to current, it's possible to remove sched.h from interrupt.h and not break m68k! Many thanks to Heiko Carstens for allowing this. Signed-off-by: Alexey Dobriyan --- arch/arm/kernel/time.c | 1 + arch/arm/mach-integrator/pci_v3.c | 1 + arch/arm/plat-s3c24xx/adc.c | 1 + arch/blackfin/kernel/time.c | 1 + arch/m32r/kernel/smp.c | 1 + arch/um/drivers/line.c | 1 + arch/um/drivers/port_kern.c | 1 + arch/um/kernel/irq.c | 1 + arch/x86/kernel/cpu/mcheck/mce_intel.c | 1 + arch/x86/kernel/pci-gart_64.c | 1 + arch/x86/kernel/reboot.c | 1 + arch/xtensa/kernel/time.c | 1 + crypto/aead.c | 1 + drivers/char/applicom.c | 1 + drivers/char/epca.c | 1 + drivers/char/generic_serial.c | 1 + drivers/char/istallion.c | 1 + drivers/char/nozomi.c | 1 + drivers/char/pty.c | 1 + drivers/char/rio/riocmd.c | 1 + drivers/char/rio/rioctrl.c | 1 + drivers/char/rio/riotty.c | 1 + drivers/char/ser_a2232.c | 1 + drivers/char/stallion.c | 1 + drivers/char/tlclk.c | 1 + drivers/hwmon/sht15.c | 1 + drivers/ieee1394/raw1394.c | 1 + drivers/ieee1394/video1394.c | 1 + drivers/infiniband/core/iwcm.c | 1 + drivers/infiniband/core/ucma.c | 1 + drivers/infiniband/hw/cxgb3/iwch_provider.c | 1 + drivers/infiniband/hw/cxgb3/iwch_qp.c | 1 + drivers/infiniband/hw/ipath/ipath_driver.c | 1 + drivers/infiniband/hw/ipath/ipath_iba7220.c | 1 + drivers/infiniband/hw/ipath/ipath_intr.c | 1 + drivers/infiniband/hw/ipath/ipath_qp.c | 1 + drivers/infiniband/hw/ipath/ipath_ruc.c | 1 + drivers/infiniband/hw/ipath/ipath_ud.c | 1 + drivers/infiniband/hw/ipath/ipath_user_pages.c | 1 + drivers/infiniband/hw/ipath/ipath_user_sdma.c | 1 + drivers/infiniband/hw/ipath/ipath_verbs_mcast.c | 1 + drivers/input/keyboard/hilkbd.c | 1 + drivers/input/keyboard/sunkbd.c | 1 + drivers/input/serio/libps2.c | 1 + drivers/input/serio/serio_raw.c | 1 + drivers/input/serio/serport.c | 1 + drivers/isdn/capi/kcapi.c | 1 + drivers/isdn/hisax/arcofi.c | 1 + drivers/isdn/hisax/hfc_2bds0.c | 1 + drivers/isdn/hisax/hfc_pci.c | 1 + drivers/isdn/hysdn/hysdn_procconf.c | 1 + drivers/isdn/hysdn/hysdn_proclog.c | 1 + drivers/isdn/pcbit/drv.c | 1 + drivers/isdn/pcbit/layer2.c | 1 + drivers/isdn/sc/init.c | 1 + drivers/lguest/interrupts_and_traps.c | 1 + drivers/media/dvb/dvb-core/dvb_net.c | 1 + drivers/media/video/meye.c | 1 + drivers/media/video/videobuf-core.c | 1 + drivers/media/video/videobuf-dma-sg.c | 1 + drivers/message/fusion/mptlan.c | 1 + drivers/mfd/ucb1x00-core.c | 1 + drivers/misc/hpilo.c | 1 + drivers/misc/ibmasm/command.c | 1 + drivers/misc/ibmasm/event.c | 1 + drivers/misc/ibmasm/r_heartbeat.c | 1 + drivers/misc/phantom.c | 1 + drivers/mtd/devices/m25p80.c | 1 + drivers/mtd/devices/sst25l.c | 1 + drivers/net/bonding/bond_sysfs.c | 1 + drivers/net/depca.c | 1 + drivers/net/e100.c | 1 + drivers/net/eql.c | 1 + drivers/net/ethoc.c | 1 + drivers/net/ewrk3.c | 1 + drivers/net/forcedeth.c | 1 + drivers/net/hamachi.c | 1 + drivers/net/hamradio/baycom_epp.c | 1 + drivers/net/hamradio/baycom_ser_fdx.c | 1 + drivers/net/hamradio/baycom_ser_hdx.c | 1 + drivers/net/hamradio/hdlcdrv.c | 1 + drivers/net/hp100.c | 1 + drivers/net/igb/igb_ethtool.c | 1 + drivers/net/irda/toim3232-sir.c | 1 + drivers/net/ns83820.c | 1 + drivers/net/pcnet32.c | 1 + drivers/net/sb1000.c | 1 + drivers/net/sis900.c | 1 + drivers/net/skfp/skfddi.c | 1 + drivers/net/skge.c | 1 + drivers/net/slip.c | 1 + drivers/net/sungem.c | 1 + drivers/net/tokenring/ibmtr.c | 1 + drivers/net/typhoon.c | 1 + drivers/net/wan/cosa.c | 1 + drivers/net/wan/cycx_x25.c | 1 + drivers/net/wan/dscc4.c | 1 + drivers/net/wan/farsync.c | 1 + drivers/net/wireless/b43/pio.c | 1 + drivers/net/wireless/b43legacy/main.c | 1 + drivers/net/wireless/b43legacy/phy.c | 1 + drivers/net/wireless/hostap/hostap_info.c | 1 + drivers/net/wireless/hostap/hostap_ioctl.c | 1 + drivers/net/wireless/ipw2x00/ipw2200.c | 1 + drivers/net/wireless/iwlwifi/iwl-3945.c | 1 + drivers/net/wireless/iwlwifi/iwl-4965.c | 1 + drivers/net/wireless/iwlwifi/iwl-5000.c | 1 + drivers/net/wireless/iwlwifi/iwl-agn.c | 1 + drivers/net/wireless/iwlwifi/iwl-core.c | 1 + drivers/net/wireless/iwlwifi/iwl-hcmd.c | 1 + drivers/net/wireless/iwlwifi/iwl-tx.c | 1 + drivers/net/wireless/iwlwifi/iwl3945-base.c | 1 + drivers/net/wireless/iwmc3200wifi/cfg80211.c | 1 + drivers/net/wireless/iwmc3200wifi/commands.c | 1 + drivers/net/wireless/iwmc3200wifi/main.c | 1 + drivers/net/wireless/iwmc3200wifi/rx.c | 1 + drivers/net/wireless/libertas/cmd.c | 1 + drivers/net/wireless/libertas/tx.c | 1 + drivers/net/wireless/prism54/isl_ioctl.c | 1 + drivers/net/wireless/prism54/islpci_dev.c | 1 + drivers/net/wireless/prism54/islpci_mgt.c | 1 + drivers/net/wireless/rt2x00/rt2x00debug.c | 1 + drivers/pci/pcie/aer/aerdrv.c | 1 + drivers/rtc/interface.c | 1 + drivers/rtc/rtc-dev.c | 1 + drivers/uio/uio.c | 1 + drivers/uwb/whc-rc.c | 1 + fs/file.c | 1 + include/linux/interrupt.h | 2 +- include/linux/mmc/host.h | 1 + kernel/irq/handle.c | 1 + kernel/mutex-debug.c | 1 + kernel/time/timekeeping.c | 1 + lib/debugobjects.c | 1 + lib/fault-inject.c | 1 + mm/vmalloc.c | 1 + net/irda/ircomm/ircomm_tty_attach.c | 1 + net/irda/irlan/irlan_common.c | 1 + net/irda/irlan/irlan_eth.c | 1 + net/irda/irnet/irnet_irda.c | 1 + net/irda/irnet/irnet_ppp.c | 1 + net/mac80211/rc80211_pid_debugfs.c | 1 + net/netfilter/nf_conntrack_core.c | 1 + net/sunrpc/xprtrdma/svc_rdma_transport.c | 1 + net/wireless/core.c | 1 + 145 files changed, 145 insertions(+), 1 deletion(-) diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 4cdc4a0bd02d..d38cdf2c8276 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c index 901cc205015e..148d25fc636f 100644 --- a/arch/arm/mach-integrator/pci_v3.c +++ b/arch/arm/mach-integrator/pci_v3.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include diff --git a/arch/arm/plat-s3c24xx/adc.c b/arch/arm/plat-s3c24xx/adc.c index 11117a7ba911..4d36b784fb8b 100644 --- a/arch/arm/plat-s3c24xx/adc.c +++ b/arch/arm/plat-s3c24xx/adc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/blackfin/kernel/time.c b/arch/blackfin/kernel/time.c index e5069fe6861e..bd3b53da295e 100644 --- a/arch/blackfin/kernel/time.c +++ b/arch/blackfin/kernel/time.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/arch/m32r/kernel/smp.c b/arch/m32r/kernel/smp.c index 1b7598e6f6e8..8a88f1f0a3e2 100644 --- a/arch/m32r/kernel/smp.c +++ b/arch/m32r/kernel/smp.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index 14a102e877d6..cf8a97f34518 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -5,6 +5,7 @@ #include "linux/irqreturn.h" #include "linux/kd.h" +#include "linux/sched.h" #include "chan_kern.h" #include "irq_kern.h" #include "irq_user.h" diff --git a/arch/um/drivers/port_kern.c b/arch/um/drivers/port_kern.c index 19930081d3d8..4ebc8a34738f 100644 --- a/arch/um/drivers/port_kern.c +++ b/arch/um/drivers/port_kern.c @@ -7,6 +7,7 @@ #include "linux/interrupt.h" #include "linux/list.h" #include "linux/mutex.h" +#include "linux/workqueue.h" #include "asm/atomic.h" #include "init.h" #include "irq_kern.h" diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c index 454cdb43e351..039270b9b73b 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c @@ -10,6 +10,7 @@ #include "linux/interrupt.h" #include "linux/kernel_stat.h" #include "linux/module.h" +#include "linux/sched.h" #include "linux/seq_file.h" #include "as-layout.h" #include "kern_util.h" diff --git a/arch/x86/kernel/cpu/mcheck/mce_intel.c b/arch/x86/kernel/cpu/mcheck/mce_intel.c index 889f665fe93d..7c785634af2b 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_intel.c +++ b/arch/x86/kernel/cpu/mcheck/mce_intel.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/kernel/pci-gart_64.c b/arch/x86/kernel/pci-gart_64.c index 98a827ee9ed7..a7f1b64f86e0 100644 --- a/arch/x86/kernel/pci-gart_64.c +++ b/arch/x86/kernel/pci-gart_64.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 27349f92a6d7..a1a3cdda06e1 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index 19085ff0484a..19f7df30937f 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include diff --git a/crypto/aead.c b/crypto/aead.c index d9aa733db164..0a55da70845e 100644 --- a/crypto/aead.c +++ b/crypto/aead.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index 73a0765344b6..fe2cb2f5db17 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 9d589e3144de..dde5134713e2 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 9e4e569dc00d..d400cbd280f2 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index ab2f3349c5c4..402838f4083e 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -19,6 +19,7 @@ /*****************************************************************************/ #include +#include #include #include #include diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index ec58d8c387ff..d3400b20444f 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/char/pty.c b/drivers/char/pty.c index e066c4fdf81b..62f282e67638 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/char/rio/riocmd.c b/drivers/char/rio/riocmd.c index 01f2654d5a2e..f121357e5af0 100644 --- a/drivers/char/rio/riocmd.c +++ b/drivers/char/rio/riocmd.c @@ -32,6 +32,7 @@ */ #include +#include #include #include #include diff --git a/drivers/char/rio/rioctrl.c b/drivers/char/rio/rioctrl.c index 74339559f0b9..780506326a73 100644 --- a/drivers/char/rio/rioctrl.c +++ b/drivers/char/rio/rioctrl.c @@ -31,6 +31,7 @@ */ #include +#include #include #include #include diff --git a/drivers/char/rio/riotty.c b/drivers/char/rio/riotty.c index 2fb49e89b324..47fab7c33073 100644 --- a/drivers/char/rio/riotty.c +++ b/drivers/char/rio/riotty.c @@ -33,6 +33,7 @@ #define __EXPLICIT_DEF_H__ #include +#include #include #include #include diff --git a/drivers/char/ser_a2232.c b/drivers/char/ser_a2232.c index 33a2b531802e..9610861d1f5f 100644 --- a/drivers/char/ser_a2232.c +++ b/drivers/char/ser_a2232.c @@ -89,6 +89,7 @@ #include #include #include +#include #include #include diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 53e504f41b20..db6dcfa35ba0 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -27,6 +27,7 @@ /*****************************************************************************/ #include +#include #include #include #include diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index 8f2284be68e1..80ea6bcfffdc 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -32,6 +32,7 @@ #include /* printk() */ #include /* everything... */ #include /* error codes */ +#include #include #include #include diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 303c02694c3c..2da6fb2c325e 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 0bc3d78ce7b1..8aa56ac07e29 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c index d287ba79821d..949064a05675 100644 --- a/drivers/ieee1394/video1394.c +++ b/drivers/ieee1394/video1394.c @@ -30,6 +30,7 @@ */ #include #include +#include #include #include #include diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c index 625fec5a741c..0f89909abce9 100644 --- a/drivers/infiniband/core/iwcm.c +++ b/drivers/infiniband/core/iwcm.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 4346a24568fb..bb96d3c4b0f4 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 03cfaecc3bb7..ed7175549ebd 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c index 6e8653471941..1cecf98829ac 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_qp.c +++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c @@ -29,6 +29,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include #include "iwch_provider.h" #include "iwch.h" #include "iwch_cm.h" diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c index 04e88b600558..013d1380e77c 100644 --- a/drivers/infiniband/hw/ipath/ipath_driver.c +++ b/drivers/infiniband/hw/ipath/ipath_driver.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#include #include #include #include diff --git a/drivers/infiniband/hw/ipath/ipath_iba7220.c b/drivers/infiniband/hw/ipath/ipath_iba7220.c index b2a9d4c155d1..a805402dd4ae 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba7220.c +++ b/drivers/infiniband/hw/ipath/ipath_iba7220.c @@ -37,6 +37,7 @@ #include #include +#include #include #include #include diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c index 6c21b4b5ec71..c0a03ac03ee7 100644 --- a/drivers/infiniband/hw/ipath/ipath_intr.c +++ b/drivers/infiniband/hw/ipath/ipath_intr.c @@ -33,6 +33,7 @@ #include #include +#include #include "ipath_kernel.h" #include "ipath_verbs.h" diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c index 3a5a89b609c4..cb2d3ef2ae12 100644 --- a/drivers/infiniband/hw/ipath/ipath_qp.c +++ b/drivers/infiniband/hw/ipath/ipath_qp.c @@ -32,6 +32,7 @@ */ #include +#include #include #include "ipath_verbs.h" diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c index 2296832f94da..1f95bbaf7602 100644 --- a/drivers/infiniband/hw/ipath/ipath_ruc.c +++ b/drivers/infiniband/hw/ipath/ipath_ruc.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#include #include #include "ipath_verbs.h" diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c index 6076cb61bf6a..7420715256a9 100644 --- a/drivers/infiniband/hw/ipath/ipath_ud.c +++ b/drivers/infiniband/hw/ipath/ipath_ud.c @@ -31,6 +31,7 @@ * SOFTWARE. */ +#include #include #include "ipath_verbs.h" diff --git a/drivers/infiniband/hw/ipath/ipath_user_pages.c b/drivers/infiniband/hw/ipath/ipath_user_pages.c index 855911e7396d..82878e348627 100644 --- a/drivers/infiniband/hw/ipath/ipath_user_pages.c +++ b/drivers/infiniband/hw/ipath/ipath_user_pages.c @@ -33,6 +33,7 @@ #include #include +#include #include "ipath_kernel.h" diff --git a/drivers/infiniband/hw/ipath/ipath_user_sdma.c b/drivers/infiniband/hw/ipath/ipath_user_sdma.c index 7bff4b9baa0a..be78f6643c06 100644 --- a/drivers/infiniband/hw/ipath/ipath_user_sdma.c +++ b/drivers/infiniband/hw/ipath/ipath_user_sdma.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c b/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c index d73e32232879..6923e1d986da 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c +++ b/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c @@ -32,6 +32,7 @@ */ #include +#include #include "ipath_verbs.h" diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c index e9d639ec283d..5f72440b50c8 100644 --- a/drivers/input/keyboard/hilkbd.c +++ b/drivers/input/keyboard/hilkbd.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_HP300 diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c index 472b56639cdb..a99a04b03ee4 100644 --- a/drivers/input/keyboard/sunkbd.c +++ b/drivers/input/keyboard/sunkbd.c @@ -27,6 +27,7 @@ */ #include +#include #include #include #include diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 769ba65a585a..f3876acc3e83 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index b03009bb7468..27fdaaffbb40 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -9,6 +9,7 @@ * the Free Software Foundation. */ +#include #include #include #include diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index b9694b6445d0..6d345112bcb7 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c index 57d26360f64e..dc506ab99cac 100644 --- a/drivers/isdn/capi/kcapi.c +++ b/drivers/isdn/capi/kcapi.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c index d30ce5b978c2..85a8fd8dd0b7 100644 --- a/drivers/isdn/hisax/arcofi.c +++ b/drivers/isdn/hisax/arcofi.c @@ -10,6 +10,7 @@ * */ +#include #include "hisax.h" #include "isdnl1.h" #include "isac.h" diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c index 5c46a7130e06..8d22f50760eb 100644 --- a/drivers/isdn/hisax/hfc_2bds0.c +++ b/drivers/isdn/hisax/hfc_2bds0.c @@ -11,6 +11,7 @@ */ #include +#include #include "hisax.h" #include "hfc_2bds0.h" #include "isdnl1.h" diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index d110a77940a4..10914731b304 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -20,6 +20,7 @@ #include "hfc_pci.h" #include "isdnl1.h" #include +#include #include static const char *hfcpci_revision = "$Revision: 1.48.2.4 $"; diff --git a/drivers/isdn/hysdn/hysdn_procconf.c b/drivers/isdn/hysdn/hysdn_procconf.c index 8f9f4912de32..90b35e1a4b7e 100644 --- a/drivers/isdn/hysdn/hysdn_procconf.c +++ b/drivers/isdn/hysdn/hysdn_procconf.c @@ -11,6 +11,7 @@ * */ +#include #include #include #include diff --git a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c index 8991d2c8ee4a..8bcae28c4409 100644 --- a/drivers/isdn/hysdn/hysdn_proclog.c +++ b/drivers/isdn/hysdn/hysdn_proclog.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "hysdn_defs.h" diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c index 8c66bcb953a1..123c1d6c43b4 100644 --- a/drivers/isdn/pcbit/drv.c +++ b/drivers/isdn/pcbit/drv.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c index e075e8d2fce0..30f0f45e3139 100644 --- a/drivers/isdn/pcbit/layer2.c +++ b/drivers/isdn/pcbit/layer2.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c index dd0acd06750b..5a0774880d56 100644 --- a/drivers/isdn/sc/init.c +++ b/drivers/isdn/sc/init.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "includes.h" #include "hardware.h" #include "card.h" diff --git a/drivers/lguest/interrupts_and_traps.c b/drivers/lguest/interrupts_and_traps.c index 18648180db02..daaf86631647 100644 --- a/drivers/lguest/interrupts_and_traps.c +++ b/drivers/lguest/interrupts_and_traps.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "lg.h" /* Allow Guests to use a non-128 (ie. non-Linux) syscall trap. */ diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index 8c9ae0a3a272..0241a7c5c34a 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -63,6 +63,7 @@ #include #include #include +#include #include "dvb_demux.h" #include "dvb_net.h" diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 4b1bc05a462c..01e1eefcf1eb 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -28,6 +28,7 @@ */ #include #include +#include #include #include #include diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index f1ccf98c0a6f..8e93c6f25c83 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index 53cdd67cebe1..032ebae0134a 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c index bc2ec2182c00..34f3f36f819b 100644 --- a/drivers/message/fusion/mptlan.c +++ b/drivers/message/fusion/mptlan.c @@ -56,6 +56,7 @@ #include #include #include +#include #define my_VERSION MPT_LINUX_VERSION_COMMON #define MYNAM "mptlan" diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c index fea9085fe52c..60c3988f3cf3 100644 --- a/drivers/mfd/ucb1x00-core.c +++ b/drivers/mfd/ucb1x00-core.c @@ -18,6 +18,7 @@ */ #include #include +#include #include #include #include diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 1ad27c6abcca..a92a3a742b43 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/misc/ibmasm/command.c b/drivers/misc/ibmasm/command.c index 276d3fb68094..e2031739aa29 100644 --- a/drivers/misc/ibmasm/command.c +++ b/drivers/misc/ibmasm/command.c @@ -22,6 +22,7 @@ * */ +#include #include "ibmasm.h" #include "lowlevel.h" diff --git a/drivers/misc/ibmasm/event.c b/drivers/misc/ibmasm/event.c index 68a0a5b94795..572d41ffc186 100644 --- a/drivers/misc/ibmasm/event.c +++ b/drivers/misc/ibmasm/event.c @@ -22,6 +22,7 @@ * */ +#include #include "ibmasm.h" #include "lowlevel.h" diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c index bec9e2c44bef..2de487ac788c 100644 --- a/drivers/misc/ibmasm/r_heartbeat.c +++ b/drivers/misc/ibmasm/r_heartbeat.c @@ -20,6 +20,7 @@ * */ +#include #include "ibmasm.h" #include "dot_command.h" diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 90a95ce8dc34..04c27266f567 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 379c316f329e..4c19269de91a 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index c2baf3353f84..0a11721f146e 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index ff449de6f3c0..8762a27a2a18 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/depca.c b/drivers/net/depca.c index 9686c1fa28f1..7a3bdac84abe 100644 --- a/drivers/net/depca.c +++ b/drivers/net/depca.c @@ -237,6 +237,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 679965c2bb86..5d2f48f02251 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -151,6 +151,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/eql.c b/drivers/net/eql.c index d4d9a3eda695..f5b96cadeb25 100644 --- a/drivers/net/eql.c +++ b/drivers/net/eql.c @@ -111,6 +111,7 @@ * Sorry, I had to rewrite most of this for 2.5.x -DaveM */ +#include #include #include #include diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index 34d0c69e67f7..96f5b2a2d2c5 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -17,6 +17,7 @@ #include #include #include +#include #include static int buffer_size = 0x8000; /* 32 KBytes */ diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c index b2a5ec8f3721..dd4ba01fd92d 100644 --- a/drivers/net/ewrk3.c +++ b/drivers/net/ewrk3.c @@ -145,6 +145,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 0a1c2bb27d4d..e1da4666f204 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c index 1d5064a09aca..f7519a594945 100644 --- a/drivers/net/hamachi.c +++ b/drivers/net/hamachi.c @@ -145,6 +145,7 @@ static int tx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (5*HZ) +#include #include #include #include diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 7bcaf7c66243..e344c84c0ef9 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index aa4488e871b2..ed60fd664273 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -71,6 +71,7 @@ /*****************************************************************************/ +#include #include #include #include diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c index 88c593596020..1686f6dcbbce 100644 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ b/drivers/net/hamradio/baycom_ser_hdx.c @@ -61,6 +61,7 @@ /*****************************************************************************/ +#include #include #include #include diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index 0013c409782c..91c5790c9581 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -42,6 +42,7 @@ /*****************************************************************************/ +#include #include #include #include diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index a9a1a99f02dd..dd8665138062 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -98,6 +98,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c index d004c359244c..deaea8fa1032 100644 --- a/drivers/net/igb/igb_ethtool.c +++ b/drivers/net/igb/igb_ethtool.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "igb.h" diff --git a/drivers/net/irda/toim3232-sir.c b/drivers/net/irda/toim3232-sir.c index fcf287b749db..99e1ec02a011 100644 --- a/drivers/net/irda/toim3232-sir.c +++ b/drivers/net/irda/toim3232-sir.c @@ -120,6 +120,7 @@ #include #include #include +#include #include diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c index c594e1946476..57fd483dbb1f 100644 --- a/drivers/net/ns83820.c +++ b/drivers/net/ns83820.c @@ -111,6 +111,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 6d28b18e7e28..c1b3f09f452c 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -31,6 +31,7 @@ static const char *const version = #include #include +#include #include #include #include diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index ee366c5a8fa3..c9c70ab0cce0 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -36,6 +36,7 @@ static char version[] = "sb1000.c:v1.1.2 6/01/98 (fventuri@mediaone.net)\n"; #include #include +#include #include #include #include diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index 97949d0a699b..c072f7f36acf 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c index 38a508b4aad9..b27156eaf267 100644 --- a/drivers/net/skfp/skfddi.c +++ b/drivers/net/skfp/skfddi.c @@ -73,6 +73,7 @@ static const char * const boot_msg = /* Include files */ +#include #include #include #include diff --git a/drivers/net/skge.c b/drivers/net/skge.c index 01f6811f1324..8f5414348e86 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/slip.c b/drivers/net/slip.c index e17c535a577e..fe3cebb984de 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 305ec3d783db..7019a0d1a82b 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index 525bbc5b9c9d..36cb2423bcf1 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -108,6 +108,7 @@ in the event that chatty debug messages are desired - jjs 12/30/98 */ #define IBMTR_DEBUG_MESSAGES 0 #include +#include #ifdef PCMCIA /* required for ibmtr_cs.c to build */ #undef MODULE /* yes, really */ diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index d6d345229fe9..5921f5bdd764 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -108,6 +108,7 @@ static const int multicast_filter_limit = 32; #include #include +#include #include #include #include diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 66360a2a14c2..e2c33c06190b 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -76,6 +76,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/wan/cycx_x25.c b/drivers/net/wan/cycx_x25.c index 2573c18b6aa5..cd8cb95c5bd7 100644 --- a/drivers/net/wan/cycx_x25.c +++ b/drivers/net/wan/cycx_x25.c @@ -84,6 +84,7 @@ #include /* printk(), and other useful stuff */ #include #include /* inline memset(), etc. */ +#include #include /* kmalloc(), kfree() */ #include /* offsetof(), etc. */ #include /* WAN router definitions */ diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index 81c8aec9df92..07d00b4cf48a 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -81,6 +81,7 @@ */ #include +#include #include #include #include diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 3e90eb816181..beda387f2fc7 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c index 9c1397996e0a..5e87650b07fb 100644 --- a/drivers/net/wireless/b43/pio.c +++ b/drivers/net/wireless/b43/pio.c @@ -30,6 +30,7 @@ #include "xmit.h" #include +#include static u16 generate_cookie(struct b43_pio_txqueue *q, diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 1d9223b3d4c4..4b60148a5e61 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/b43legacy/phy.c b/drivers/net/wireless/b43legacy/phy.c index 11319ec2d64a..aaf227203a98 100644 --- a/drivers/net/wireless/b43legacy/phy.c +++ b/drivers/net/wireless/b43legacy/phy.c @@ -31,6 +31,7 @@ #include #include +#include #include #include "b43legacy.h" diff --git a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/hostap/hostap_info.c index 6fa14a4e4b53..4dfb40a84c96 100644 --- a/drivers/net/wireless/hostap/hostap_info.c +++ b/drivers/net/wireless/hostap/hostap_info.c @@ -1,6 +1,7 @@ /* Host AP driver Info Frame processing (part of hostap.o module) */ #include +#include #include "hostap_wlan.h" #include "hostap.h" #include "hostap_ap.h" diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c index 3f2bda881a4f..9419cebca8a5 100644 --- a/drivers/net/wireless/hostap/hostap_ioctl.c +++ b/drivers/net/wireless/hostap/hostap_ioctl.c @@ -1,6 +1,7 @@ /* ioctl() (mostly Linux Wireless Extensions) routines for Host AP driver */ #include +#include #include #include #include diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 8d58e6ed4e7d..827824d45de9 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -30,6 +30,7 @@ ******************************************************************************/ +#include #include "ipw2200.h" diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index e70c5b0af364..68136172b823 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index a22a0501c190..6f703a041847 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index eb08f4411000..d6bc0e051043 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index cdc07c477457..313d3e5ee84b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 484d5c1a7312..2dc928755454 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "iwl-eeprom.h" diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index 532c8d6cd8da..a6856daf14cb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -28,6 +28,7 @@ #include #include +#include #include #include "iwl-dev.h" /* FIXME: remove */ diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index c18907544701..fb9bcfa6d947 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -28,6 +28,7 @@ *****************************************************************************/ #include +#include #include #include "iwl-eeprom.h" #include "iwl-dev.h" diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index c390dbd877e4..aa49230422f3 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c index a56a2b0ac99a..f3c55658225b 100644 --- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c +++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c index 23b52fa2605f..84158b6d35d8 100644 --- a/drivers/net/wireless/iwmc3200wifi/commands.c +++ b/drivers/net/wireless/iwmc3200wifi/commands.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "iwm.h" #include "bus.h" diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c index d668e4756324..222eb2cf1b30 100644 --- a/drivers/net/wireless/iwmc3200wifi/main.c +++ b/drivers/net/wireless/iwmc3200wifi/main.c @@ -38,6 +38,7 @@ #include #include +#include #include #include diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c index 40dbcbc16593..771a301003c9 100644 --- a/drivers/net/wireless/iwmc3200wifi/rx.c +++ b/drivers/net/wireless/iwmc3200wifi/rx.c @@ -38,6 +38,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 685098148e10..0a324dcd264c 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "host.h" #include "hostcmd.h" #include "decl.h" diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c index 4c018f7a0a8d..8c3766a6e8e7 100644 --- a/drivers/net/wireless/libertas/tx.c +++ b/drivers/net/wireless/libertas/tx.c @@ -3,6 +3,7 @@ */ #include #include +#include #include "hostcmd.h" #include "radiotap.h" diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 4c97c6ad6f5d..bc08464d8323 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -19,6 +19,7 @@ * */ +#include #include #include #include diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c index e26d7b3ceab5..2505be56ae39 100644 --- a/drivers/net/wireless/prism54/islpci_dev.c +++ b/drivers/net/wireless/prism54/islpci_dev.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/prism54/islpci_mgt.c b/drivers/net/wireless/prism54/islpci_mgt.c index f7c677e2094d..69d2f882fd06 100644 --- a/drivers/net/wireless/prism54/islpci_mgt.c +++ b/drivers/net/wireless/prism54/islpci_mgt.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index 7b3ee8c2eaef..68bc9bb1dbf9 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "rt2x00.h" diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 2ce8f9ccc66e..d49ecc94bd49 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 4cdb31a362ca..a0c816238aa9 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -12,6 +12,7 @@ */ #include +#include #include int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 8a11de9552cd..62227cd52410 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -13,6 +13,7 @@ #include #include +#include #include "rtc-core.h" static dev_t rtc_devt; diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index a9d707047202..e941367dd28f 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/uwb/whc-rc.c b/drivers/uwb/whc-rc.c index 1d9a6f54658e..01950c62dc8d 100644 --- a/drivers/uwb/whc-rc.c +++ b/drivers/uwb/whc-rc.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/file.c b/fs/file.c index f313314f996f..87e129030ab1 100644 --- a/fs/file.c +++ b/fs/file.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index b78cf8194957..7ca72b74eec7 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -610,6 +609,7 @@ extern void debug_poll_all_shared_irqs(void); static inline void debug_poll_all_shared_irqs(void) { } #endif +struct seq_file; int show_interrupts(struct seq_file *p, void *v); struct irq_desc; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 81bb42358595..eaf36364b7d4 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -11,6 +11,7 @@ #define LINUX_MMC_HOST_H #include +#include #include diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index a81cf80554db..17c71bb565c6 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include diff --git a/kernel/mutex-debug.c b/kernel/mutex-debug.c index 50d022e5a560..ec815a960b5d 100644 --- a/kernel/mutex-debug.c +++ b/kernel/mutex-debug.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index fb0f46fa1ecd..c3a4e2907eaa 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/debugobjects.c b/lib/debugobjects.c index 2755a3bd16a1..eae56fddfa3b 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -9,6 +9,7 @@ */ #include #include +#include #include #include #include diff --git a/lib/fault-inject.c b/lib/fault-inject.c index f97af55bdd96..7e65af70635e 100644 --- a/lib/fault-inject.c +++ b/lib/fault-inject.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 5e7aed0802bf..0f551a4a44cd 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index eafc010907c2..3c1754023022 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -30,6 +30,7 @@ ********************************************************************/ #include +#include #include #include diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c index 62116829b817..315ead3cb926 100644 --- a/net/irda/irlan/irlan_common.c +++ b/net/irda/irlan/irlan_common.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index 7b6b631f647f..d340110f5c0c 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include diff --git a/net/irda/irnet/irnet_irda.c b/net/irda/irnet/irnet_irda.c index cf9a4b531a98..cccc2e93234f 100644 --- a/net/irda/irnet/irnet_irda.c +++ b/net/irda/irnet/irnet_irda.c @@ -9,6 +9,7 @@ */ #include "irnet_irda.h" /* Private header */ +#include #include #include diff --git a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c index 68cbcb19cbd8..7dea882dbb75 100644 --- a/net/irda/irnet/irnet_ppp.c +++ b/net/irda/irnet/irnet_ppp.c @@ -13,6 +13,7 @@ * 2) as a control channel (write commands, read events) */ +#include #include #include "irnet_ppp.h" /* Private header */ /* Please put other headers in irnet.h - Thanks */ diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c index a59043fbb0ff..45667054a5f3 100644 --- a/net/mac80211/rc80211_pid_debugfs.c +++ b/net/mac80211/rc80211_pid_debugfs.c @@ -6,6 +6,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 7c9ec3dee96e..ca6e68dcd8a8 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 0cf5e8c27a10..3fa5751af0ec 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include diff --git a/net/wireless/core.c b/net/wireless/core.c index 45b2be3274db..a595f712b5bf 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include "nl80211.h" -- cgit v1.2.3 From ef1f7a7e878e4ae37b3a78ebdeef9f911bae59df Mon Sep 17 00:00:00 2001 From: Bernd Schmidt Date: Tue, 6 Oct 2009 09:55:26 +0100 Subject: ROMFS: fix length used with romfs_dev_strnlen() function An interestingly corrupted romfs file system exposed a problem with the romfs_dev_strnlen function: it's passing the wrong value to its helpers. Rather than limit the string to the length passed in by the callers, it uses the size of the device as the limit. Signed-off-by: Bernd Schmidt Signed-off-by: Mike Frysinger Signed-off-by: David Howells Signed-off-by: Linus Torvalds --- fs/romfs/storage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/romfs/storage.c b/fs/romfs/storage.c index b3208adf8e71..71e2b4d50a0a 100644 --- a/fs/romfs/storage.c +++ b/fs/romfs/storage.c @@ -253,11 +253,11 @@ ssize_t romfs_dev_strnlen(struct super_block *sb, #ifdef CONFIG_ROMFS_ON_MTD if (sb->s_mtd) - return romfs_mtd_strnlen(sb, pos, limit); + return romfs_mtd_strnlen(sb, pos, maxlen); #endif #ifdef CONFIG_ROMFS_ON_BLOCK if (sb->s_bdev) - return romfs_blk_strnlen(sb, pos, limit); + return romfs_blk_strnlen(sb, pos, maxlen); #endif return -EIO; } -- cgit v1.2.3 From 15b812f1d0a5ca8f5efe7f5882f468af10682ca8 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sun, 11 Oct 2009 14:17:16 -0700 Subject: pci: increase alignment to make more space for hidden code As reported in http://bugzilla.kernel.org/show_bug.cgi?id=13940 on some system when acpi are enabled, acpi clears some BAR for some devices without reason, and kernel will need to allocate devices for them. It then apparently hits some undocumented resource conflict, resulting in non-working devices. Try to increase alignment to get more safe range for unassigned devices. Signed-off-by: Yinghai Lu Signed-off-by: Linus Torvalds --- arch/x86/kernel/e820.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 85419bb7d4ab..d17d482a04f4 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -1378,8 +1378,8 @@ static unsigned long ram_alignment(resource_size_t pos) if (mb < 16) return 1024*1024; - /* To 32MB for anything above that */ - return 32*1024*1024; + /* To 64MB for anything above that */ + return 64*1024*1024; } #define MAX_RESOURCE_SIZE ((resource_size_t)-1) -- cgit v1.2.3 From 161291396e76e0832c08f617eb9bd364d1648148 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 11 Oct 2009 14:43:56 -0700 Subject: Linux 2.6.32-rc4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e50569ab5fe8..927d7a32ed9e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 32 -EXTRAVERSION = -rc3 +EXTRAVERSION = -rc4 NAME = Man-Eating Seals of Antiquity # *DOCUMENTATION* -- cgit v1.2.3 From d93a8f829fe1d2f3002f2c6ddb553d12db420412 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 11 Oct 2009 15:57:57 -0700 Subject: Revert "USB: Work around BIOS bugs by quiescing USB controllers earlier" This reverts commit db8be50c4307dac2b37305fc59c8dc0f978d09ea, as per http://bugzilla.kernel.org/show_bug.cgi?id=14374 http://marc.info/?l=linux-kernel&m=125446885705223&w=4 We simply can't do the USB handoff at FIXUP_HEADER time, since it will often require us to have valid IO mappings etc. But that in turn requires a whole different approach, not this trivial one-liner. Maybe we could teach all the USB quirk handoff handlers to only do the quirk if the device has all its registers set up (since if it isn't initialized, it's unlikely to be active), but regardless that will need a whole lot more code than just saying "let's do it really early". The proper fix is almost certainly to just leave the legacy IOMMU mappings active until after all devices have been initialized. Reported-by: Nick Piggin Cc: David Woodhouse Cc: Greg Kroah-Hartman Signed-off-by: Linus Torvalds --- drivers/usb/host/pci-quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 23cf3bde4762..83b5f9cea85a 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -475,4 +475,4 @@ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) else if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI) quirk_usb_handoff_xhci(pdev); } -DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); -- cgit v1.2.3 From c639ef431797813a9ab509e7cb5c77a080277a5b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 16 Sep 2009 11:06:52 +0200 Subject: [ARM] pxa/csb726: adjust duplicate structure field initialization Currently the irq_type field of the csb726_lan_config structure is initialized twice. The value in the first case, SMSC911X_IRQ_POLARITY_ACTIVE_LOW, is normally stored in the irq_polarity field, so I have renamed the field in the first initialization to that. Signed-off-by: Julia Lawall Signed-off-by: Eric Miao --- arch/arm/mach-pxa/csb726.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-pxa/csb726.c b/arch/arm/mach-pxa/csb726.c index 79141f862728..965480eb4fe6 100644 --- a/arch/arm/mach-pxa/csb726.c +++ b/arch/arm/mach-pxa/csb726.c @@ -238,7 +238,7 @@ static struct resource csb726_lan_resources[] = { }; struct smsc911x_platform_config csb726_lan_config = { - .irq_type = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, .flags = SMSC911X_USE_32BIT, .phy_interface = PHY_INTERFACE_MODE_MII, -- cgit v1.2.3 From 48f029542f1219eb50ade5e24b1b2799bd057413 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Fri, 2 Oct 2009 16:24:02 +0200 Subject: [ARM] pxamci: fix printing gpio numbers in pxamci_probe Signed-off-by: Antonio Ospite Reviewed-by: Robert Jarzmik Signed-off-by: Eric Miao --- drivers/mmc/host/pxamci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 5e0b1529964d..b00d67319058 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -693,7 +693,7 @@ static int pxamci_probe(struct platform_device *pdev) if (gpio_is_valid(gpio_ro)) { ret = gpio_request(gpio_ro, "mmc card read only"); if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_power); + dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_ro); goto err_gpio_ro; } gpio_direction_input(gpio_ro); @@ -701,7 +701,7 @@ static int pxamci_probe(struct platform_device *pdev) if (gpio_is_valid(gpio_cd)) { ret = gpio_request(gpio_cd, "mmc card detect"); if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", gpio_power); + dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", gpio_cd); goto err_gpio_cd; } gpio_direction_input(gpio_cd); -- cgit v1.2.3 From 4367216a099b4df3fa2c4f2b086cda1a1e9afc4e Mon Sep 17 00:00:00 2001 From: Dennis O'Brien Date: Sat, 10 Oct 2009 15:08:52 +0800 Subject: [ARM] pxa: workaround errata #37 by not using half turbo switching PXA27x Errata #37 implies system will hang when switching into or out of half turbo (HT bit in CLKCFG) mode, workaround this by not using it. Signed-off-by: Dennis O'Brien Cc: stable-2.6.31 Signed-off-by: Eric Miao --- arch/arm/mach-pxa/cpufreq-pxa2xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-pxa/cpufreq-pxa2xx.c b/arch/arm/mach-pxa/cpufreq-pxa2xx.c index 3a8ee2272add..983cc8c20081 100644 --- a/arch/arm/mach-pxa/cpufreq-pxa2xx.c +++ b/arch/arm/mach-pxa/cpufreq-pxa2xx.c @@ -155,7 +155,7 @@ MODULE_PARM_DESC(pxa255_turbo_table, "Selects the frequency table (0 = run table static pxa_freqs_t pxa27x_freqs[] = { {104000, 104000, PXA27x_CCCR(1, 8, 2), 0, CCLKCFG2(1, 0, 1), 900000, 1705000 }, - {156000, 104000, PXA27x_CCCR(1, 8, 6), 0, CCLKCFG2(1, 1, 1), 1000000, 1705000 }, + {156000, 104000, PXA27x_CCCR(1, 8, 3), 0, CCLKCFG2(1, 0, 1), 1000000, 1705000 }, {208000, 208000, PXA27x_CCCR(0, 16, 2), 1, CCLKCFG2(0, 0, 1), 1180000, 1705000 }, {312000, 208000, PXA27x_CCCR(1, 16, 3), 1, CCLKCFG2(1, 0, 1), 1250000, 1705000 }, {416000, 208000, PXA27x_CCCR(1, 16, 4), 1, CCLKCFG2(1, 0, 1), 1350000, 1705000 }, -- cgit v1.2.3 From 787b2faadc4356b6c2c71feb42fb944fece9a12f Mon Sep 17 00:00:00 2001 From: Nitin Gupta Date: Mon, 12 Oct 2009 14:20:23 +0530 Subject: ARM: force dcache flush if dcache_dirty bit set On ARM, update_mmu_cache() does dcache flush for a page only if it has a kernel mapping (page_mapping(page) != NULL). The correct behavior would be to force the flush based on dcache_dirty bit only. One of the cases where present logic would be a problem is when a RAM based block device[1] is used as a swap disk. In this case, we would have in-memory data corruption as shown in steps below: do_swap_page() { - Allocate a new page (if not already in swap cache) - Issue read from swap disk - Block driver issues flush_dcache_page() - flush_dcache_page() simply sets PG_dcache_dirty bit and does not actually issue a flush since this page has no user space mapping yet. - Now, if swap disk is almost full, this newly read page is removed from swap cache and corrsponding swap slot is freed. - Map this page anonymously in user space. - update_mmu_cache() - Since this page does not have kernel mapping (its not in page/swap cache and is mapped anonymously), it does not issue dcache flush even if dcache_dirty bit is set by flush_dcache_page() above. } Same problem exists on mips too. [1] example: - brd (RAM based block device) - ramzswap (RAM based compressed swap device) Signed-off-by: Nitin Gupta Signed-off-by: Russell King --- arch/arm/mm/fault-armv.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index bc0099d5ae85..d0d17b6a3703 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -153,14 +153,11 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte) page = pfn_to_page(pfn); mapping = page_mapping(page); - if (mapping) { #ifndef CONFIG_SMP - int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags); - - if (dirty) - __flush_dcache_page(mapping, page); + if (test_and_clear_bit(PG_dcache_dirty, &page->flags)) + __flush_dcache_page(mapping, page); #endif - + if (mapping) { if (cache_is_vivt()) make_coherent(mapping, vma, addr, pfn); else if (vma->vm_flags & VM_EXEC) -- cgit v1.2.3 From eea7e17e0eb23729d58368420659f8e7c357d82e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 12 Oct 2009 19:13:54 +0200 Subject: tty, serial: Fix race and NULL check in uart_close() Commit 46d57a449aa1 ("serial: use tty_port pointers in the core code") contained two bugs that causes (rare) crashes: - the rename typoed one site - a NULL check was missed Reported-and-tested-by: Ingo Molnar Cc: Greg KH Cc: Alan Cox Signed-off-by: Ingo Molnar Signed-off-by: Linus Torvalds --- drivers/serial/serial_core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 1689bda1d13b..dcc72444e8e7 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -1270,6 +1270,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp) BUG_ON(!kernel_locked()); + if (!state) + return; + uport = state->uart_port; port = &state->port; @@ -1316,9 +1319,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp) */ if (port->flags & ASYNC_INITIALIZED) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + spin_lock_irqsave(&uport->lock, flags); uport->ops->stop_rx(uport); - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock_irqrestore(&uport->lock, flags); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially -- cgit v1.2.3 From a1be9eee2996fd9969625e7b5e2f2bc2032fd3cb Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Mon, 12 Oct 2009 11:26:12 -0400 Subject: NFS: suppress a build warning struct sockaddr_storage * can safely be used as struct sockaddr *. Suppress an "incompatible pointer type" warning. Signed-off-by: Stefan Richter Signed-off-by: Trond Myklebust Signed-off-by: Linus Torvalds --- fs/nfs/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 6dabf6feec94..a2c18acb8568 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1848,8 +1848,8 @@ nfs_compare_remount_data(struct nfs_server *nfss, data->timeo != (10U * nfss->client->cl_timeout->to_initval / HZ) || data->nfs_server.port != nfss->port || data->nfs_server.addrlen != nfss->nfs_client->cl_addrlen || - !rpc_cmp_addr(&data->nfs_server.address, - &nfss->nfs_client->cl_addr)) + !rpc_cmp_addr((struct sockaddr *)&data->nfs_server.address, + (struct sockaddr *)&nfss->nfs_client->cl_addr)) return -EINVAL; return 0; -- cgit v1.2.3 From e3c6f15fecee5aaa2dd1d0b83b17d09b64997e31 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 12 Oct 2009 09:49:56 -0400 Subject: USB: musb: invert arch depend string The MUSB code relies on platform implementations that currently only exists for Arm and Blackfin processors, so have the MUSB Kconfig depend upon those arches. This should prevent other arches from building MUSB via randconfig. Reported-by: Ingo Molnar Signed-off-by: Mike Frysinger Signed-off-by: Linus Torvalds --- drivers/usb/musb/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 760e7271d17b..b84abd8ee8a5 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -9,7 +9,7 @@ comment "Enable Host or Gadget support to see Inventra options" # (M)HDRC = (Multipoint) Highspeed Dual-Role Controller config USB_MUSB_HDRC depends on (USB || USB_GADGET) - depends on !SUPERH + depends on (ARM || BLACKFIN) select NOP_USB_XCEIV if ARCH_DAVINCI select TWL4030_USB if MACH_OMAP_3430SDP select NOP_USB_XCEIV if MACH_OMAP3EVM -- cgit v1.2.3 From af67c3a9e68ee0a9e30ee8582577408adba0e299 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 13 Oct 2009 10:57:52 +0900 Subject: sh: update die() output. This follows the ARM change, as SH had all of the same issues: Make die() better match x86: - add printing of the last accessed sysfs file - ensure console_verbose() is called under the lock - ensure we panic outside of oops_exit() Signed-off-by: Paul Mundt --- arch/sh/kernel/traps_32.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index e0b5e4b5accd..7a2ee3a6b8e7 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -159,12 +160,12 @@ void die(const char * str, struct pt_regs * regs, long err) oops_enter(); - console_verbose(); spin_lock_irq(&die_lock); + console_verbose(); bust_spinlocks(1); printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); - + sysfs_printk_last_file(); print_modules(); show_regs(regs); @@ -180,6 +181,7 @@ void die(const char * str, struct pt_regs * regs, long err) bust_spinlocks(0); add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); + oops_exit(); if (kexec_should_crash(current)) crash_kexec(regs); @@ -190,7 +192,6 @@ void die(const char * str, struct pt_regs * regs, long err) if (panic_on_oops) panic("Fatal exception"); - oops_exit(); do_exit(SIGSEGV); } -- cgit v1.2.3 From 964f7e5a56814b32c727821de77d22bd7ef782bc Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 13 Oct 2009 11:18:34 +0900 Subject: sh: force dcache flush if dcache_dirty bit set. This too follows the ARM change, given that the issue at hand applies to all platforms that implement lazy D-cache writeback. This fixes up the case when a page mapping disappears between the flush_dcache_page() call (when PG_dcache_dirty is set for the page) and the update_mmu_cache() call -- such as in the case of swap cache being freed early. This kills off the mapping test in update_mmu_cache() and switches to simply testing for PG_dcache_dirty. Reported-by: Nitin Gupta Reported-by: Hugh Dickins Signed-off-by: Paul Mundt --- arch/sh/mm/cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sh/mm/cache.c b/arch/sh/mm/cache.c index 35c37b7f717a..5e1091be9dc4 100644 --- a/arch/sh/mm/cache.c +++ b/arch/sh/mm/cache.c @@ -128,7 +128,7 @@ void __update_cache(struct vm_area_struct *vma, return; page = pfn_to_page(pfn); - if (pfn_valid(pfn) && page_mapping(page)) { + if (pfn_valid(pfn)) { int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags); if (dirty) { unsigned long addr = (unsigned long)page_address(page); -- cgit v1.2.3