162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci 3w-sas.c -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci Written By: Adam Radford <aradford@gmail.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci Copyright (C) 2009 LSI Corporation. 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci This program is free software; you can redistribute it and/or modify 962306a36Sopenharmony_ci it under the terms of the GNU General Public License as published by 1062306a36Sopenharmony_ci the Free Software Foundation; version 2 of the License. 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci This program is distributed in the hope that it will be useful, 1362306a36Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 1462306a36Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1562306a36Sopenharmony_ci GNU General Public License for more details. 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci NO WARRANTY 1862306a36Sopenharmony_ci THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR 1962306a36Sopenharmony_ci CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT 2062306a36Sopenharmony_ci LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 2162306a36Sopenharmony_ci MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is 2262306a36Sopenharmony_ci solely responsible for determining the appropriateness of using and 2362306a36Sopenharmony_ci distributing the Program and assumes all risks associated with its 2462306a36Sopenharmony_ci exercise of rights under this Agreement, including but not limited to 2562306a36Sopenharmony_ci the risks and costs of program errors, damage to or loss of data, 2662306a36Sopenharmony_ci programs or equipment, and unavailability or interruption of operations. 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci DISCLAIMER OF LIABILITY 2962306a36Sopenharmony_ci NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY 3062306a36Sopenharmony_ci DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3162306a36Sopenharmony_ci DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND 3262306a36Sopenharmony_ci ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 3362306a36Sopenharmony_ci TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 3462306a36Sopenharmony_ci USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 3562306a36Sopenharmony_ci HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci You should have received a copy of the GNU General Public License 3862306a36Sopenharmony_ci along with this program; if not, write to the Free Software 3962306a36Sopenharmony_ci Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci Controllers supported by this driver: 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci LSI 3ware 9750 6Gb/s SAS/SATA-RAID 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci Bugs/Comments/Suggestions should be mailed to: 4662306a36Sopenharmony_ci aradford@gmail.com 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci History 4962306a36Sopenharmony_ci ------- 5062306a36Sopenharmony_ci 3.26.02.000 - Initial driver release. 5162306a36Sopenharmony_ci*/ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#include <linux/module.h> 5462306a36Sopenharmony_ci#include <linux/reboot.h> 5562306a36Sopenharmony_ci#include <linux/spinlock.h> 5662306a36Sopenharmony_ci#include <linux/interrupt.h> 5762306a36Sopenharmony_ci#include <linux/moduleparam.h> 5862306a36Sopenharmony_ci#include <linux/errno.h> 5962306a36Sopenharmony_ci#include <linux/types.h> 6062306a36Sopenharmony_ci#include <linux/delay.h> 6162306a36Sopenharmony_ci#include <linux/pci.h> 6262306a36Sopenharmony_ci#include <linux/time.h> 6362306a36Sopenharmony_ci#include <linux/mutex.h> 6462306a36Sopenharmony_ci#include <linux/slab.h> 6562306a36Sopenharmony_ci#include <asm/io.h> 6662306a36Sopenharmony_ci#include <asm/irq.h> 6762306a36Sopenharmony_ci#include <linux/uaccess.h> 6862306a36Sopenharmony_ci#include <scsi/scsi.h> 6962306a36Sopenharmony_ci#include <scsi/scsi_host.h> 7062306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 7162306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 7262306a36Sopenharmony_ci#include "3w-sas.h" 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* Globals */ 7562306a36Sopenharmony_ci#define TW_DRIVER_VERSION "3.26.02.000" 7662306a36Sopenharmony_cistatic DEFINE_MUTEX(twl_chrdev_mutex); 7762306a36Sopenharmony_cistatic TW_Device_Extension *twl_device_extension_list[TW_MAX_SLOT]; 7862306a36Sopenharmony_cistatic unsigned int twl_device_extension_count; 7962306a36Sopenharmony_cistatic int twl_major = -1; 8062306a36Sopenharmony_ciextern struct timezone sys_tz; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* Module parameters */ 8362306a36Sopenharmony_ciMODULE_AUTHOR ("LSI"); 8462306a36Sopenharmony_ciMODULE_DESCRIPTION ("LSI 3ware SAS/SATA-RAID Linux Driver"); 8562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 8662306a36Sopenharmony_ciMODULE_VERSION(TW_DRIVER_VERSION); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int use_msi; 8962306a36Sopenharmony_cimodule_param(use_msi, int, S_IRUGO); 9062306a36Sopenharmony_ciMODULE_PARM_DESC(use_msi, "Use Message Signaled Interrupts. Default: 0"); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* Function prototypes */ 9362306a36Sopenharmony_cistatic int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* Functions */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* This function returns AENs through sysfs */ 9862306a36Sopenharmony_cistatic ssize_t twl_sysfs_aen_read(struct file *filp, struct kobject *kobj, 9962306a36Sopenharmony_ci struct bin_attribute *bin_attr, 10062306a36Sopenharmony_ci char *outbuf, loff_t offset, size_t count) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct device *dev = container_of(kobj, struct device, kobj); 10362306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 10462306a36Sopenharmony_ci TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata; 10562306a36Sopenharmony_ci unsigned long flags = 0; 10662306a36Sopenharmony_ci ssize_t ret; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 10962306a36Sopenharmony_ci return -EACCES; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci spin_lock_irqsave(tw_dev->host->host_lock, flags); 11262306a36Sopenharmony_ci ret = memory_read_from_buffer(outbuf, count, &offset, tw_dev->event_queue[0], sizeof(TW_Event) * TW_Q_LENGTH); 11362306a36Sopenharmony_ci spin_unlock_irqrestore(tw_dev->host->host_lock, flags); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return ret; 11662306a36Sopenharmony_ci} /* End twl_sysfs_aen_read() */ 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* aen_read sysfs attribute initializer */ 11962306a36Sopenharmony_cistatic struct bin_attribute twl_sysfs_aen_read_attr = { 12062306a36Sopenharmony_ci .attr = { 12162306a36Sopenharmony_ci .name = "3ware_aen_read", 12262306a36Sopenharmony_ci .mode = S_IRUSR, 12362306a36Sopenharmony_ci }, 12462306a36Sopenharmony_ci .size = 0, 12562306a36Sopenharmony_ci .read = twl_sysfs_aen_read 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* This function returns driver compatibility info through sysfs */ 12962306a36Sopenharmony_cistatic ssize_t twl_sysfs_compat_info(struct file *filp, struct kobject *kobj, 13062306a36Sopenharmony_ci struct bin_attribute *bin_attr, 13162306a36Sopenharmony_ci char *outbuf, loff_t offset, size_t count) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct device *dev = container_of(kobj, struct device, kobj); 13462306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(dev); 13562306a36Sopenharmony_ci TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata; 13662306a36Sopenharmony_ci unsigned long flags = 0; 13762306a36Sopenharmony_ci ssize_t ret; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 14062306a36Sopenharmony_ci return -EACCES; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci spin_lock_irqsave(tw_dev->host->host_lock, flags); 14362306a36Sopenharmony_ci ret = memory_read_from_buffer(outbuf, count, &offset, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info)); 14462306a36Sopenharmony_ci spin_unlock_irqrestore(tw_dev->host->host_lock, flags); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci} /* End twl_sysfs_compat_info() */ 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* compat_info sysfs attribute initializer */ 15062306a36Sopenharmony_cistatic struct bin_attribute twl_sysfs_compat_info_attr = { 15162306a36Sopenharmony_ci .attr = { 15262306a36Sopenharmony_ci .name = "3ware_compat_info", 15362306a36Sopenharmony_ci .mode = S_IRUSR, 15462306a36Sopenharmony_ci }, 15562306a36Sopenharmony_ci .size = 0, 15662306a36Sopenharmony_ci .read = twl_sysfs_compat_info 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* Show some statistics about the card */ 16062306a36Sopenharmony_cistatic ssize_t twl_show_stats(struct device *dev, 16162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct Scsi_Host *host = class_to_shost(dev); 16462306a36Sopenharmony_ci TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; 16562306a36Sopenharmony_ci unsigned long flags = 0; 16662306a36Sopenharmony_ci ssize_t len; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci spin_lock_irqsave(tw_dev->host->host_lock, flags); 16962306a36Sopenharmony_ci len = snprintf(buf, PAGE_SIZE, "3w-sas Driver version: %s\n" 17062306a36Sopenharmony_ci "Current commands posted: %4d\n" 17162306a36Sopenharmony_ci "Max commands posted: %4d\n" 17262306a36Sopenharmony_ci "Last sgl length: %4d\n" 17362306a36Sopenharmony_ci "Max sgl length: %4d\n" 17462306a36Sopenharmony_ci "Last sector count: %4d\n" 17562306a36Sopenharmony_ci "Max sector count: %4d\n" 17662306a36Sopenharmony_ci "SCSI Host Resets: %4d\n" 17762306a36Sopenharmony_ci "AEN's: %4d\n", 17862306a36Sopenharmony_ci TW_DRIVER_VERSION, 17962306a36Sopenharmony_ci tw_dev->posted_request_count, 18062306a36Sopenharmony_ci tw_dev->max_posted_request_count, 18162306a36Sopenharmony_ci tw_dev->sgl_entries, 18262306a36Sopenharmony_ci tw_dev->max_sgl_entries, 18362306a36Sopenharmony_ci tw_dev->sector_count, 18462306a36Sopenharmony_ci tw_dev->max_sector_count, 18562306a36Sopenharmony_ci tw_dev->num_resets, 18662306a36Sopenharmony_ci tw_dev->aen_count); 18762306a36Sopenharmony_ci spin_unlock_irqrestore(tw_dev->host->host_lock, flags); 18862306a36Sopenharmony_ci return len; 18962306a36Sopenharmony_ci} /* End twl_show_stats() */ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* stats sysfs attribute initializer */ 19262306a36Sopenharmony_cistatic struct device_attribute twl_host_stats_attr = { 19362306a36Sopenharmony_ci .attr = { 19462306a36Sopenharmony_ci .name = "3ware_stats", 19562306a36Sopenharmony_ci .mode = S_IRUGO, 19662306a36Sopenharmony_ci }, 19762306a36Sopenharmony_ci .show = twl_show_stats 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* Host attributes initializer */ 20162306a36Sopenharmony_cistatic struct attribute *twl_host_attrs[] = { 20262306a36Sopenharmony_ci &twl_host_stats_attr.attr, 20362306a36Sopenharmony_ci NULL, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciATTRIBUTE_GROUPS(twl_host); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* This function will look up an AEN severity string */ 20962306a36Sopenharmony_cistatic char *twl_aen_severity_lookup(unsigned char severity_code) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci char *retval = NULL; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if ((severity_code < (unsigned char) TW_AEN_SEVERITY_ERROR) || 21462306a36Sopenharmony_ci (severity_code > (unsigned char) TW_AEN_SEVERITY_DEBUG)) 21562306a36Sopenharmony_ci goto out; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci retval = twl_aen_severity_table[severity_code]; 21862306a36Sopenharmony_ciout: 21962306a36Sopenharmony_ci return retval; 22062306a36Sopenharmony_ci} /* End twl_aen_severity_lookup() */ 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* This function will queue an event */ 22362306a36Sopenharmony_cistatic void twl_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci u32 local_time; 22662306a36Sopenharmony_ci TW_Event *event; 22762306a36Sopenharmony_ci unsigned short aen; 22862306a36Sopenharmony_ci char host[16]; 22962306a36Sopenharmony_ci char *error_str; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci tw_dev->aen_count++; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Fill out event info */ 23462306a36Sopenharmony_ci event = tw_dev->event_queue[tw_dev->error_index]; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci host[0] = '\0'; 23762306a36Sopenharmony_ci if (tw_dev->host) 23862306a36Sopenharmony_ci sprintf(host, " scsi%d:", tw_dev->host->host_no); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci aen = le16_to_cpu(header->status_block.error); 24162306a36Sopenharmony_ci memset(event, 0, sizeof(TW_Event)); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci event->severity = TW_SEV_OUT(header->status_block.severity__reserved); 24462306a36Sopenharmony_ci /* event->time_stamp_sec overflows in y2106 */ 24562306a36Sopenharmony_ci local_time = (u32)(ktime_get_real_seconds() - (sys_tz.tz_minuteswest * 60)); 24662306a36Sopenharmony_ci event->time_stamp_sec = local_time; 24762306a36Sopenharmony_ci event->aen_code = aen; 24862306a36Sopenharmony_ci event->retrieved = TW_AEN_NOT_RETRIEVED; 24962306a36Sopenharmony_ci event->sequence_id = tw_dev->error_sequence_id; 25062306a36Sopenharmony_ci tw_dev->error_sequence_id++; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Check for embedded error string */ 25362306a36Sopenharmony_ci error_str = &(header->err_specific_desc[strlen(header->err_specific_desc)+1]); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0'; 25662306a36Sopenharmony_ci event->parameter_len = strlen(header->err_specific_desc); 25762306a36Sopenharmony_ci memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len + 1 + strlen(error_str)); 25862306a36Sopenharmony_ci if (event->severity != TW_AEN_SEVERITY_DEBUG) 25962306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n", 26062306a36Sopenharmony_ci host, 26162306a36Sopenharmony_ci twl_aen_severity_lookup(TW_SEV_OUT(header->status_block.severity__reserved)), 26262306a36Sopenharmony_ci TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen, error_str, 26362306a36Sopenharmony_ci header->err_specific_desc); 26462306a36Sopenharmony_ci else 26562306a36Sopenharmony_ci tw_dev->aen_count--; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci tw_dev->error_index = (tw_dev->error_index + 1 ) % TW_Q_LENGTH; 26862306a36Sopenharmony_ci} /* End twl_aen_queue_event() */ 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* This function will attempt to post a command packet to the board */ 27162306a36Sopenharmony_cistatic int twl_post_command_packet(TW_Device_Extension *tw_dev, int request_id) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci dma_addr_t command_que_value; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci command_que_value = tw_dev->command_packet_phys[request_id]; 27662306a36Sopenharmony_ci command_que_value += TW_COMMAND_OFFSET; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* First write upper 4 bytes */ 27962306a36Sopenharmony_ci writel((u32)((u64)command_que_value >> 32), TWL_HIBQPH_REG_ADDR(tw_dev)); 28062306a36Sopenharmony_ci /* Then the lower 4 bytes */ 28162306a36Sopenharmony_ci writel((u32)(command_que_value | TWL_PULL_MODE), TWL_HIBQPL_REG_ADDR(tw_dev)); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci tw_dev->state[request_id] = TW_S_POSTED; 28462306a36Sopenharmony_ci tw_dev->posted_request_count++; 28562306a36Sopenharmony_ci if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) 28662306a36Sopenharmony_ci tw_dev->max_posted_request_count = tw_dev->posted_request_count; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci} /* End twl_post_command_packet() */ 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/* This function hands scsi cdb's to the firmware */ 29262306a36Sopenharmony_cistatic int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, 29362306a36Sopenharmony_ci unsigned char *cdb, int use_sg, 29462306a36Sopenharmony_ci TW_SG_Entry_ISO *sglistarg) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci TW_Command_Full *full_command_packet; 29762306a36Sopenharmony_ci TW_Command_Apache *command_packet; 29862306a36Sopenharmony_ci int i, sg_count; 29962306a36Sopenharmony_ci struct scsi_cmnd *srb = NULL; 30062306a36Sopenharmony_ci struct scatterlist *sg; 30162306a36Sopenharmony_ci int retval = 1; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (tw_dev->srb[request_id]) 30462306a36Sopenharmony_ci srb = tw_dev->srb[request_id]; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* Initialize command packet */ 30762306a36Sopenharmony_ci full_command_packet = tw_dev->command_packet_virt[request_id]; 30862306a36Sopenharmony_ci full_command_packet->header.header_desc.size_header = 128; 30962306a36Sopenharmony_ci full_command_packet->header.status_block.error = 0; 31062306a36Sopenharmony_ci full_command_packet->header.status_block.severity__reserved = 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci command_packet = &full_command_packet->command.newcommand; 31362306a36Sopenharmony_ci command_packet->status = 0; 31462306a36Sopenharmony_ci command_packet->opcode__reserved = TW_OPRES_IN(0, TW_OP_EXECUTE_SCSI); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* We forced 16 byte cdb use earlier */ 31762306a36Sopenharmony_ci if (!cdb) 31862306a36Sopenharmony_ci memcpy(command_packet->cdb, srb->cmnd, TW_MAX_CDB_LEN); 31962306a36Sopenharmony_ci else 32062306a36Sopenharmony_ci memcpy(command_packet->cdb, cdb, TW_MAX_CDB_LEN); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (srb) { 32362306a36Sopenharmony_ci command_packet->unit = srb->device->id; 32462306a36Sopenharmony_ci command_packet->request_id__lunl = 32562306a36Sopenharmony_ci cpu_to_le16(TW_REQ_LUN_IN(srb->device->lun, request_id)); 32662306a36Sopenharmony_ci } else { 32762306a36Sopenharmony_ci command_packet->request_id__lunl = 32862306a36Sopenharmony_ci cpu_to_le16(TW_REQ_LUN_IN(0, request_id)); 32962306a36Sopenharmony_ci command_packet->unit = 0; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci command_packet->sgl_offset = 16; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!sglistarg) { 33562306a36Sopenharmony_ci /* Map sglist from scsi layer to cmd packet */ 33662306a36Sopenharmony_ci if (scsi_sg_count(srb)) { 33762306a36Sopenharmony_ci sg_count = scsi_dma_map(srb); 33862306a36Sopenharmony_ci if (sg_count <= 0) 33962306a36Sopenharmony_ci goto out; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci scsi_for_each_sg(srb, sg, sg_count, i) { 34262306a36Sopenharmony_ci command_packet->sg_list[i].address = TW_CPU_TO_SGL(sg_dma_address(sg)); 34362306a36Sopenharmony_ci command_packet->sg_list[i].length = TW_CPU_TO_SGL(sg_dma_len(sg)); 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), scsi_sg_count(tw_dev->srb[request_id]))); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci } else { 34862306a36Sopenharmony_ci /* Internal cdb post */ 34962306a36Sopenharmony_ci for (i = 0; i < use_sg; i++) { 35062306a36Sopenharmony_ci command_packet->sg_list[i].address = TW_CPU_TO_SGL(sglistarg[i].address); 35162306a36Sopenharmony_ci command_packet->sg_list[i].length = TW_CPU_TO_SGL(sglistarg[i].length); 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN(0, use_sg)); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Update some stats */ 35762306a36Sopenharmony_ci if (srb) { 35862306a36Sopenharmony_ci tw_dev->sector_count = scsi_bufflen(srb) / 512; 35962306a36Sopenharmony_ci if (tw_dev->sector_count > tw_dev->max_sector_count) 36062306a36Sopenharmony_ci tw_dev->max_sector_count = tw_dev->sector_count; 36162306a36Sopenharmony_ci tw_dev->sgl_entries = scsi_sg_count(srb); 36262306a36Sopenharmony_ci if (tw_dev->sgl_entries > tw_dev->max_sgl_entries) 36362306a36Sopenharmony_ci tw_dev->max_sgl_entries = tw_dev->sgl_entries; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Now post the command to the board */ 36762306a36Sopenharmony_ci retval = twl_post_command_packet(tw_dev, request_id); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciout: 37062306a36Sopenharmony_ci return retval; 37162306a36Sopenharmony_ci} /* End twl_scsiop_execute_scsi() */ 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci/* This function will read the aen queue from the isr */ 37462306a36Sopenharmony_cistatic int twl_aen_read_queue(TW_Device_Extension *tw_dev, int request_id) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci unsigned char cdb[TW_MAX_CDB_LEN]; 37762306a36Sopenharmony_ci TW_SG_Entry_ISO sglist[1]; 37862306a36Sopenharmony_ci TW_Command_Full *full_command_packet; 37962306a36Sopenharmony_ci int retval = 1; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci full_command_packet = tw_dev->command_packet_virt[request_id]; 38262306a36Sopenharmony_ci memset(full_command_packet, 0, sizeof(TW_Command_Full)); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Initialize cdb */ 38562306a36Sopenharmony_ci memset(&cdb, 0, TW_MAX_CDB_LEN); 38662306a36Sopenharmony_ci cdb[0] = REQUEST_SENSE; /* opcode */ 38762306a36Sopenharmony_ci cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */ 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Initialize sglist */ 39062306a36Sopenharmony_ci memset(&sglist, 0, sizeof(TW_SG_Entry_ISO)); 39162306a36Sopenharmony_ci sglist[0].length = TW_SECTOR_SIZE; 39262306a36Sopenharmony_ci sglist[0].address = tw_dev->generic_buffer_phys[request_id]; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* Mark internal command */ 39562306a36Sopenharmony_ci tw_dev->srb[request_id] = NULL; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Now post the command packet */ 39862306a36Sopenharmony_ci if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) { 39962306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Post failed while reading AEN queue"); 40062306a36Sopenharmony_ci goto out; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci retval = 0; 40362306a36Sopenharmony_ciout: 40462306a36Sopenharmony_ci return retval; 40562306a36Sopenharmony_ci} /* End twl_aen_read_queue() */ 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* This function will sync firmware time with the host time */ 40862306a36Sopenharmony_cistatic void twl_aen_sync_time(TW_Device_Extension *tw_dev, int request_id) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci u32 schedulertime; 41162306a36Sopenharmony_ci TW_Command_Full *full_command_packet; 41262306a36Sopenharmony_ci TW_Command *command_packet; 41362306a36Sopenharmony_ci TW_Param_Apache *param; 41462306a36Sopenharmony_ci time64_t local_time; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* Fill out the command packet */ 41762306a36Sopenharmony_ci full_command_packet = tw_dev->command_packet_virt[request_id]; 41862306a36Sopenharmony_ci memset(full_command_packet, 0, sizeof(TW_Command_Full)); 41962306a36Sopenharmony_ci command_packet = &full_command_packet->command.oldcommand; 42062306a36Sopenharmony_ci command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM); 42162306a36Sopenharmony_ci command_packet->request_id = request_id; 42262306a36Sopenharmony_ci command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]); 42362306a36Sopenharmony_ci command_packet->byte8_offset.param.sgl[0].length = TW_CPU_TO_SGL(TW_SECTOR_SIZE); 42462306a36Sopenharmony_ci command_packet->size = TW_COMMAND_SIZE; 42562306a36Sopenharmony_ci command_packet->byte6_offset.parameter_count = cpu_to_le16(1); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Setup the param */ 42862306a36Sopenharmony_ci param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id]; 42962306a36Sopenharmony_ci memset(param, 0, TW_SECTOR_SIZE); 43062306a36Sopenharmony_ci param->table_id = cpu_to_le16(TW_TIMEKEEP_TABLE | 0x8000); /* Controller time keep table */ 43162306a36Sopenharmony_ci param->parameter_id = cpu_to_le16(0x3); /* SchedulerTime */ 43262306a36Sopenharmony_ci param->parameter_size_bytes = cpu_to_le16(4); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* Convert system time in UTC to local time seconds since last 43562306a36Sopenharmony_ci Sunday 12:00AM */ 43662306a36Sopenharmony_ci local_time = (ktime_get_real_seconds() - (sys_tz.tz_minuteswest * 60)); 43762306a36Sopenharmony_ci div_u64_rem(local_time - (3 * 86400), 604800, &schedulertime); 43862306a36Sopenharmony_ci schedulertime = cpu_to_le32(schedulertime); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci memcpy(param->data, &schedulertime, sizeof(u32)); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Mark internal command */ 44362306a36Sopenharmony_ci tw_dev->srb[request_id] = NULL; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* Now post the command */ 44662306a36Sopenharmony_ci twl_post_command_packet(tw_dev, request_id); 44762306a36Sopenharmony_ci} /* End twl_aen_sync_time() */ 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* This function will assign an available request id */ 45062306a36Sopenharmony_cistatic void twl_get_request_id(TW_Device_Extension *tw_dev, int *request_id) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci *request_id = tw_dev->free_queue[tw_dev->free_head]; 45362306a36Sopenharmony_ci tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH; 45462306a36Sopenharmony_ci tw_dev->state[*request_id] = TW_S_STARTED; 45562306a36Sopenharmony_ci} /* End twl_get_request_id() */ 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci/* This function will free a request id */ 45862306a36Sopenharmony_cistatic void twl_free_request_id(TW_Device_Extension *tw_dev, int request_id) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci tw_dev->free_queue[tw_dev->free_tail] = request_id; 46162306a36Sopenharmony_ci tw_dev->state[request_id] = TW_S_FINISHED; 46262306a36Sopenharmony_ci tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH; 46362306a36Sopenharmony_ci} /* End twl_free_request_id() */ 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci/* This function will complete an aen request from the isr */ 46662306a36Sopenharmony_cistatic int twl_aen_complete(TW_Device_Extension *tw_dev, int request_id) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci TW_Command_Full *full_command_packet; 46962306a36Sopenharmony_ci TW_Command *command_packet; 47062306a36Sopenharmony_ci TW_Command_Apache_Header *header; 47162306a36Sopenharmony_ci unsigned short aen; 47262306a36Sopenharmony_ci int retval = 1; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; 47562306a36Sopenharmony_ci tw_dev->posted_request_count--; 47662306a36Sopenharmony_ci aen = le16_to_cpu(header->status_block.error); 47762306a36Sopenharmony_ci full_command_packet = tw_dev->command_packet_virt[request_id]; 47862306a36Sopenharmony_ci command_packet = &full_command_packet->command.oldcommand; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* First check for internal completion of set param for time sync */ 48162306a36Sopenharmony_ci if (TW_OP_OUT(command_packet->opcode__sgloffset) == TW_OP_SET_PARAM) { 48262306a36Sopenharmony_ci /* Keep reading the queue in case there are more aen's */ 48362306a36Sopenharmony_ci if (twl_aen_read_queue(tw_dev, request_id)) 48462306a36Sopenharmony_ci goto out2; 48562306a36Sopenharmony_ci else { 48662306a36Sopenharmony_ci retval = 0; 48762306a36Sopenharmony_ci goto out; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci switch (aen) { 49262306a36Sopenharmony_ci case TW_AEN_QUEUE_EMPTY: 49362306a36Sopenharmony_ci /* Quit reading the queue if this is the last one */ 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci case TW_AEN_SYNC_TIME_WITH_HOST: 49662306a36Sopenharmony_ci twl_aen_sync_time(tw_dev, request_id); 49762306a36Sopenharmony_ci retval = 0; 49862306a36Sopenharmony_ci goto out; 49962306a36Sopenharmony_ci default: 50062306a36Sopenharmony_ci twl_aen_queue_event(tw_dev, header); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* If there are more aen's, keep reading the queue */ 50362306a36Sopenharmony_ci if (twl_aen_read_queue(tw_dev, request_id)) 50462306a36Sopenharmony_ci goto out2; 50562306a36Sopenharmony_ci else { 50662306a36Sopenharmony_ci retval = 0; 50762306a36Sopenharmony_ci goto out; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci retval = 0; 51162306a36Sopenharmony_ciout2: 51262306a36Sopenharmony_ci tw_dev->state[request_id] = TW_S_COMPLETED; 51362306a36Sopenharmony_ci twl_free_request_id(tw_dev, request_id); 51462306a36Sopenharmony_ci clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags); 51562306a36Sopenharmony_ciout: 51662306a36Sopenharmony_ci return retval; 51762306a36Sopenharmony_ci} /* End twl_aen_complete() */ 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci/* This function will poll for a response */ 52062306a36Sopenharmony_cistatic int twl_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci unsigned long before; 52362306a36Sopenharmony_ci dma_addr_t mfa; 52462306a36Sopenharmony_ci u32 regh, regl; 52562306a36Sopenharmony_ci u32 response; 52662306a36Sopenharmony_ci int retval = 1; 52762306a36Sopenharmony_ci int found = 0; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci before = jiffies; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci while (!found) { 53262306a36Sopenharmony_ci if (sizeof(dma_addr_t) > 4) { 53362306a36Sopenharmony_ci regh = readl(TWL_HOBQPH_REG_ADDR(tw_dev)); 53462306a36Sopenharmony_ci regl = readl(TWL_HOBQPL_REG_ADDR(tw_dev)); 53562306a36Sopenharmony_ci mfa = ((u64)regh << 32) | regl; 53662306a36Sopenharmony_ci } else 53762306a36Sopenharmony_ci mfa = readl(TWL_HOBQPL_REG_ADDR(tw_dev)); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci response = (u32)mfa; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (TW_RESID_OUT(response) == request_id) 54262306a36Sopenharmony_ci found = 1; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (time_after(jiffies, before + HZ * seconds)) 54562306a36Sopenharmony_ci goto out; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci msleep(50); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci retval = 0; 55062306a36Sopenharmony_ciout: 55162306a36Sopenharmony_ci return retval; 55262306a36Sopenharmony_ci} /* End twl_poll_response() */ 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* This function will drain the aen queue */ 55562306a36Sopenharmony_cistatic int twl_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci int request_id = 0; 55862306a36Sopenharmony_ci unsigned char cdb[TW_MAX_CDB_LEN]; 55962306a36Sopenharmony_ci TW_SG_Entry_ISO sglist[1]; 56062306a36Sopenharmony_ci int finished = 0, count = 0; 56162306a36Sopenharmony_ci TW_Command_Full *full_command_packet; 56262306a36Sopenharmony_ci TW_Command_Apache_Header *header; 56362306a36Sopenharmony_ci unsigned short aen; 56462306a36Sopenharmony_ci int first_reset = 0, queue = 0, retval = 1; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (no_check_reset) 56762306a36Sopenharmony_ci first_reset = 0; 56862306a36Sopenharmony_ci else 56962306a36Sopenharmony_ci first_reset = 1; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci full_command_packet = tw_dev->command_packet_virt[request_id]; 57262306a36Sopenharmony_ci memset(full_command_packet, 0, sizeof(TW_Command_Full)); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Initialize cdb */ 57562306a36Sopenharmony_ci memset(&cdb, 0, TW_MAX_CDB_LEN); 57662306a36Sopenharmony_ci cdb[0] = REQUEST_SENSE; /* opcode */ 57762306a36Sopenharmony_ci cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */ 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Initialize sglist */ 58062306a36Sopenharmony_ci memset(&sglist, 0, sizeof(TW_SG_Entry_ISO)); 58162306a36Sopenharmony_ci sglist[0].length = TW_SECTOR_SIZE; 58262306a36Sopenharmony_ci sglist[0].address = tw_dev->generic_buffer_phys[request_id]; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* Mark internal command */ 58562306a36Sopenharmony_ci tw_dev->srb[request_id] = NULL; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci do { 58862306a36Sopenharmony_ci /* Send command to the board */ 58962306a36Sopenharmony_ci if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) { 59062306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "Error posting request sense"); 59162306a36Sopenharmony_ci goto out; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* Now poll for completion */ 59562306a36Sopenharmony_ci if (twl_poll_response(tw_dev, request_id, 30)) { 59662306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "No valid response while draining AEN queue"); 59762306a36Sopenharmony_ci tw_dev->posted_request_count--; 59862306a36Sopenharmony_ci goto out; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci tw_dev->posted_request_count--; 60262306a36Sopenharmony_ci header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id]; 60362306a36Sopenharmony_ci aen = le16_to_cpu(header->status_block.error); 60462306a36Sopenharmony_ci queue = 0; 60562306a36Sopenharmony_ci count++; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci switch (aen) { 60862306a36Sopenharmony_ci case TW_AEN_QUEUE_EMPTY: 60962306a36Sopenharmony_ci if (first_reset != 1) 61062306a36Sopenharmony_ci goto out; 61162306a36Sopenharmony_ci else 61262306a36Sopenharmony_ci finished = 1; 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci case TW_AEN_SOFT_RESET: 61562306a36Sopenharmony_ci if (first_reset == 0) 61662306a36Sopenharmony_ci first_reset = 1; 61762306a36Sopenharmony_ci else 61862306a36Sopenharmony_ci queue = 1; 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci case TW_AEN_SYNC_TIME_WITH_HOST: 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci default: 62362306a36Sopenharmony_ci queue = 1; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* Now queue an event info */ 62762306a36Sopenharmony_ci if (queue) 62862306a36Sopenharmony_ci twl_aen_queue_event(tw_dev, header); 62962306a36Sopenharmony_ci } while ((finished == 0) && (count < TW_MAX_AEN_DRAIN)); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (count == TW_MAX_AEN_DRAIN) 63262306a36Sopenharmony_ci goto out; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci retval = 0; 63562306a36Sopenharmony_ciout: 63662306a36Sopenharmony_ci tw_dev->state[request_id] = TW_S_INITIAL; 63762306a36Sopenharmony_ci return retval; 63862306a36Sopenharmony_ci} /* End twl_aen_drain_queue() */ 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci/* This function will allocate memory and check if it is correctly aligned */ 64162306a36Sopenharmony_cistatic int twl_allocate_memory(TW_Device_Extension *tw_dev, int size, int which) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci int i; 64462306a36Sopenharmony_ci dma_addr_t dma_handle; 64562306a36Sopenharmony_ci unsigned long *cpu_addr; 64662306a36Sopenharmony_ci int retval = 1; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, 64962306a36Sopenharmony_ci size * TW_Q_LENGTH, &dma_handle, 65062306a36Sopenharmony_ci GFP_KERNEL); 65162306a36Sopenharmony_ci if (!cpu_addr) { 65262306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed"); 65362306a36Sopenharmony_ci goto out; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci for (i = 0; i < TW_Q_LENGTH; i++) { 65762306a36Sopenharmony_ci switch(which) { 65862306a36Sopenharmony_ci case 0: 65962306a36Sopenharmony_ci tw_dev->command_packet_phys[i] = dma_handle+(i*size); 66062306a36Sopenharmony_ci tw_dev->command_packet_virt[i] = (TW_Command_Full *)((unsigned char *)cpu_addr + (i*size)); 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci case 1: 66362306a36Sopenharmony_ci tw_dev->generic_buffer_phys[i] = dma_handle+(i*size); 66462306a36Sopenharmony_ci tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci case 2: 66762306a36Sopenharmony_ci tw_dev->sense_buffer_phys[i] = dma_handle+(i*size); 66862306a36Sopenharmony_ci tw_dev->sense_buffer_virt[i] = (TW_Command_Apache_Header *)((unsigned char *)cpu_addr + (i*size)); 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci retval = 0; 67362306a36Sopenharmony_ciout: 67462306a36Sopenharmony_ci return retval; 67562306a36Sopenharmony_ci} /* End twl_allocate_memory() */ 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci/* This function will load the request id and various sgls for ioctls */ 67862306a36Sopenharmony_cistatic void twl_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci TW_Command *oldcommand; 68162306a36Sopenharmony_ci TW_Command_Apache *newcommand; 68262306a36Sopenharmony_ci TW_SG_Entry_ISO *sgl; 68362306a36Sopenharmony_ci unsigned int pae = 0; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4)) 68662306a36Sopenharmony_ci pae = 1; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) { 68962306a36Sopenharmony_ci newcommand = &full_command_packet->command.newcommand; 69062306a36Sopenharmony_ci newcommand->request_id__lunl = 69162306a36Sopenharmony_ci cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id)); 69262306a36Sopenharmony_ci if (length) { 69362306a36Sopenharmony_ci newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache)); 69462306a36Sopenharmony_ci newcommand->sg_list[0].length = TW_CPU_TO_SGL(length); 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci newcommand->sgl_entries__lunh = 69762306a36Sopenharmony_ci cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), length ? 1 : 0)); 69862306a36Sopenharmony_ci } else { 69962306a36Sopenharmony_ci oldcommand = &full_command_packet->command.oldcommand; 70062306a36Sopenharmony_ci oldcommand->request_id = request_id; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) { 70362306a36Sopenharmony_ci /* Load the sg list */ 70462306a36Sopenharmony_ci sgl = (TW_SG_Entry_ISO *)((u32 *)oldcommand+oldcommand->size - (sizeof(TW_SG_Entry_ISO)/4) + pae + (sizeof(dma_addr_t) > 4 ? 1 : 0)); 70562306a36Sopenharmony_ci sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache)); 70662306a36Sopenharmony_ci sgl->length = TW_CPU_TO_SGL(length); 70762306a36Sopenharmony_ci oldcommand->size += pae; 70862306a36Sopenharmony_ci oldcommand->size += sizeof(dma_addr_t) > 4 ? 1 : 0; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci} /* End twl_load_sgl() */ 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci/* This function handles ioctl for the character device 71462306a36Sopenharmony_ci This interface is used by smartmontools open source software */ 71562306a36Sopenharmony_cistatic long twl_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci long timeout; 71862306a36Sopenharmony_ci unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0; 71962306a36Sopenharmony_ci dma_addr_t dma_handle; 72062306a36Sopenharmony_ci int request_id = 0; 72162306a36Sopenharmony_ci TW_Ioctl_Driver_Command driver_command; 72262306a36Sopenharmony_ci struct inode *inode = file_inode(file); 72362306a36Sopenharmony_ci TW_Ioctl_Buf_Apache *tw_ioctl; 72462306a36Sopenharmony_ci TW_Command_Full *full_command_packet; 72562306a36Sopenharmony_ci TW_Device_Extension *tw_dev = twl_device_extension_list[iminor(inode)]; 72662306a36Sopenharmony_ci int retval = -EFAULT; 72762306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci mutex_lock(&twl_chrdev_mutex); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* Only let one of these through at a time */ 73262306a36Sopenharmony_ci if (mutex_lock_interruptible(&tw_dev->ioctl_lock)) { 73362306a36Sopenharmony_ci retval = -EINTR; 73462306a36Sopenharmony_ci goto out; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* First copy down the driver command */ 73862306a36Sopenharmony_ci if (copy_from_user(&driver_command, argp, sizeof(TW_Ioctl_Driver_Command))) 73962306a36Sopenharmony_ci goto out2; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Check data buffer size */ 74262306a36Sopenharmony_ci if (driver_command.buffer_length > TW_MAX_SECTORS * 2048) { 74362306a36Sopenharmony_ci retval = -EINVAL; 74462306a36Sopenharmony_ci goto out2; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* Hardware can only do multiple of 512 byte transfers */ 74862306a36Sopenharmony_ci data_buffer_length_adjusted = (driver_command.buffer_length + 511) & ~511; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* Now allocate ioctl buf memory */ 75162306a36Sopenharmony_ci cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted + sizeof(TW_Ioctl_Buf_Apache), &dma_handle, GFP_KERNEL); 75262306a36Sopenharmony_ci if (!cpu_addr) { 75362306a36Sopenharmony_ci retval = -ENOMEM; 75462306a36Sopenharmony_ci goto out2; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* Now copy down the entire ioctl */ 76062306a36Sopenharmony_ci if (copy_from_user(tw_ioctl, argp, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache))) 76162306a36Sopenharmony_ci goto out3; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* See which ioctl we are doing */ 76462306a36Sopenharmony_ci switch (cmd) { 76562306a36Sopenharmony_ci case TW_IOCTL_FIRMWARE_PASS_THROUGH: 76662306a36Sopenharmony_ci spin_lock_irqsave(tw_dev->host->host_lock, flags); 76762306a36Sopenharmony_ci twl_get_request_id(tw_dev, &request_id); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Flag internal command */ 77062306a36Sopenharmony_ci tw_dev->srb[request_id] = NULL; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* Flag chrdev ioctl */ 77362306a36Sopenharmony_ci tw_dev->chrdev_request_id = request_id; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci full_command_packet = (TW_Command_Full *)&tw_ioctl->firmware_command; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* Load request id and sglist for both command types */ 77862306a36Sopenharmony_ci twl_load_sgl(tw_dev, full_command_packet, request_id, dma_handle, data_buffer_length_adjusted); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full)); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* Now post the command packet to the controller */ 78362306a36Sopenharmony_ci twl_post_command_packet(tw_dev, request_id); 78462306a36Sopenharmony_ci spin_unlock_irqrestore(tw_dev->host->host_lock, flags); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* Now wait for command to complete */ 78962306a36Sopenharmony_ci timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* We timed out, and didn't get an interrupt */ 79262306a36Sopenharmony_ci if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { 79362306a36Sopenharmony_ci /* Now we need to reset the board */ 79462306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n", 79562306a36Sopenharmony_ci tw_dev->host->host_no, TW_DRIVER, 0x6, 79662306a36Sopenharmony_ci cmd); 79762306a36Sopenharmony_ci retval = -EIO; 79862306a36Sopenharmony_ci twl_reset_device_extension(tw_dev, 1); 79962306a36Sopenharmony_ci goto out3; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* Now copy in the command packet response */ 80362306a36Sopenharmony_ci memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full)); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* Now complete the io */ 80662306a36Sopenharmony_ci spin_lock_irqsave(tw_dev->host->host_lock, flags); 80762306a36Sopenharmony_ci tw_dev->posted_request_count--; 80862306a36Sopenharmony_ci tw_dev->state[request_id] = TW_S_COMPLETED; 80962306a36Sopenharmony_ci twl_free_request_id(tw_dev, request_id); 81062306a36Sopenharmony_ci spin_unlock_irqrestore(tw_dev->host->host_lock, flags); 81162306a36Sopenharmony_ci break; 81262306a36Sopenharmony_ci default: 81362306a36Sopenharmony_ci retval = -ENOTTY; 81462306a36Sopenharmony_ci goto out3; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* Now copy the entire response to userspace */ 81862306a36Sopenharmony_ci if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length) == 0) 81962306a36Sopenharmony_ci retval = 0; 82062306a36Sopenharmony_ciout3: 82162306a36Sopenharmony_ci /* Now free ioctl buf memory */ 82262306a36Sopenharmony_ci dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted + sizeof(TW_Ioctl_Buf_Apache), cpu_addr, dma_handle); 82362306a36Sopenharmony_ciout2: 82462306a36Sopenharmony_ci mutex_unlock(&tw_dev->ioctl_lock); 82562306a36Sopenharmony_ciout: 82662306a36Sopenharmony_ci mutex_unlock(&twl_chrdev_mutex); 82762306a36Sopenharmony_ci return retval; 82862306a36Sopenharmony_ci} /* End twl_chrdev_ioctl() */ 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci/* This function handles open for the character device */ 83162306a36Sopenharmony_cistatic int twl_chrdev_open(struct inode *inode, struct file *file) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci unsigned int minor_number; 83462306a36Sopenharmony_ci int retval = -ENODEV; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) { 83762306a36Sopenharmony_ci retval = -EACCES; 83862306a36Sopenharmony_ci goto out; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci minor_number = iminor(inode); 84262306a36Sopenharmony_ci if (minor_number >= twl_device_extension_count) 84362306a36Sopenharmony_ci goto out; 84462306a36Sopenharmony_ci retval = 0; 84562306a36Sopenharmony_ciout: 84662306a36Sopenharmony_ci return retval; 84762306a36Sopenharmony_ci} /* End twl_chrdev_open() */ 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci/* File operations struct for character device */ 85062306a36Sopenharmony_cistatic const struct file_operations twl_fops = { 85162306a36Sopenharmony_ci .owner = THIS_MODULE, 85262306a36Sopenharmony_ci .unlocked_ioctl = twl_chrdev_ioctl, 85362306a36Sopenharmony_ci .open = twl_chrdev_open, 85462306a36Sopenharmony_ci .release = NULL, 85562306a36Sopenharmony_ci .llseek = noop_llseek, 85662306a36Sopenharmony_ci}; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci/* This function passes sense data from firmware to scsi layer */ 85962306a36Sopenharmony_cistatic int twl_fill_sense(TW_Device_Extension *tw_dev, int i, int request_id, int copy_sense, int print_host) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci TW_Command_Apache_Header *header; 86262306a36Sopenharmony_ci TW_Command_Full *full_command_packet; 86362306a36Sopenharmony_ci unsigned short error; 86462306a36Sopenharmony_ci char *error_str; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci header = tw_dev->sense_buffer_virt[i]; 86762306a36Sopenharmony_ci full_command_packet = tw_dev->command_packet_virt[request_id]; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci /* Get embedded firmware error string */ 87062306a36Sopenharmony_ci error_str = &(header->err_specific_desc[strlen(header->err_specific_desc) + 1]); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* Don't print error for Logical unit not supported during rollcall */ 87362306a36Sopenharmony_ci error = le16_to_cpu(header->status_block.error); 87462306a36Sopenharmony_ci if ((error != TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) && (error != TW_ERROR_UNIT_OFFLINE) && (error != TW_ERROR_INVALID_FIELD_IN_CDB)) { 87562306a36Sopenharmony_ci if (print_host) 87662306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n", 87762306a36Sopenharmony_ci tw_dev->host->host_no, 87862306a36Sopenharmony_ci TW_MESSAGE_SOURCE_CONTROLLER_ERROR, 87962306a36Sopenharmony_ci header->status_block.error, 88062306a36Sopenharmony_ci error_str, 88162306a36Sopenharmony_ci header->err_specific_desc); 88262306a36Sopenharmony_ci else 88362306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas: ERROR: (0x%02X:0x%04X): %s:%s.\n", 88462306a36Sopenharmony_ci TW_MESSAGE_SOURCE_CONTROLLER_ERROR, 88562306a36Sopenharmony_ci header->status_block.error, 88662306a36Sopenharmony_ci error_str, 88762306a36Sopenharmony_ci header->err_specific_desc); 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci if (copy_sense) { 89162306a36Sopenharmony_ci memcpy(tw_dev->srb[request_id]->sense_buffer, header->sense_data, TW_SENSE_DATA_LENGTH); 89262306a36Sopenharmony_ci tw_dev->srb[request_id]->result = (full_command_packet->command.newcommand.status << 1); 89362306a36Sopenharmony_ci goto out; 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ciout: 89662306a36Sopenharmony_ci return 1; 89762306a36Sopenharmony_ci} /* End twl_fill_sense() */ 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/* This function will free up device extension resources */ 90062306a36Sopenharmony_cistatic void twl_free_device_extension(TW_Device_Extension *tw_dev) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci if (tw_dev->command_packet_virt[0]) 90362306a36Sopenharmony_ci dma_free_coherent(&tw_dev->tw_pci_dev->dev, 90462306a36Sopenharmony_ci sizeof(TW_Command_Full)*TW_Q_LENGTH, 90562306a36Sopenharmony_ci tw_dev->command_packet_virt[0], 90662306a36Sopenharmony_ci tw_dev->command_packet_phys[0]); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (tw_dev->generic_buffer_virt[0]) 90962306a36Sopenharmony_ci dma_free_coherent(&tw_dev->tw_pci_dev->dev, 91062306a36Sopenharmony_ci TW_SECTOR_SIZE*TW_Q_LENGTH, 91162306a36Sopenharmony_ci tw_dev->generic_buffer_virt[0], 91262306a36Sopenharmony_ci tw_dev->generic_buffer_phys[0]); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (tw_dev->sense_buffer_virt[0]) 91562306a36Sopenharmony_ci dma_free_coherent(&tw_dev->tw_pci_dev->dev, 91662306a36Sopenharmony_ci sizeof(TW_Command_Apache_Header)* 91762306a36Sopenharmony_ci TW_Q_LENGTH, 91862306a36Sopenharmony_ci tw_dev->sense_buffer_virt[0], 91962306a36Sopenharmony_ci tw_dev->sense_buffer_phys[0]); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci kfree(tw_dev->event_queue[0]); 92262306a36Sopenharmony_ci} /* End twl_free_device_extension() */ 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci/* This function will get parameter table entries from the firmware */ 92562306a36Sopenharmony_cistatic void *twl_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci TW_Command_Full *full_command_packet; 92862306a36Sopenharmony_ci TW_Command *command_packet; 92962306a36Sopenharmony_ci TW_Param_Apache *param; 93062306a36Sopenharmony_ci void *retval = NULL; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* Setup the command packet */ 93362306a36Sopenharmony_ci full_command_packet = tw_dev->command_packet_virt[request_id]; 93462306a36Sopenharmony_ci memset(full_command_packet, 0, sizeof(TW_Command_Full)); 93562306a36Sopenharmony_ci command_packet = &full_command_packet->command.oldcommand; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM); 93862306a36Sopenharmony_ci command_packet->size = TW_COMMAND_SIZE; 93962306a36Sopenharmony_ci command_packet->request_id = request_id; 94062306a36Sopenharmony_ci command_packet->byte6_offset.block_count = cpu_to_le16(1); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci /* Now setup the param */ 94362306a36Sopenharmony_ci param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id]; 94462306a36Sopenharmony_ci memset(param, 0, TW_SECTOR_SIZE); 94562306a36Sopenharmony_ci param->table_id = cpu_to_le16(table_id | 0x8000); 94662306a36Sopenharmony_ci param->parameter_id = cpu_to_le16(parameter_id); 94762306a36Sopenharmony_ci param->parameter_size_bytes = cpu_to_le16(parameter_size_bytes); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]); 95062306a36Sopenharmony_ci command_packet->byte8_offset.param.sgl[0].length = TW_CPU_TO_SGL(TW_SECTOR_SIZE); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* Post the command packet to the board */ 95362306a36Sopenharmony_ci twl_post_command_packet(tw_dev, request_id); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* Poll for completion */ 95662306a36Sopenharmony_ci if (twl_poll_response(tw_dev, request_id, 30)) 95762306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x7, "No valid response during get param") 95862306a36Sopenharmony_ci else 95962306a36Sopenharmony_ci retval = (void *)&(param->data[0]); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci tw_dev->posted_request_count--; 96262306a36Sopenharmony_ci tw_dev->state[request_id] = TW_S_INITIAL; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci return retval; 96562306a36Sopenharmony_ci} /* End twl_get_param() */ 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci/* This function will send an initconnection command to controller */ 96862306a36Sopenharmony_cistatic int twl_initconnection(TW_Device_Extension *tw_dev, int message_credits, 96962306a36Sopenharmony_ci u32 set_features, unsigned short current_fw_srl, 97062306a36Sopenharmony_ci unsigned short current_fw_arch_id, 97162306a36Sopenharmony_ci unsigned short current_fw_branch, 97262306a36Sopenharmony_ci unsigned short current_fw_build, 97362306a36Sopenharmony_ci unsigned short *fw_on_ctlr_srl, 97462306a36Sopenharmony_ci unsigned short *fw_on_ctlr_arch_id, 97562306a36Sopenharmony_ci unsigned short *fw_on_ctlr_branch, 97662306a36Sopenharmony_ci unsigned short *fw_on_ctlr_build, 97762306a36Sopenharmony_ci u32 *init_connect_result) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci TW_Command_Full *full_command_packet; 98062306a36Sopenharmony_ci TW_Initconnect *tw_initconnect; 98162306a36Sopenharmony_ci int request_id = 0, retval = 1; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* Initialize InitConnection command packet */ 98462306a36Sopenharmony_ci full_command_packet = tw_dev->command_packet_virt[request_id]; 98562306a36Sopenharmony_ci memset(full_command_packet, 0, sizeof(TW_Command_Full)); 98662306a36Sopenharmony_ci full_command_packet->header.header_desc.size_header = 128; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand; 98962306a36Sopenharmony_ci tw_initconnect->opcode__reserved = TW_OPRES_IN(0, TW_OP_INIT_CONNECTION); 99062306a36Sopenharmony_ci tw_initconnect->request_id = request_id; 99162306a36Sopenharmony_ci tw_initconnect->message_credits = cpu_to_le16(message_credits); 99262306a36Sopenharmony_ci tw_initconnect->features = set_features; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* Turn on 64-bit sgl support if we need to */ 99562306a36Sopenharmony_ci tw_initconnect->features |= sizeof(dma_addr_t) > 4 ? 1 : 0; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci tw_initconnect->features = cpu_to_le32(tw_initconnect->features); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (set_features & TW_EXTENDED_INIT_CONNECT) { 100062306a36Sopenharmony_ci tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE_EXTENDED; 100162306a36Sopenharmony_ci tw_initconnect->fw_srl = cpu_to_le16(current_fw_srl); 100262306a36Sopenharmony_ci tw_initconnect->fw_arch_id = cpu_to_le16(current_fw_arch_id); 100362306a36Sopenharmony_ci tw_initconnect->fw_branch = cpu_to_le16(current_fw_branch); 100462306a36Sopenharmony_ci tw_initconnect->fw_build = cpu_to_le16(current_fw_build); 100562306a36Sopenharmony_ci } else 100662306a36Sopenharmony_ci tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci /* Send command packet to the board */ 100962306a36Sopenharmony_ci twl_post_command_packet(tw_dev, request_id); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* Poll for completion */ 101262306a36Sopenharmony_ci if (twl_poll_response(tw_dev, request_id, 30)) { 101362306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x8, "No valid response during init connection"); 101462306a36Sopenharmony_ci } else { 101562306a36Sopenharmony_ci if (set_features & TW_EXTENDED_INIT_CONNECT) { 101662306a36Sopenharmony_ci *fw_on_ctlr_srl = le16_to_cpu(tw_initconnect->fw_srl); 101762306a36Sopenharmony_ci *fw_on_ctlr_arch_id = le16_to_cpu(tw_initconnect->fw_arch_id); 101862306a36Sopenharmony_ci *fw_on_ctlr_branch = le16_to_cpu(tw_initconnect->fw_branch); 101962306a36Sopenharmony_ci *fw_on_ctlr_build = le16_to_cpu(tw_initconnect->fw_build); 102062306a36Sopenharmony_ci *init_connect_result = le32_to_cpu(tw_initconnect->result); 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci retval = 0; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci tw_dev->posted_request_count--; 102662306a36Sopenharmony_ci tw_dev->state[request_id] = TW_S_INITIAL; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci return retval; 102962306a36Sopenharmony_ci} /* End twl_initconnection() */ 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci/* This function will initialize the fields of a device extension */ 103262306a36Sopenharmony_cistatic int twl_initialize_device_extension(TW_Device_Extension *tw_dev) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci int i, retval = 1; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci /* Initialize command packet buffers */ 103762306a36Sopenharmony_ci if (twl_allocate_memory(tw_dev, sizeof(TW_Command_Full), 0)) { 103862306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x9, "Command packet memory allocation failed"); 103962306a36Sopenharmony_ci goto out; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* Initialize generic buffer */ 104362306a36Sopenharmony_ci if (twl_allocate_memory(tw_dev, TW_SECTOR_SIZE, 1)) { 104462306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0xa, "Generic memory allocation failed"); 104562306a36Sopenharmony_ci goto out; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* Allocate sense buffers */ 104962306a36Sopenharmony_ci if (twl_allocate_memory(tw_dev, sizeof(TW_Command_Apache_Header), 2)) { 105062306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0xb, "Sense buffer allocation failed"); 105162306a36Sopenharmony_ci goto out; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci /* Allocate event info space */ 105562306a36Sopenharmony_ci tw_dev->event_queue[0] = kcalloc(TW_Q_LENGTH, sizeof(TW_Event), GFP_KERNEL); 105662306a36Sopenharmony_ci if (!tw_dev->event_queue[0]) { 105762306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0xc, "Event info memory allocation failed"); 105862306a36Sopenharmony_ci goto out; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci for (i = 0; i < TW_Q_LENGTH; i++) { 106262306a36Sopenharmony_ci tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0] + (i * sizeof(TW_Event))); 106362306a36Sopenharmony_ci tw_dev->free_queue[i] = i; 106462306a36Sopenharmony_ci tw_dev->state[i] = TW_S_INITIAL; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci tw_dev->free_head = TW_Q_START; 106862306a36Sopenharmony_ci tw_dev->free_tail = TW_Q_START; 106962306a36Sopenharmony_ci tw_dev->error_sequence_id = 1; 107062306a36Sopenharmony_ci tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci mutex_init(&tw_dev->ioctl_lock); 107362306a36Sopenharmony_ci init_waitqueue_head(&tw_dev->ioctl_wqueue); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci retval = 0; 107662306a36Sopenharmony_ciout: 107762306a36Sopenharmony_ci return retval; 107862306a36Sopenharmony_ci} /* End twl_initialize_device_extension() */ 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci/* This function will handle attention interrupts */ 108162306a36Sopenharmony_cistatic int twl_handle_attention_interrupt(TW_Device_Extension *tw_dev) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci int retval = 1; 108462306a36Sopenharmony_ci u32 request_id, doorbell; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* Read doorbell status */ 108762306a36Sopenharmony_ci doorbell = readl(TWL_HOBDB_REG_ADDR(tw_dev)); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* Check for controller errors */ 109062306a36Sopenharmony_ci if (doorbell & TWL_DOORBELL_CONTROLLER_ERROR) { 109162306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0xd, "Microcontroller Error: clearing"); 109262306a36Sopenharmony_ci goto out; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci /* Check if we need to perform an AEN drain */ 109662306a36Sopenharmony_ci if (doorbell & TWL_DOORBELL_ATTENTION_INTERRUPT) { 109762306a36Sopenharmony_ci if (!(test_and_set_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags))) { 109862306a36Sopenharmony_ci twl_get_request_id(tw_dev, &request_id); 109962306a36Sopenharmony_ci if (twl_aen_read_queue(tw_dev, request_id)) { 110062306a36Sopenharmony_ci tw_dev->state[request_id] = TW_S_COMPLETED; 110162306a36Sopenharmony_ci twl_free_request_id(tw_dev, request_id); 110262306a36Sopenharmony_ci clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags); 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci retval = 0; 110862306a36Sopenharmony_ciout: 110962306a36Sopenharmony_ci /* Clear doorbell interrupt */ 111062306a36Sopenharmony_ci TWL_CLEAR_DB_INTERRUPT(tw_dev); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci /* Make sure the clear was flushed by reading it back */ 111362306a36Sopenharmony_ci readl(TWL_HOBDBC_REG_ADDR(tw_dev)); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci return retval; 111662306a36Sopenharmony_ci} /* End twl_handle_attention_interrupt() */ 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci/* Interrupt service routine */ 111962306a36Sopenharmony_cistatic irqreturn_t twl_interrupt(int irq, void *dev_instance) 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance; 112262306a36Sopenharmony_ci int i, handled = 0, error = 0; 112362306a36Sopenharmony_ci dma_addr_t mfa = 0; 112462306a36Sopenharmony_ci u32 reg, regl, regh, response, request_id = 0; 112562306a36Sopenharmony_ci struct scsi_cmnd *cmd; 112662306a36Sopenharmony_ci TW_Command_Full *full_command_packet; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci spin_lock(tw_dev->host->host_lock); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci /* Read host interrupt status */ 113162306a36Sopenharmony_ci reg = readl(TWL_HISTAT_REG_ADDR(tw_dev)); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci /* Check if this is our interrupt, otherwise bail */ 113462306a36Sopenharmony_ci if (!(reg & TWL_HISTATUS_VALID_INTERRUPT)) 113562306a36Sopenharmony_ci goto twl_interrupt_bail; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci handled = 1; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci /* If we are resetting, bail */ 114062306a36Sopenharmony_ci if (test_bit(TW_IN_RESET, &tw_dev->flags)) 114162306a36Sopenharmony_ci goto twl_interrupt_bail; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* Attention interrupt */ 114462306a36Sopenharmony_ci if (reg & TWL_HISTATUS_ATTENTION_INTERRUPT) { 114562306a36Sopenharmony_ci if (twl_handle_attention_interrupt(tw_dev)) { 114662306a36Sopenharmony_ci TWL_MASK_INTERRUPTS(tw_dev); 114762306a36Sopenharmony_ci goto twl_interrupt_bail; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci /* Response interrupt */ 115262306a36Sopenharmony_ci while (reg & TWL_HISTATUS_RESPONSE_INTERRUPT) { 115362306a36Sopenharmony_ci if (sizeof(dma_addr_t) > 4) { 115462306a36Sopenharmony_ci regh = readl(TWL_HOBQPH_REG_ADDR(tw_dev)); 115562306a36Sopenharmony_ci regl = readl(TWL_HOBQPL_REG_ADDR(tw_dev)); 115662306a36Sopenharmony_ci mfa = ((u64)regh << 32) | regl; 115762306a36Sopenharmony_ci } else 115862306a36Sopenharmony_ci mfa = readl(TWL_HOBQPL_REG_ADDR(tw_dev)); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci error = 0; 116162306a36Sopenharmony_ci response = (u32)mfa; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* Check for command packet error */ 116462306a36Sopenharmony_ci if (!TW_NOTMFA_OUT(response)) { 116562306a36Sopenharmony_ci for (i=0;i<TW_Q_LENGTH;i++) { 116662306a36Sopenharmony_ci if (tw_dev->sense_buffer_phys[i] == mfa) { 116762306a36Sopenharmony_ci request_id = le16_to_cpu(tw_dev->sense_buffer_virt[i]->header_desc.request_id); 116862306a36Sopenharmony_ci if (tw_dev->srb[request_id] != NULL) 116962306a36Sopenharmony_ci error = twl_fill_sense(tw_dev, i, request_id, 1, 1); 117062306a36Sopenharmony_ci else { 117162306a36Sopenharmony_ci /* Skip ioctl error prints */ 117262306a36Sopenharmony_ci if (request_id != tw_dev->chrdev_request_id) 117362306a36Sopenharmony_ci error = twl_fill_sense(tw_dev, i, request_id, 0, 1); 117462306a36Sopenharmony_ci else 117562306a36Sopenharmony_ci memcpy(tw_dev->command_packet_virt[request_id], tw_dev->sense_buffer_virt[i], sizeof(TW_Command_Apache_Header)); 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci /* Now re-post the sense buffer */ 117962306a36Sopenharmony_ci writel((u32)((u64)tw_dev->sense_buffer_phys[i] >> 32), TWL_HOBQPH_REG_ADDR(tw_dev)); 118062306a36Sopenharmony_ci writel((u32)tw_dev->sense_buffer_phys[i], TWL_HOBQPL_REG_ADDR(tw_dev)); 118162306a36Sopenharmony_ci break; 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci } else 118562306a36Sopenharmony_ci request_id = TW_RESID_OUT(response); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci full_command_packet = tw_dev->command_packet_virt[request_id]; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci /* Check for correct state */ 119062306a36Sopenharmony_ci if (tw_dev->state[request_id] != TW_S_POSTED) { 119162306a36Sopenharmony_ci if (tw_dev->srb[request_id] != NULL) { 119262306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Received a request id that wasn't posted"); 119362306a36Sopenharmony_ci TWL_MASK_INTERRUPTS(tw_dev); 119462306a36Sopenharmony_ci goto twl_interrupt_bail; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci /* Check for internal command completion */ 119962306a36Sopenharmony_ci if (tw_dev->srb[request_id] == NULL) { 120062306a36Sopenharmony_ci if (request_id != tw_dev->chrdev_request_id) { 120162306a36Sopenharmony_ci if (twl_aen_complete(tw_dev, request_id)) 120262306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0xf, "Error completing AEN during attention interrupt"); 120362306a36Sopenharmony_ci } else { 120462306a36Sopenharmony_ci tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; 120562306a36Sopenharmony_ci wake_up(&tw_dev->ioctl_wqueue); 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci } else { 120862306a36Sopenharmony_ci cmd = tw_dev->srb[request_id]; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (!error) 121162306a36Sopenharmony_ci cmd->result = (DID_OK << 16); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci /* Report residual bytes for single sgl */ 121462306a36Sopenharmony_ci if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) { 121562306a36Sopenharmony_ci if (full_command_packet->command.newcommand.sg_list[0].length < scsi_bufflen(tw_dev->srb[request_id])) 121662306a36Sopenharmony_ci scsi_set_resid(cmd, scsi_bufflen(cmd) - full_command_packet->command.newcommand.sg_list[0].length); 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* Now complete the io */ 122062306a36Sopenharmony_ci scsi_dma_unmap(cmd); 122162306a36Sopenharmony_ci scsi_done(cmd); 122262306a36Sopenharmony_ci tw_dev->state[request_id] = TW_S_COMPLETED; 122362306a36Sopenharmony_ci twl_free_request_id(tw_dev, request_id); 122462306a36Sopenharmony_ci tw_dev->posted_request_count--; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci /* Check for another response interrupt */ 122862306a36Sopenharmony_ci reg = readl(TWL_HISTAT_REG_ADDR(tw_dev)); 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_citwl_interrupt_bail: 123262306a36Sopenharmony_ci spin_unlock(tw_dev->host->host_lock); 123362306a36Sopenharmony_ci return IRQ_RETVAL(handled); 123462306a36Sopenharmony_ci} /* End twl_interrupt() */ 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci/* This function will poll for a register change */ 123762306a36Sopenharmony_cistatic int twl_poll_register(TW_Device_Extension *tw_dev, void *reg, u32 value, u32 result, int seconds) 123862306a36Sopenharmony_ci{ 123962306a36Sopenharmony_ci unsigned long before; 124062306a36Sopenharmony_ci int retval = 1; 124162306a36Sopenharmony_ci u32 reg_value; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci reg_value = readl(reg); 124462306a36Sopenharmony_ci before = jiffies; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci while ((reg_value & value) != result) { 124762306a36Sopenharmony_ci reg_value = readl(reg); 124862306a36Sopenharmony_ci if (time_after(jiffies, before + HZ * seconds)) 124962306a36Sopenharmony_ci goto out; 125062306a36Sopenharmony_ci msleep(50); 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci retval = 0; 125362306a36Sopenharmony_ciout: 125462306a36Sopenharmony_ci return retval; 125562306a36Sopenharmony_ci} /* End twl_poll_register() */ 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci/* This function will reset a controller */ 125862306a36Sopenharmony_cistatic int twl_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci int retval = 1; 126162306a36Sopenharmony_ci int i = 0; 126262306a36Sopenharmony_ci u32 status = 0; 126362306a36Sopenharmony_ci unsigned short fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0; 126462306a36Sopenharmony_ci unsigned short fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0; 126562306a36Sopenharmony_ci u32 init_connect_result = 0; 126662306a36Sopenharmony_ci int tries = 0; 126762306a36Sopenharmony_ci int do_soft_reset = soft_reset; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci while (tries < TW_MAX_RESET_TRIES) { 127062306a36Sopenharmony_ci /* Do a soft reset if one is needed */ 127162306a36Sopenharmony_ci if (do_soft_reset) { 127262306a36Sopenharmony_ci TWL_SOFT_RESET(tw_dev); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci /* Make sure controller is in a good state */ 127562306a36Sopenharmony_ci if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, 0x0, 30)) { 127662306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x10, "Controller never went non-ready during reset sequence"); 127762306a36Sopenharmony_ci tries++; 127862306a36Sopenharmony_ci continue; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, TWL_CONTROLLER_READY, 60)) { 128162306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x11, "Controller not ready during reset sequence"); 128262306a36Sopenharmony_ci tries++; 128362306a36Sopenharmony_ci continue; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci /* Initconnect */ 128862306a36Sopenharmony_ci if (twl_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS, 128962306a36Sopenharmony_ci TW_EXTENDED_INIT_CONNECT, TW_CURRENT_DRIVER_SRL, 129062306a36Sopenharmony_ci TW_9750_ARCH_ID, TW_CURRENT_DRIVER_BRANCH, 129162306a36Sopenharmony_ci TW_CURRENT_DRIVER_BUILD, &fw_on_ctlr_srl, 129262306a36Sopenharmony_ci &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, 129362306a36Sopenharmony_ci &fw_on_ctlr_build, &init_connect_result)) { 129462306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x12, "Initconnection failed while checking SRL"); 129562306a36Sopenharmony_ci do_soft_reset = 1; 129662306a36Sopenharmony_ci tries++; 129762306a36Sopenharmony_ci continue; 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci /* Load sense buffers */ 130162306a36Sopenharmony_ci while (i < TW_Q_LENGTH) { 130262306a36Sopenharmony_ci writel((u32)((u64)tw_dev->sense_buffer_phys[i] >> 32), TWL_HOBQPH_REG_ADDR(tw_dev)); 130362306a36Sopenharmony_ci writel((u32)tw_dev->sense_buffer_phys[i], TWL_HOBQPL_REG_ADDR(tw_dev)); 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* Check status for over-run after each write */ 130662306a36Sopenharmony_ci status = readl(TWL_STATUS_REG_ADDR(tw_dev)); 130762306a36Sopenharmony_ci if (!(status & TWL_STATUS_OVERRUN_SUBMIT)) 130862306a36Sopenharmony_ci i++; 130962306a36Sopenharmony_ci } 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci /* Now check status */ 131262306a36Sopenharmony_ci status = readl(TWL_STATUS_REG_ADDR(tw_dev)); 131362306a36Sopenharmony_ci if (status) { 131462306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "Bad controller status after loading sense buffers"); 131562306a36Sopenharmony_ci do_soft_reset = 1; 131662306a36Sopenharmony_ci tries++; 131762306a36Sopenharmony_ci continue; 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci /* Drain the AEN queue */ 132162306a36Sopenharmony_ci if (twl_aen_drain_queue(tw_dev, soft_reset)) { 132262306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x14, "AEN drain failed during reset sequence"); 132362306a36Sopenharmony_ci do_soft_reset = 1; 132462306a36Sopenharmony_ci tries++; 132562306a36Sopenharmony_ci continue; 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci /* Load rest of compatibility struct */ 132962306a36Sopenharmony_ci strncpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION)); 133062306a36Sopenharmony_ci tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL; 133162306a36Sopenharmony_ci tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH; 133262306a36Sopenharmony_ci tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD; 133362306a36Sopenharmony_ci tw_dev->tw_compat_info.driver_srl_low = TW_BASE_FW_SRL; 133462306a36Sopenharmony_ci tw_dev->tw_compat_info.driver_branch_low = TW_BASE_FW_BRANCH; 133562306a36Sopenharmony_ci tw_dev->tw_compat_info.driver_build_low = TW_BASE_FW_BUILD; 133662306a36Sopenharmony_ci tw_dev->tw_compat_info.fw_on_ctlr_srl = fw_on_ctlr_srl; 133762306a36Sopenharmony_ci tw_dev->tw_compat_info.fw_on_ctlr_branch = fw_on_ctlr_branch; 133862306a36Sopenharmony_ci tw_dev->tw_compat_info.fw_on_ctlr_build = fw_on_ctlr_build; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci /* If we got here, controller is in a good state */ 134162306a36Sopenharmony_ci retval = 0; 134262306a36Sopenharmony_ci goto out; 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ciout: 134562306a36Sopenharmony_ci return retval; 134662306a36Sopenharmony_ci} /* End twl_reset_sequence() */ 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci/* This function will reset a device extension */ 134962306a36Sopenharmony_cistatic int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset) 135062306a36Sopenharmony_ci{ 135162306a36Sopenharmony_ci int i = 0, retval = 1; 135262306a36Sopenharmony_ci unsigned long flags = 0; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci /* Block SCSI requests while we are resetting */ 135562306a36Sopenharmony_ci if (ioctl_reset) 135662306a36Sopenharmony_ci scsi_block_requests(tw_dev->host); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci set_bit(TW_IN_RESET, &tw_dev->flags); 135962306a36Sopenharmony_ci TWL_MASK_INTERRUPTS(tw_dev); 136062306a36Sopenharmony_ci TWL_CLEAR_DB_INTERRUPT(tw_dev); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci spin_lock_irqsave(tw_dev->host->host_lock, flags); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* Abort all requests that are in progress */ 136562306a36Sopenharmony_ci for (i = 0; i < TW_Q_LENGTH; i++) { 136662306a36Sopenharmony_ci if ((tw_dev->state[i] != TW_S_FINISHED) && 136762306a36Sopenharmony_ci (tw_dev->state[i] != TW_S_INITIAL) && 136862306a36Sopenharmony_ci (tw_dev->state[i] != TW_S_COMPLETED)) { 136962306a36Sopenharmony_ci struct scsi_cmnd *cmd = tw_dev->srb[i]; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci if (cmd) { 137262306a36Sopenharmony_ci cmd->result = (DID_RESET << 16); 137362306a36Sopenharmony_ci scsi_dma_unmap(cmd); 137462306a36Sopenharmony_ci scsi_done(cmd); 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci } 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci /* Reset queues and counts */ 138062306a36Sopenharmony_ci for (i = 0; i < TW_Q_LENGTH; i++) { 138162306a36Sopenharmony_ci tw_dev->free_queue[i] = i; 138262306a36Sopenharmony_ci tw_dev->state[i] = TW_S_INITIAL; 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci tw_dev->free_head = TW_Q_START; 138562306a36Sopenharmony_ci tw_dev->free_tail = TW_Q_START; 138662306a36Sopenharmony_ci tw_dev->posted_request_count = 0; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci spin_unlock_irqrestore(tw_dev->host->host_lock, flags); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci if (twl_reset_sequence(tw_dev, 1)) 139162306a36Sopenharmony_ci goto out; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci TWL_UNMASK_INTERRUPTS(tw_dev); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci clear_bit(TW_IN_RESET, &tw_dev->flags); 139662306a36Sopenharmony_ci tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci retval = 0; 139962306a36Sopenharmony_ciout: 140062306a36Sopenharmony_ci if (ioctl_reset) 140162306a36Sopenharmony_ci scsi_unblock_requests(tw_dev->host); 140262306a36Sopenharmony_ci return retval; 140362306a36Sopenharmony_ci} /* End twl_reset_device_extension() */ 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci/* This funciton returns unit geometry in cylinders/heads/sectors */ 140662306a36Sopenharmony_cistatic int twl_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]) 140762306a36Sopenharmony_ci{ 140862306a36Sopenharmony_ci int heads, sectors; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci if (capacity >= 0x200000) { 141162306a36Sopenharmony_ci heads = 255; 141262306a36Sopenharmony_ci sectors = 63; 141362306a36Sopenharmony_ci } else { 141462306a36Sopenharmony_ci heads = 64; 141562306a36Sopenharmony_ci sectors = 32; 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci geom[0] = heads; 141962306a36Sopenharmony_ci geom[1] = sectors; 142062306a36Sopenharmony_ci geom[2] = sector_div(capacity, heads * sectors); /* cylinders */ 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci return 0; 142362306a36Sopenharmony_ci} /* End twl_scsi_biosparam() */ 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci/* This is the new scsi eh reset function */ 142662306a36Sopenharmony_cistatic int twl_scsi_eh_reset(struct scsi_cmnd *SCpnt) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci TW_Device_Extension *tw_dev = NULL; 142962306a36Sopenharmony_ci int retval = FAILED; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci tw_dev->num_resets++; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci sdev_printk(KERN_WARNING, SCpnt->device, 143662306a36Sopenharmony_ci "WARNING: (0x%02X:0x%04X): Command (0x%x) timed out, resetting card.\n", 143762306a36Sopenharmony_ci TW_DRIVER, 0x2c, SCpnt->cmnd[0]); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci /* Make sure we are not issuing an ioctl or resetting from ioctl */ 144062306a36Sopenharmony_ci mutex_lock(&tw_dev->ioctl_lock); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci /* Now reset the card and some of the device extension data */ 144362306a36Sopenharmony_ci if (twl_reset_device_extension(tw_dev, 0)) { 144462306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x15, "Controller reset failed during scsi host reset"); 144562306a36Sopenharmony_ci goto out; 144662306a36Sopenharmony_ci } 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci retval = SUCCESS; 144962306a36Sopenharmony_ciout: 145062306a36Sopenharmony_ci mutex_unlock(&tw_dev->ioctl_lock); 145162306a36Sopenharmony_ci return retval; 145262306a36Sopenharmony_ci} /* End twl_scsi_eh_reset() */ 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci/* This is the main scsi queue function to handle scsi opcodes */ 145562306a36Sopenharmony_cistatic int twl_scsi_queue_lck(struct scsi_cmnd *SCpnt) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci void (*done)(struct scsi_cmnd *) = scsi_done; 145862306a36Sopenharmony_ci int request_id, retval; 145962306a36Sopenharmony_ci TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci /* If we are resetting due to timed out ioctl, report as busy */ 146262306a36Sopenharmony_ci if (test_bit(TW_IN_RESET, &tw_dev->flags)) { 146362306a36Sopenharmony_ci retval = SCSI_MLQUEUE_HOST_BUSY; 146462306a36Sopenharmony_ci goto out; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci /* Get a free request id */ 146862306a36Sopenharmony_ci twl_get_request_id(tw_dev, &request_id); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci /* Save the scsi command for use by the ISR */ 147162306a36Sopenharmony_ci tw_dev->srb[request_id] = SCpnt; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci retval = twl_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL); 147462306a36Sopenharmony_ci if (retval) { 147562306a36Sopenharmony_ci tw_dev->state[request_id] = TW_S_COMPLETED; 147662306a36Sopenharmony_ci twl_free_request_id(tw_dev, request_id); 147762306a36Sopenharmony_ci SCpnt->result = (DID_ERROR << 16); 147862306a36Sopenharmony_ci done(SCpnt); 147962306a36Sopenharmony_ci retval = 0; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ciout: 148262306a36Sopenharmony_ci return retval; 148362306a36Sopenharmony_ci} /* End twl_scsi_queue() */ 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_cistatic DEF_SCSI_QCMD(twl_scsi_queue) 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci/* This function tells the controller to shut down */ 148862306a36Sopenharmony_cistatic void __twl_shutdown(TW_Device_Extension *tw_dev) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci /* Disable interrupts */ 149162306a36Sopenharmony_ci TWL_MASK_INTERRUPTS(tw_dev); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci /* Free up the IRQ */ 149462306a36Sopenharmony_ci free_irq(tw_dev->tw_pci_dev->irq, tw_dev); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas: Shutting down host %d.\n", tw_dev->host->host_no); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci /* Tell the card we are shutting down */ 149962306a36Sopenharmony_ci if (twl_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) { 150062306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x16, "Connection shutdown failed"); 150162306a36Sopenharmony_ci } else { 150262306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas: Shutdown complete.\n"); 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci /* Clear doorbell interrupt just before exit */ 150662306a36Sopenharmony_ci TWL_CLEAR_DB_INTERRUPT(tw_dev); 150762306a36Sopenharmony_ci} /* End __twl_shutdown() */ 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci/* Wrapper for __twl_shutdown */ 151062306a36Sopenharmony_cistatic void twl_shutdown(struct pci_dev *pdev) 151162306a36Sopenharmony_ci{ 151262306a36Sopenharmony_ci struct Scsi_Host *host = pci_get_drvdata(pdev); 151362306a36Sopenharmony_ci TW_Device_Extension *tw_dev; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci if (!host) 151662306a36Sopenharmony_ci return; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci tw_dev = (TW_Device_Extension *)host->hostdata; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci if (tw_dev->online) 152162306a36Sopenharmony_ci __twl_shutdown(tw_dev); 152262306a36Sopenharmony_ci} /* End twl_shutdown() */ 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci/* This function configures unit settings when a unit is coming on-line */ 152562306a36Sopenharmony_cistatic int twl_slave_configure(struct scsi_device *sdev) 152662306a36Sopenharmony_ci{ 152762306a36Sopenharmony_ci /* Force 60 second timeout */ 152862306a36Sopenharmony_ci blk_queue_rq_timeout(sdev->request_queue, 60 * HZ); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci return 0; 153162306a36Sopenharmony_ci} /* End twl_slave_configure() */ 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_cistatic const struct scsi_host_template driver_template = { 153462306a36Sopenharmony_ci .module = THIS_MODULE, 153562306a36Sopenharmony_ci .name = "3w-sas", 153662306a36Sopenharmony_ci .queuecommand = twl_scsi_queue, 153762306a36Sopenharmony_ci .eh_host_reset_handler = twl_scsi_eh_reset, 153862306a36Sopenharmony_ci .bios_param = twl_scsi_biosparam, 153962306a36Sopenharmony_ci .change_queue_depth = scsi_change_queue_depth, 154062306a36Sopenharmony_ci .can_queue = TW_Q_LENGTH-2, 154162306a36Sopenharmony_ci .slave_configure = twl_slave_configure, 154262306a36Sopenharmony_ci .this_id = -1, 154362306a36Sopenharmony_ci .sg_tablesize = TW_LIBERATOR_MAX_SGL_LENGTH, 154462306a36Sopenharmony_ci .max_sectors = TW_MAX_SECTORS, 154562306a36Sopenharmony_ci .cmd_per_lun = TW_MAX_CMDS_PER_LUN, 154662306a36Sopenharmony_ci .shost_groups = twl_host_groups, 154762306a36Sopenharmony_ci .emulated = 1, 154862306a36Sopenharmony_ci .no_write_same = 1, 154962306a36Sopenharmony_ci}; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci/* This function will probe and initialize a card */ 155262306a36Sopenharmony_cistatic int twl_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) 155362306a36Sopenharmony_ci{ 155462306a36Sopenharmony_ci struct Scsi_Host *host = NULL; 155562306a36Sopenharmony_ci TW_Device_Extension *tw_dev; 155662306a36Sopenharmony_ci int retval = -ENODEV; 155762306a36Sopenharmony_ci int *ptr_phycount, phycount=0; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci retval = pci_enable_device(pdev); 156062306a36Sopenharmony_ci if (retval) { 156162306a36Sopenharmony_ci TW_PRINTK(host, TW_DRIVER, 0x17, "Failed to enable pci device"); 156262306a36Sopenharmony_ci goto out_disable_device; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci pci_set_master(pdev); 156662306a36Sopenharmony_ci pci_try_set_mwi(pdev); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 156962306a36Sopenharmony_ci if (retval) { 157062306a36Sopenharmony_ci TW_PRINTK(host, TW_DRIVER, 0x18, "Failed to set dma mask"); 157162306a36Sopenharmony_ci retval = -ENODEV; 157262306a36Sopenharmony_ci goto out_disable_device; 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension)); 157662306a36Sopenharmony_ci if (!host) { 157762306a36Sopenharmony_ci TW_PRINTK(host, TW_DRIVER, 0x19, "Failed to allocate memory for device extension"); 157862306a36Sopenharmony_ci retval = -ENOMEM; 157962306a36Sopenharmony_ci goto out_disable_device; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci tw_dev = shost_priv(host); 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci /* Save values to device extension */ 158462306a36Sopenharmony_ci tw_dev->host = host; 158562306a36Sopenharmony_ci tw_dev->tw_pci_dev = pdev; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (twl_initialize_device_extension(tw_dev)) { 158862306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Failed to initialize device extension"); 158962306a36Sopenharmony_ci retval = -ENOMEM; 159062306a36Sopenharmony_ci goto out_free_device_extension; 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci /* Request IO regions */ 159462306a36Sopenharmony_ci retval = pci_request_regions(pdev, "3w-sas"); 159562306a36Sopenharmony_ci if (retval) { 159662306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Failed to get mem region"); 159762306a36Sopenharmony_ci goto out_free_device_extension; 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci /* Save base address, use region 1 */ 160162306a36Sopenharmony_ci tw_dev->base_addr = pci_iomap(pdev, 1, 0); 160262306a36Sopenharmony_ci if (!tw_dev->base_addr) { 160362306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to ioremap"); 160462306a36Sopenharmony_ci retval = -ENOMEM; 160562306a36Sopenharmony_ci goto out_release_mem_region; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci /* Disable interrupts on the card */ 160962306a36Sopenharmony_ci TWL_MASK_INTERRUPTS(tw_dev); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci /* Initialize the card */ 161262306a36Sopenharmony_ci if (twl_reset_sequence(tw_dev, 0)) { 161362306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Controller reset failed during probe"); 161462306a36Sopenharmony_ci retval = -ENOMEM; 161562306a36Sopenharmony_ci goto out_iounmap; 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci /* Set host specific parameters */ 161962306a36Sopenharmony_ci host->max_id = TW_MAX_UNITS; 162062306a36Sopenharmony_ci host->max_cmd_len = TW_MAX_CDB_LEN; 162162306a36Sopenharmony_ci host->max_lun = TW_MAX_LUNS; 162262306a36Sopenharmony_ci host->max_channel = 0; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci /* Register the card with the kernel SCSI layer */ 162562306a36Sopenharmony_ci retval = scsi_add_host(host, &pdev->dev); 162662306a36Sopenharmony_ci if (retval) { 162762306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "scsi add host failed"); 162862306a36Sopenharmony_ci goto out_iounmap; 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci pci_set_drvdata(pdev, host); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas: scsi%d: Found an LSI 3ware %s Controller at 0x%llx, IRQ: %d.\n", 163462306a36Sopenharmony_ci host->host_no, 163562306a36Sopenharmony_ci (char *)twl_get_param(tw_dev, 1, TW_VERSION_TABLE, 163662306a36Sopenharmony_ci TW_PARAM_MODEL, TW_PARAM_MODEL_LENGTH), 163762306a36Sopenharmony_ci (u64)pci_resource_start(pdev, 1), pdev->irq); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci ptr_phycount = twl_get_param(tw_dev, 2, TW_PARAM_PHY_SUMMARY_TABLE, 164062306a36Sopenharmony_ci TW_PARAM_PHYCOUNT, TW_PARAM_PHYCOUNT_LENGTH); 164162306a36Sopenharmony_ci if (ptr_phycount) 164262306a36Sopenharmony_ci phycount = le32_to_cpu(*(int *)ptr_phycount); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas: scsi%d: Firmware %s, BIOS %s, Phys: %d.\n", 164562306a36Sopenharmony_ci host->host_no, 164662306a36Sopenharmony_ci (char *)twl_get_param(tw_dev, 1, TW_VERSION_TABLE, 164762306a36Sopenharmony_ci TW_PARAM_FWVER, TW_PARAM_FWVER_LENGTH), 164862306a36Sopenharmony_ci (char *)twl_get_param(tw_dev, 2, TW_VERSION_TABLE, 164962306a36Sopenharmony_ci TW_PARAM_BIOSVER, TW_PARAM_BIOSVER_LENGTH), 165062306a36Sopenharmony_ci phycount); 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci /* Try to enable MSI */ 165362306a36Sopenharmony_ci if (use_msi && !pci_enable_msi(pdev)) 165462306a36Sopenharmony_ci set_bit(TW_USING_MSI, &tw_dev->flags); 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci /* Now setup the interrupt handler */ 165762306a36Sopenharmony_ci retval = request_irq(pdev->irq, twl_interrupt, IRQF_SHARED, "3w-sas", tw_dev); 165862306a36Sopenharmony_ci if (retval) { 165962306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1f, "Error requesting IRQ"); 166062306a36Sopenharmony_ci goto out_remove_host; 166162306a36Sopenharmony_ci } 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci twl_device_extension_list[twl_device_extension_count] = tw_dev; 166462306a36Sopenharmony_ci twl_device_extension_count++; 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci /* Re-enable interrupts on the card */ 166762306a36Sopenharmony_ci TWL_UNMASK_INTERRUPTS(tw_dev); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci /* Finally, scan the host */ 167062306a36Sopenharmony_ci scsi_scan_host(host); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci /* Add sysfs binary files */ 167362306a36Sopenharmony_ci if (sysfs_create_bin_file(&host->shost_dev.kobj, &twl_sysfs_aen_read_attr)) 167462306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x20, "Failed to create sysfs binary file: 3ware_aen_read"); 167562306a36Sopenharmony_ci if (sysfs_create_bin_file(&host->shost_dev.kobj, &twl_sysfs_compat_info_attr)) 167662306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x21, "Failed to create sysfs binary file: 3ware_compat_info"); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci if (twl_major == -1) { 167962306a36Sopenharmony_ci if ((twl_major = register_chrdev (0, "twl", &twl_fops)) < 0) 168062306a36Sopenharmony_ci TW_PRINTK(host, TW_DRIVER, 0x22, "Failed to register character device"); 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci tw_dev->online = 1; 168362306a36Sopenharmony_ci return 0; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ciout_remove_host: 168662306a36Sopenharmony_ci if (test_bit(TW_USING_MSI, &tw_dev->flags)) 168762306a36Sopenharmony_ci pci_disable_msi(pdev); 168862306a36Sopenharmony_ci scsi_remove_host(host); 168962306a36Sopenharmony_ciout_iounmap: 169062306a36Sopenharmony_ci iounmap(tw_dev->base_addr); 169162306a36Sopenharmony_ciout_release_mem_region: 169262306a36Sopenharmony_ci pci_release_regions(pdev); 169362306a36Sopenharmony_ciout_free_device_extension: 169462306a36Sopenharmony_ci twl_free_device_extension(tw_dev); 169562306a36Sopenharmony_ci scsi_host_put(host); 169662306a36Sopenharmony_ciout_disable_device: 169762306a36Sopenharmony_ci pci_disable_device(pdev); 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci return retval; 170062306a36Sopenharmony_ci} /* End twl_probe() */ 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci/* This function is called to remove a device */ 170362306a36Sopenharmony_cistatic void twl_remove(struct pci_dev *pdev) 170462306a36Sopenharmony_ci{ 170562306a36Sopenharmony_ci struct Scsi_Host *host = pci_get_drvdata(pdev); 170662306a36Sopenharmony_ci TW_Device_Extension *tw_dev; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci if (!host) 170962306a36Sopenharmony_ci return; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci tw_dev = (TW_Device_Extension *)host->hostdata; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci if (!tw_dev->online) 171462306a36Sopenharmony_ci return; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci /* Remove sysfs binary files */ 171762306a36Sopenharmony_ci sysfs_remove_bin_file(&host->shost_dev.kobj, &twl_sysfs_aen_read_attr); 171862306a36Sopenharmony_ci sysfs_remove_bin_file(&host->shost_dev.kobj, &twl_sysfs_compat_info_attr); 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci scsi_remove_host(tw_dev->host); 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci /* Unregister character device */ 172362306a36Sopenharmony_ci if (twl_major >= 0) { 172462306a36Sopenharmony_ci unregister_chrdev(twl_major, "twl"); 172562306a36Sopenharmony_ci twl_major = -1; 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci /* Shutdown the card */ 172962306a36Sopenharmony_ci __twl_shutdown(tw_dev); 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci /* Disable MSI if enabled */ 173262306a36Sopenharmony_ci if (test_bit(TW_USING_MSI, &tw_dev->flags)) 173362306a36Sopenharmony_ci pci_disable_msi(pdev); 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci /* Free IO remapping */ 173662306a36Sopenharmony_ci iounmap(tw_dev->base_addr); 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci /* Free up the mem region */ 173962306a36Sopenharmony_ci pci_release_regions(pdev); 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci /* Free up device extension resources */ 174262306a36Sopenharmony_ci twl_free_device_extension(tw_dev); 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci scsi_host_put(tw_dev->host); 174562306a36Sopenharmony_ci pci_disable_device(pdev); 174662306a36Sopenharmony_ci twl_device_extension_count--; 174762306a36Sopenharmony_ci} /* End twl_remove() */ 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci/* This function is called on PCI suspend */ 175062306a36Sopenharmony_cistatic int __maybe_unused twl_suspend(struct device *dev) 175162306a36Sopenharmony_ci{ 175262306a36Sopenharmony_ci struct Scsi_Host *host = dev_get_drvdata(dev); 175362306a36Sopenharmony_ci TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas: Suspending host %d.\n", tw_dev->host->host_no); 175662306a36Sopenharmony_ci /* Disable interrupts */ 175762306a36Sopenharmony_ci TWL_MASK_INTERRUPTS(tw_dev); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci free_irq(tw_dev->tw_pci_dev->irq, tw_dev); 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci /* Tell the card we are shutting down */ 176262306a36Sopenharmony_ci if (twl_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) { 176362306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x23, "Connection shutdown failed during suspend"); 176462306a36Sopenharmony_ci } else { 176562306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas: Suspend complete.\n"); 176662306a36Sopenharmony_ci } 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci /* Clear doorbell interrupt */ 176962306a36Sopenharmony_ci TWL_CLEAR_DB_INTERRUPT(tw_dev); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci return 0; 177262306a36Sopenharmony_ci} /* End twl_suspend() */ 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci/* This function is called on PCI resume */ 177562306a36Sopenharmony_cistatic int __maybe_unused twl_resume(struct device *dev) 177662306a36Sopenharmony_ci{ 177762306a36Sopenharmony_ci int retval = 0; 177862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 177962306a36Sopenharmony_ci struct Scsi_Host *host = pci_get_drvdata(pdev); 178062306a36Sopenharmony_ci TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas: Resuming host %d.\n", tw_dev->host->host_no); 178362306a36Sopenharmony_ci pci_try_set_mwi(pdev); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 178662306a36Sopenharmony_ci if (retval) { 178762306a36Sopenharmony_ci TW_PRINTK(host, TW_DRIVER, 0x25, "Failed to set dma mask during resume"); 178862306a36Sopenharmony_ci retval = -ENODEV; 178962306a36Sopenharmony_ci goto out_disable_device; 179062306a36Sopenharmony_ci } 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci /* Initialize the card */ 179362306a36Sopenharmony_ci if (twl_reset_sequence(tw_dev, 0)) { 179462306a36Sopenharmony_ci retval = -ENODEV; 179562306a36Sopenharmony_ci goto out_disable_device; 179662306a36Sopenharmony_ci } 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci /* Now setup the interrupt handler */ 179962306a36Sopenharmony_ci retval = request_irq(pdev->irq, twl_interrupt, IRQF_SHARED, "3w-sas", tw_dev); 180062306a36Sopenharmony_ci if (retval) { 180162306a36Sopenharmony_ci TW_PRINTK(tw_dev->host, TW_DRIVER, 0x26, "Error requesting IRQ during resume"); 180262306a36Sopenharmony_ci retval = -ENODEV; 180362306a36Sopenharmony_ci goto out_disable_device; 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci /* Now enable MSI if enabled */ 180762306a36Sopenharmony_ci if (test_bit(TW_USING_MSI, &tw_dev->flags)) 180862306a36Sopenharmony_ci pci_enable_msi(pdev); 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci /* Re-enable interrupts on the card */ 181162306a36Sopenharmony_ci TWL_UNMASK_INTERRUPTS(tw_dev); 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci printk(KERN_WARNING "3w-sas: Resume complete.\n"); 181462306a36Sopenharmony_ci return 0; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ciout_disable_device: 181762306a36Sopenharmony_ci scsi_remove_host(host); 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci return retval; 182062306a36Sopenharmony_ci} /* End twl_resume() */ 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci/* PCI Devices supported by this driver */ 182362306a36Sopenharmony_cistatic struct pci_device_id twl_pci_tbl[] = { 182462306a36Sopenharmony_ci { PCI_VDEVICE(3WARE, PCI_DEVICE_ID_3WARE_9750) }, 182562306a36Sopenharmony_ci { } 182662306a36Sopenharmony_ci}; 182762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, twl_pci_tbl); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(twl_pm_ops, twl_suspend, twl_resume); 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci/* pci_driver initializer */ 183262306a36Sopenharmony_cistatic struct pci_driver twl_driver = { 183362306a36Sopenharmony_ci .name = "3w-sas", 183462306a36Sopenharmony_ci .id_table = twl_pci_tbl, 183562306a36Sopenharmony_ci .probe = twl_probe, 183662306a36Sopenharmony_ci .remove = twl_remove, 183762306a36Sopenharmony_ci .driver.pm = &twl_pm_ops, 183862306a36Sopenharmony_ci .shutdown = twl_shutdown 183962306a36Sopenharmony_ci}; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci/* This function is called on driver initialization */ 184262306a36Sopenharmony_cistatic int __init twl_init(void) 184362306a36Sopenharmony_ci{ 184462306a36Sopenharmony_ci printk(KERN_INFO "LSI 3ware SAS/SATA-RAID Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION); 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci return pci_register_driver(&twl_driver); 184762306a36Sopenharmony_ci} /* End twl_init() */ 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci/* This function is called on driver exit */ 185062306a36Sopenharmony_cistatic void __exit twl_exit(void) 185162306a36Sopenharmony_ci{ 185262306a36Sopenharmony_ci pci_unregister_driver(&twl_driver); 185362306a36Sopenharmony_ci} /* End twl_exit() */ 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_cimodule_init(twl_init); 185662306a36Sopenharmony_cimodule_exit(twl_exit); 185762306a36Sopenharmony_ci 1858