4726 lines
146 KiB
C
4726 lines
146 KiB
C
|
/*
|
||
|
*******************************************************************************
|
||
|
** O.S : Linux
|
||
|
** FILE NAME : arcmsr_hba.c
|
||
|
** BY : Nick Cheng, C.L. Huang
|
||
|
** Description: SCSI RAID Device Driver for Areca RAID Controller
|
||
|
*******************************************************************************
|
||
|
** Copyright (C) 2002 - 2014, Areca Technology Corporation All rights reserved
|
||
|
**
|
||
|
** Web site: www.areca.com.tw
|
||
|
** E-mail: support@areca.com.tw
|
||
|
**
|
||
|
** 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.
|
||
|
** 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.
|
||
|
*******************************************************************************
|
||
|
** Redistribution and use in source and binary forms, with or without
|
||
|
** modification, are permitted provided that the following conditions
|
||
|
** are met:
|
||
|
** 1. Redistributions of source code must retain the above copyright
|
||
|
** notice, this list of conditions and the following disclaimer.
|
||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||
|
** notice, this list of conditions and the following disclaimer in the
|
||
|
** documentation and/or other materials provided with the distribution.
|
||
|
** 3. The name of the author may not be used to endorse or promote products
|
||
|
** derived from this software without specific prior written permission.
|
||
|
**
|
||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING,BUT
|
||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY
|
||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF
|
||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*******************************************************************************
|
||
|
** For history of changes, see Documentation/scsi/ChangeLog.arcmsr
|
||
|
** Firmware Specification, see Documentation/scsi/arcmsr_spec.rst
|
||
|
*******************************************************************************
|
||
|
*/
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/reboot.h>
|
||
|
#include <linux/spinlock.h>
|
||
|
#include <linux/pci_ids.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/moduleparam.h>
|
||
|
#include <linux/errno.h>
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/dma-mapping.h>
|
||
|
#include <linux/timer.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/pci.h>
|
||
|
#include <linux/aer.h>
|
||
|
#include <linux/circ_buf.h>
|
||
|
#include <asm/dma.h>
|
||
|
#include <asm/io.h>
|
||
|
#include <linux/uaccess.h>
|
||
|
#include <scsi/scsi_host.h>
|
||
|
#include <scsi/scsi.h>
|
||
|
#include <scsi/scsi_cmnd.h>
|
||
|
#include <scsi/scsi_tcq.h>
|
||
|
#include <scsi/scsi_device.h>
|
||
|
#include <scsi/scsi_transport.h>
|
||
|
#include <scsi/scsicam.h>
|
||
|
#include "arcmsr.h"
|
||
|
MODULE_AUTHOR("Nick Cheng, C.L. Huang <support@areca.com.tw>");
|
||
|
MODULE_DESCRIPTION("Areca ARC11xx/12xx/16xx/188x SAS/SATA RAID Controller Driver");
|
||
|
MODULE_LICENSE("Dual BSD/GPL");
|
||
|
MODULE_VERSION(ARCMSR_DRIVER_VERSION);
|
||
|
|
||
|
static int msix_enable = 1;
|
||
|
module_param(msix_enable, int, S_IRUGO);
|
||
|
MODULE_PARM_DESC(msix_enable, "Enable MSI-X interrupt(0 ~ 1), msix_enable=1(enable), =0(disable)");
|
||
|
|
||
|
static int msi_enable = 1;
|
||
|
module_param(msi_enable, int, S_IRUGO);
|
||
|
MODULE_PARM_DESC(msi_enable, "Enable MSI interrupt(0 ~ 1), msi_enable=1(enable), =0(disable)");
|
||
|
|
||
|
static int host_can_queue = ARCMSR_DEFAULT_OUTSTANDING_CMD;
|
||
|
module_param(host_can_queue, int, S_IRUGO);
|
||
|
MODULE_PARM_DESC(host_can_queue, " adapter queue depth(32 ~ 1024), default is 128");
|
||
|
|
||
|
static int cmd_per_lun = ARCMSR_DEFAULT_CMD_PERLUN;
|
||
|
module_param(cmd_per_lun, int, S_IRUGO);
|
||
|
MODULE_PARM_DESC(cmd_per_lun, " device queue depth(1 ~ 128), default is 32");
|
||
|
|
||
|
static int dma_mask_64 = 0;
|
||
|
module_param(dma_mask_64, int, S_IRUGO);
|
||
|
MODULE_PARM_DESC(dma_mask_64, " set DMA mask to 64 bits(0 ~ 1), dma_mask_64=1(64 bits), =0(32 bits)");
|
||
|
|
||
|
static int set_date_time = 0;
|
||
|
module_param(set_date_time, int, S_IRUGO);
|
||
|
MODULE_PARM_DESC(set_date_time, " send date, time to iop(0 ~ 1), set_date_time=1(enable), default(=0) is disable");
|
||
|
|
||
|
static int cmd_timeout = ARCMSR_DEFAULT_TIMEOUT;
|
||
|
module_param(cmd_timeout, int, S_IRUGO);
|
||
|
MODULE_PARM_DESC(cmd_timeout, " scsi cmd timeout(0 ~ 120 sec.), default is 90");
|
||
|
|
||
|
#define ARCMSR_SLEEPTIME 10
|
||
|
#define ARCMSR_RETRYCOUNT 12
|
||
|
|
||
|
static wait_queue_head_t wait_q;
|
||
|
static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb,
|
||
|
struct scsi_cmnd *cmd);
|
||
|
static int arcmsr_iop_confirm(struct AdapterControlBlock *acb);
|
||
|
static int arcmsr_abort(struct scsi_cmnd *);
|
||
|
static int arcmsr_bus_reset(struct scsi_cmnd *);
|
||
|
static int arcmsr_bios_param(struct scsi_device *sdev,
|
||
|
struct block_device *bdev, sector_t capacity, int *info);
|
||
|
static int arcmsr_queue_command(struct Scsi_Host *h, struct scsi_cmnd *cmd);
|
||
|
static int arcmsr_probe(struct pci_dev *pdev,
|
||
|
const struct pci_device_id *id);
|
||
|
static int __maybe_unused arcmsr_suspend(struct device *dev);
|
||
|
static int __maybe_unused arcmsr_resume(struct device *dev);
|
||
|
static void arcmsr_remove(struct pci_dev *pdev);
|
||
|
static void arcmsr_shutdown(struct pci_dev *pdev);
|
||
|
static void arcmsr_iop_init(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_free_ccb_pool(struct AdapterControlBlock *acb);
|
||
|
static u32 arcmsr_disable_outbound_ints(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_enable_outbound_ints(struct AdapterControlBlock *acb,
|
||
|
u32 intmask_org);
|
||
|
static void arcmsr_stop_adapter_bgrb(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_hbaA_flush_cache(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_hbaB_flush_cache(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_request_device_map(struct timer_list *t);
|
||
|
static void arcmsr_message_isr_bh_fn(struct work_struct *work);
|
||
|
static bool arcmsr_get_firmware_spec(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_start_adapter_bgrb(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_hbaC_message_isr(struct AdapterControlBlock *pACB);
|
||
|
static void arcmsr_hbaD_message_isr(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_hbaE_message_isr(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_hbaE_postqueue_isr(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_hbaF_postqueue_isr(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_hardware_reset(struct AdapterControlBlock *acb);
|
||
|
static const char *arcmsr_info(struct Scsi_Host *);
|
||
|
static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_free_irq(struct pci_dev *, struct AdapterControlBlock *);
|
||
|
static void arcmsr_wait_firmware_ready(struct AdapterControlBlock *acb);
|
||
|
static void arcmsr_set_iop_datetime(struct timer_list *);
|
||
|
static int arcmsr_slave_config(struct scsi_device *sdev);
|
||
|
static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev, int queue_depth)
|
||
|
{
|
||
|
if (queue_depth > ARCMSR_MAX_CMD_PERLUN)
|
||
|
queue_depth = ARCMSR_MAX_CMD_PERLUN;
|
||
|
return scsi_change_queue_depth(sdev, queue_depth);
|
||
|
}
|
||
|
|
||
|
static struct scsi_host_template arcmsr_scsi_host_template = {
|
||
|
.module = THIS_MODULE,
|
||
|
.name = "Areca SAS/SATA RAID driver",
|
||
|
.info = arcmsr_info,
|
||
|
.queuecommand = arcmsr_queue_command,
|
||
|
.eh_abort_handler = arcmsr_abort,
|
||
|
.eh_bus_reset_handler = arcmsr_bus_reset,
|
||
|
.bios_param = arcmsr_bios_param,
|
||
|
.slave_configure = arcmsr_slave_config,
|
||
|
.change_queue_depth = arcmsr_adjust_disk_queue_depth,
|
||
|
.can_queue = ARCMSR_DEFAULT_OUTSTANDING_CMD,
|
||
|
.this_id = ARCMSR_SCSI_INITIATOR_ID,
|
||
|
.sg_tablesize = ARCMSR_DEFAULT_SG_ENTRIES,
|
||
|
.max_sectors = ARCMSR_MAX_XFER_SECTORS_C,
|
||
|
.cmd_per_lun = ARCMSR_DEFAULT_CMD_PERLUN,
|
||
|
.shost_groups = arcmsr_host_groups,
|
||
|
.no_write_same = 1,
|
||
|
};
|
||
|
|
||
|
static struct pci_device_id arcmsr_device_id_table[] = {
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1110),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1120),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1130),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1160),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1170),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1200),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_B},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1201),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_B},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1202),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_B},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1203),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_B},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1210),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1214),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_D},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1220),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1230),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1260),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1270),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1280),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1380),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1381),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1680),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1681),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_A},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1880),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_C},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1884),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_E},
|
||
|
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1886),
|
||
|
.driver_data = ACB_ADAPTER_TYPE_F},
|
||
|
{0, 0}, /* Terminating entry */
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(pci, arcmsr_device_id_table);
|
||
|
|
||
|
static SIMPLE_DEV_PM_OPS(arcmsr_pm_ops, arcmsr_suspend, arcmsr_resume);
|
||
|
|
||
|
static struct pci_driver arcmsr_pci_driver = {
|
||
|
.name = "arcmsr",
|
||
|
.id_table = arcmsr_device_id_table,
|
||
|
.probe = arcmsr_probe,
|
||
|
.remove = arcmsr_remove,
|
||
|
.driver.pm = &arcmsr_pm_ops,
|
||
|
.shutdown = arcmsr_shutdown,
|
||
|
};
|
||
|
/*
|
||
|
****************************************************************************
|
||
|
****************************************************************************
|
||
|
*/
|
||
|
|
||
|
static void arcmsr_free_io_queue(struct AdapterControlBlock *acb)
|
||
|
{
|
||
|
switch (acb->adapter_type) {
|
||
|
case ACB_ADAPTER_TYPE_B:
|
||
|
case ACB_ADAPTER_TYPE_D:
|
||
|
case ACB_ADAPTER_TYPE_E:
|
||
|
case ACB_ADAPTER_TYPE_F:
|
||
|
dma_free_coherent(&acb->pdev->dev, acb->ioqueue_size,
|
||
|
acb->dma_coherent2, acb->dma_coherent_handle2);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool arcmsr_remap_pciregion(struct AdapterControlBlock *acb)
|
||
|
{
|
||
|
struct pci_dev *pdev = acb->pdev;
|
||
|
switch (acb->adapter_type){
|
||
|
case ACB_ADAPTER_TYPE_A:{
|
||
|
acb->pmuA = ioremap(pci_resource_start(pdev,0), pci_resource_len(pdev,0));
|
||
|
if (!acb->pmuA) {
|
||
|
printk(KERN_NOTICE "arcmsr%d: memory mapping region fail \n", acb->host->host_no);
|
||
|
return false;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case ACB_ADAPTER_TYPE_B:{
|
||
|
void __iomem *mem_base0, *mem_base1;
|
||
|
mem_base0 = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
|
||
|
if (!mem_base0) {
|
||
|
printk(KERN_NOTICE "arcmsr%d: memory mapping region fail \n", acb->host->host_no);
|
||
|
return false;
|
||
|
}
|
||
|
mem_base1 = ioremap(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2));
|
||
|