📄 aic94xx_init.c
字号:
/* * Aic94xx SAS/SATA driver initialization. * * Copyright (C) 2005 Adaptec, Inc. All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> * * This file is licensed under GPLv2. * * This file is part of the aic94xx driver. * * The aic94xx driver 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; version 2 of the * License. * * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/pci.h>#include <linux/delay.h>#include <scsi/scsi_host.h>#include "aic94xx.h"#include "aic94xx_reg.h"#include "aic94xx_hwi.h"#include "aic94xx_seq.h"/* The format is "version.release.patchlevel" */#define ASD_DRIVER_VERSION "1.0.3"static int use_msi = 0;module_param_named(use_msi, use_msi, int, S_IRUGO);MODULE_PARM_DESC(use_msi, "\n" "\tEnable(1) or disable(0) using PCI MSI.\n" "\tDefault: 0");static int lldd_max_execute_num = 0;module_param_named(collector, lldd_max_execute_num, int, S_IRUGO);MODULE_PARM_DESC(collector, "\n" "\tIf greater than one, tells the SAS Layer to run in Task Collector\n" "\tMode. If 1 or 0, tells the SAS Layer to run in Direct Mode.\n" "\tThe aic94xx SAS LLDD supports both modes.\n" "\tDefault: 0 (Direct Mode).\n");char sas_addr_str[2*SAS_ADDR_SIZE + 1] = "";static struct scsi_transport_template *aic94xx_transport_template;static int asd_scan_finished(struct Scsi_Host *, unsigned long);static void asd_scan_start(struct Scsi_Host *);static struct scsi_host_template aic94xx_sht = { .module = THIS_MODULE, /* .name is initialized */ .name = "aic94xx", .queuecommand = sas_queuecommand, .target_alloc = sas_target_alloc, .slave_configure = sas_slave_configure, .slave_destroy = sas_slave_destroy, .scan_finished = asd_scan_finished, .scan_start = asd_scan_start, .change_queue_depth = sas_change_queue_depth, .change_queue_type = sas_change_queue_type, .bios_param = sas_bios_param, .can_queue = 1, .cmd_per_lun = 1, .this_id = -1, .sg_tablesize = SG_ALL, .max_sectors = SCSI_DEFAULT_MAX_SECTORS, .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl,};static int __devinit asd_map_memio(struct asd_ha_struct *asd_ha){ int err, i; struct asd_ha_addrspace *io_handle; asd_ha->iospace = 0; for (i = 0; i < 3; i += 2) { io_handle = &asd_ha->io_handle[i==0?0:1]; io_handle->start = pci_resource_start(asd_ha->pcidev, i); io_handle->len = pci_resource_len(asd_ha->pcidev, i); io_handle->flags = pci_resource_flags(asd_ha->pcidev, i); err = -ENODEV; if (!io_handle->start || !io_handle->len) { asd_printk("MBAR%d start or length for %s is 0.\n", i==0?0:1, pci_name(asd_ha->pcidev)); goto Err; } err = pci_request_region(asd_ha->pcidev, i, ASD_DRIVER_NAME); if (err) { asd_printk("couldn't reserve memory region for %s\n", pci_name(asd_ha->pcidev)); goto Err; } if (io_handle->flags & IORESOURCE_CACHEABLE) io_handle->addr = ioremap(io_handle->start, io_handle->len); else io_handle->addr = ioremap_nocache(io_handle->start, io_handle->len); if (!io_handle->addr) { asd_printk("couldn't map MBAR%d of %s\n", i==0?0:1, pci_name(asd_ha->pcidev)); goto Err_unreq; } } return 0;Err_unreq: pci_release_region(asd_ha->pcidev, i);Err: if (i > 0) { io_handle = &asd_ha->io_handle[0]; iounmap(io_handle->addr); pci_release_region(asd_ha->pcidev, 0); } return err;}static void __devexit asd_unmap_memio(struct asd_ha_struct *asd_ha){ struct asd_ha_addrspace *io_handle; io_handle = &asd_ha->io_handle[1]; iounmap(io_handle->addr); pci_release_region(asd_ha->pcidev, 2); io_handle = &asd_ha->io_handle[0]; iounmap(io_handle->addr); pci_release_region(asd_ha->pcidev, 0);}static int __devinit asd_map_ioport(struct asd_ha_struct *asd_ha){ int i = PCI_IOBAR_OFFSET, err; struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; asd_ha->iospace = 1; io_handle->start = pci_resource_start(asd_ha->pcidev, i); io_handle->len = pci_resource_len(asd_ha->pcidev, i); io_handle->flags = pci_resource_flags(asd_ha->pcidev, i); io_handle->addr = (void __iomem *) io_handle->start; if (!io_handle->start || !io_handle->len) { asd_printk("couldn't get IO ports for %s\n", pci_name(asd_ha->pcidev)); return -ENODEV; } err = pci_request_region(asd_ha->pcidev, i, ASD_DRIVER_NAME); if (err) { asd_printk("couldn't reserve io space for %s\n", pci_name(asd_ha->pcidev)); } return err;}static void __devexit asd_unmap_ioport(struct asd_ha_struct *asd_ha){ pci_release_region(asd_ha->pcidev, PCI_IOBAR_OFFSET);}static int __devinit asd_map_ha(struct asd_ha_struct *asd_ha){ int err; u16 cmd_reg; err = pci_read_config_word(asd_ha->pcidev, PCI_COMMAND, &cmd_reg); if (err) { asd_printk("couldn't read command register of %s\n", pci_name(asd_ha->pcidev)); goto Err; } err = -ENODEV; if (cmd_reg & PCI_COMMAND_MEMORY) { if ((err = asd_map_memio(asd_ha))) goto Err; } else if (cmd_reg & PCI_COMMAND_IO) { if ((err = asd_map_ioport(asd_ha))) goto Err; asd_printk("%s ioport mapped -- upgrade your hardware\n", pci_name(asd_ha->pcidev)); } else { asd_printk("no proper device access to %s\n", pci_name(asd_ha->pcidev)); goto Err; } return 0;Err: return err;}static void __devexit asd_unmap_ha(struct asd_ha_struct *asd_ha){ if (asd_ha->iospace) asd_unmap_ioport(asd_ha); else asd_unmap_memio(asd_ha);}static const char *asd_dev_rev[30] = { [0] = "A0", [1] = "A1", [8] = "B0",};static int __devinit asd_common_setup(struct asd_ha_struct *asd_ha){ int err, i; asd_ha->revision_id = asd_ha->pcidev->revision; err = -ENODEV; if (asd_ha->revision_id < AIC9410_DEV_REV_B0) { asd_printk("%s is revision %s (%X), which is not supported\n", pci_name(asd_ha->pcidev), asd_dev_rev[asd_ha->revision_id], asd_ha->revision_id); goto Err; } /* Provide some sane default values. */ asd_ha->hw_prof.max_scbs = 512; asd_ha->hw_prof.max_ddbs = ASD_MAX_DDBS; asd_ha->hw_prof.num_phys = ASD_MAX_PHYS; /* All phys are enabled, by default. */ asd_ha->hw_prof.enabled_phys = 0xFF; for (i = 0; i < ASD_MAX_PHYS; i++) { asd_ha->hw_prof.phy_desc[i].max_sas_lrate = SAS_LINK_RATE_3_0_GBPS; asd_ha->hw_prof.phy_desc[i].min_sas_lrate = SAS_LINK_RATE_1_5_GBPS; asd_ha->hw_prof.phy_desc[i].max_sata_lrate = SAS_LINK_RATE_1_5_GBPS; asd_ha->hw_prof.phy_desc[i].min_sata_lrate = SAS_LINK_RATE_1_5_GBPS; } return 0;Err: return err;}static int __devinit asd_aic9410_setup(struct asd_ha_struct *asd_ha){ int err = asd_common_setup(asd_ha); if (err) return err; asd_ha->hw_prof.addr_range = 8; asd_ha->hw_prof.port_name_base = 0; asd_ha->hw_prof.dev_name_base = 8; asd_ha->hw_prof.sata_name_base = 16; return 0;}static int __devinit asd_aic9405_setup(struct asd_ha_struct *asd_ha){ int err = asd_common_setup(asd_ha); if (err) return err; asd_ha->hw_prof.addr_range = 4; asd_ha->hw_prof.port_name_base = 0; asd_ha->hw_prof.dev_name_base = 4; asd_ha->hw_prof.sata_name_base = 8; return 0;}static ssize_t asd_show_dev_rev(struct device *dev, struct device_attribute *attr, char *buf){ struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev); return snprintf(buf, PAGE_SIZE, "%s\n", asd_dev_rev[asd_ha->revision_id]);}static DEVICE_ATTR(revision, S_IRUGO, asd_show_dev_rev, NULL);static ssize_t asd_show_dev_bios_build(struct device *dev, struct device_attribute *attr,char *buf){ struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev); return snprintf(buf, PAGE_SIZE, "%d\n", asd_ha->hw_prof.bios.bld);}static DEVICE_ATTR(bios_build, S_IRUGO, asd_show_dev_bios_build, NULL);static ssize_t asd_show_dev_pcba_sn(struct device *dev, struct device_attribute *attr, char *buf){ struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev); return snprintf(buf, PAGE_SIZE, "%s\n", asd_ha->hw_prof.pcba_sn);}static DEVICE_ATTR(pcba_sn, S_IRUGO, asd_show_dev_pcba_sn, NULL);static int asd_create_dev_attrs(struct asd_ha_struct *asd_ha){ int err; err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_revision); if (err) return err; err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_bios_build); if (err) goto err_rev; err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn); if (err) goto err_biosb; return 0;err_biosb: device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);err_rev: device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision); return err;}static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha){ device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision); device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build); device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);}/* The first entry, 0, is used for dynamic ids, the rest for devices * we know about. */static struct asd_pcidev_struct { const char * name; int (*setup)(struct asd_ha_struct *asd_ha);} asd_pcidev_data[] = { /* Id 0 is used for dynamic ids. */ { .name = "Adaptec AIC-94xx SAS/SATA Host Adapter", .setup = asd_aic9410_setup }, { .name = "Adaptec AIC-9410W SAS/SATA Host Adapter", .setup = asd_aic9410_setup }, { .name = "Adaptec AIC-9405W SAS/SATA Host Adapter", .setup = asd_aic9405_setup },};static inline int asd_create_ha_caches(struct asd_ha_struct *asd_ha){ asd_ha->scb_pool = dma_pool_create(ASD_DRIVER_NAME "_scb_pool", &asd_ha->pcidev->dev, sizeof(struct scb), 8, 0); if (!asd_ha->scb_pool) { asd_printk("couldn't create scb pool\n"); return -ENOMEM; } return 0;}/** * asd_free_edbs -- free empty data buffers * asd_ha: pointer to host adapter structure */static inline void asd_free_edbs(struct asd_ha_struct *asd_ha){ struct asd_seq_data *seq = &asd_ha->seq; int i; for (i = 0; i < seq->num_edbs; i++) asd_free_coherent(asd_ha, seq->edb_arr[i]); kfree(seq->edb_arr); seq->edb_arr = NULL;}static inline void asd_free_escbs(struct asd_ha_struct *asd_ha){ struct asd_seq_data *seq = &asd_ha->seq; int i; for (i = 0; i < seq->num_escbs; i++) { if (!list_empty(&seq->escb_arr[i]->list)) list_del_init(&seq->escb_arr[i]->list); asd_ascb_free(seq->escb_arr[i]); } kfree(seq->escb_arr); seq->escb_arr = NULL;}static inline void asd_destroy_ha_caches(struct asd_ha_struct *asd_ha){ int i; if (asd_ha->hw_prof.ddb_ext) asd_free_coherent(asd_ha, asd_ha->hw_prof.ddb_ext); if (asd_ha->hw_prof.scb_ext) asd_free_coherent(asd_ha, asd_ha->hw_prof.scb_ext); if (asd_ha->hw_prof.ddb_bitmap) kfree(asd_ha->hw_prof.ddb_bitmap); asd_ha->hw_prof.ddb_bitmap = NULL; for (i = 0; i < ASD_MAX_PHYS; i++) { struct asd_phy *phy = &asd_ha->phys[i]; asd_free_coherent(asd_ha, phy->id_frm_tok); } if (asd_ha->seq.escb_arr) asd_free_escbs(asd_ha); if (asd_ha->seq.edb_arr) asd_free_edbs(asd_ha); if (asd_ha->hw_prof.ue.area) { kfree(asd_ha->hw_prof.ue.area); asd_ha->hw_prof.ue.area = NULL; } if (asd_ha->seq.tc_index_array) { kfree(asd_ha->seq.tc_index_array); kfree(asd_ha->seq.tc_index_bitmap); asd_ha->seq.tc_index_array = NULL; asd_ha->seq.tc_index_bitmap = NULL; } if (asd_ha->seq.actual_dl) { asd_free_coherent(asd_ha, asd_ha->seq.actual_dl); asd_ha->seq.actual_dl = NULL; asd_ha->seq.dl = NULL; } if (asd_ha->seq.next_scb.vaddr) { dma_pool_free(asd_ha->scb_pool, asd_ha->seq.next_scb.vaddr, asd_ha->seq.next_scb.dma_handle); asd_ha->seq.next_scb.vaddr = NULL; } dma_pool_destroy(asd_ha->scb_pool); asd_ha->scb_pool = NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -