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