[SCSI] aic94xx: new driver
This is the end point of the separate aic94xx driver based on the original driver and transport class from Luben Tuikov <ltuikov@yahoo.com> The log of the separate development is: Alexis Bruemmer: o aic94xx: fix hotplug/unplug for expanderless systems o aic94xx: disable split completion timer/setting by default o aic94xx: wide port off expander support o aic94xx: remove various inline functions o aic94xx: use bitops o aic94xx: remove queue comment o aic94xx: remove sas_common.c o aic94xx: sas remove depot's o aic94xx: use available list_for_each_entry_safe_reverse() o aic94xx: sas header file merge James Bottomley: o aic94xx: fix TF_TMF_NO_CTX processing o aic94xx: convert to request_firmware interface o aic94xx: fix hotplug/unplug o aic94xx: add link error counts to the expander phys o aic94xx: add transport class phy reset capability o aic94xx: remove local_attached flag o Remove README o Fixup Makefile variable for libsas rename o Rename sas->libsas o aic94xx: correct return code for sas_discover_event o aic94xx: use parent backlink port o aic94xx: remove channel abstraction o aic94xx: fix routing algorithms o aic94xx: add backlink port o aic94xx: fix cascaded expander properties o aic94xx: fix sleep under lock o aic94xx: fix panic on module removal in complex topology o aic94xx: make use of the new sas_port o rename sas_port to asd_sas_port o Fix for eh_strategy_handler move o aic94xx: move entirely over to correct transport class formulation o remove last vestages of sas_rphy_alloc() o update for eh_timed_out move o Preliminary expander support for aic94xx o sas: remove event thread o minor warning cleanups o remove last vestiges of id mapping arrays o Further updates o Convert aic94xx over entirely to the transport class end device and o update aic94xx/sas to use the new sas transport class end device o [PATCH] aic94xx: attaching to the sas transport class o Add missing completion removal from prior patch o [PATCH] aic94xx: attaching to the sas transport class o Build fixes from akpm Jeff Garzik: o [scsi aic94xx] Remove ->owner from PCI info table Luben Tuikov: o initial aic94xx driver Mike Anderson: o aic94xx: fix panic on module insertion o aic94xx: stub out SATA_DEV case o aic94xx: compile warning cleanups o aic94xx: sas_alloc_task o aic94xx: ref count update o aic94xx nexus loss time value o [PATCH] aic94xx: driver assertion in non-x86 BIOS env Randy Dunlap: o libsas: externs not needed Robert Tarte: o aic94xx: sequence patch - fixes SATA support Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
This commit is contained in:
committed by
James Bottomley
parent
f4ad7b5807
commit
2908d778ab
41
drivers/scsi/aic94xx/Kconfig
Normal file
41
drivers/scsi/aic94xx/Kconfig
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# Kernel configuration file for aic94xx SAS/SATA driver.
|
||||
#
|
||||
# Copyright (c) 2005 Adaptec, Inc. All rights reserved.
|
||||
# Copyright (c) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
#
|
||||
# This file is licensed under GPLv2.
|
||||
#
|
||||
# This file is part of the aic94xx driver.
|
||||
#
|
||||
# The aic94xx driver is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 of the
|
||||
# License.
|
||||
#
|
||||
# The aic94xx driver is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Aic94xx Driver; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
#
|
||||
|
||||
config SCSI_AIC94XX
|
||||
tristate "Adaptec AIC94xx SAS/SATA support"
|
||||
depends on PCI
|
||||
select SCSI_SAS_LIBSAS
|
||||
help
|
||||
This driver supports Adaptec's SAS/SATA 3Gb/s 64 bit PCI-X
|
||||
AIC94xx chip based host adapters.
|
||||
|
||||
config AIC94XX_DEBUG
|
||||
bool "Compile in debug mode"
|
||||
default y
|
||||
depends on SCSI_AIC94XX
|
||||
help
|
||||
Compiles the aic94xx driver in debug mode. In debug mode,
|
||||
the driver prints some messages to the console.
|
||||
39
drivers/scsi/aic94xx/Makefile
Normal file
39
drivers/scsi/aic94xx/Makefile
Normal file
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# Makefile for Adaptec aic94xx SAS/SATA driver.
|
||||
#
|
||||
# Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
# Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
#
|
||||
# This file is licensed under GPLv2.
|
||||
#
|
||||
# This file is part of the the aic94xx driver.
|
||||
#
|
||||
# The aic94xx driver is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 of the
|
||||
# License.
|
||||
#
|
||||
# The aic94xx driver is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with the aic94xx driver; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
ifeq ($(CONFIG_AIC94XX_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DASD_DEBUG -DASD_ENTER_EXIT
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_SCSI_AIC94XX) += aic94xx.o
|
||||
aic94xx-y += aic94xx_init.o \
|
||||
aic94xx_hwi.o \
|
||||
aic94xx_reg.o \
|
||||
aic94xx_sds.o \
|
||||
aic94xx_seq.o \
|
||||
aic94xx_dump.o \
|
||||
aic94xx_scb.o \
|
||||
aic94xx_dev.o \
|
||||
aic94xx_tmf.o \
|
||||
aic94xx_task.o
|
||||
114
drivers/scsi/aic94xx/aic94xx.h
Normal file
114
drivers/scsi/aic94xx/aic94xx.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver header file.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* $Id: //depot/aic94xx/aic94xx.h#31 $
|
||||
*/
|
||||
|
||||
#ifndef _AIC94XX_H_
|
||||
#define _AIC94XX_H_
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <scsi/libsas.h>
|
||||
|
||||
#define ASD_DRIVER_NAME "aic94xx"
|
||||
#define ASD_DRIVER_DESCRIPTION "Adaptec aic94xx SAS/SATA driver"
|
||||
|
||||
#define asd_printk(fmt, ...) printk(KERN_NOTICE ASD_DRIVER_NAME ": " fmt, ## __VA_ARGS__)
|
||||
|
||||
#ifdef ASD_ENTER_EXIT
|
||||
#define ENTER printk(KERN_NOTICE "%s: ENTER %s\n", ASD_DRIVER_NAME, \
|
||||
__FUNCTION__)
|
||||
#define EXIT printk(KERN_NOTICE "%s: --EXIT %s\n", ASD_DRIVER_NAME, \
|
||||
__FUNCTION__)
|
||||
#else
|
||||
#define ENTER
|
||||
#define EXIT
|
||||
#endif
|
||||
|
||||
#ifdef ASD_DEBUG
|
||||
#define ASD_DPRINTK asd_printk
|
||||
#else
|
||||
#define ASD_DPRINTK(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* 2*ITNL timeout + 1 second */
|
||||
#define AIC94XX_SCB_TIMEOUT (5*HZ)
|
||||
|
||||
extern kmem_cache_t *asd_dma_token_cache;
|
||||
extern kmem_cache_t *asd_ascb_cache;
|
||||
extern char sas_addr_str[2*SAS_ADDR_SIZE + 1];
|
||||
|
||||
static inline void asd_stringify_sas_addr(char *p, const u8 *sas_addr)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < SAS_ADDR_SIZE; i++, p += 2)
|
||||
snprintf(p, 3, "%02X", sas_addr[i]);
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
static inline void asd_destringify_sas_addr(u8 *sas_addr, const char *p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < SAS_ADDR_SIZE; i++) {
|
||||
u8 h, l;
|
||||
if (!*p)
|
||||
break;
|
||||
h = isdigit(*p) ? *p-'0' : *p-'A'+10;
|
||||
p++;
|
||||
l = isdigit(*p) ? *p-'0' : *p-'A'+10;
|
||||
p++;
|
||||
sas_addr[i] = (h<<4) | l;
|
||||
}
|
||||
}
|
||||
|
||||
struct asd_ha_struct;
|
||||
struct asd_ascb;
|
||||
|
||||
int asd_read_ocm(struct asd_ha_struct *asd_ha);
|
||||
int asd_read_flash(struct asd_ha_struct *asd_ha);
|
||||
|
||||
int asd_dev_found(struct domain_device *dev);
|
||||
void asd_dev_gone(struct domain_device *dev);
|
||||
|
||||
void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id);
|
||||
|
||||
int asd_execute_task(struct sas_task *, int num, unsigned long gfp_flags);
|
||||
|
||||
/* ---------- TMFs ---------- */
|
||||
int asd_abort_task(struct sas_task *);
|
||||
int asd_abort_task_set(struct domain_device *, u8 *lun);
|
||||
int asd_clear_aca(struct domain_device *, u8 *lun);
|
||||
int asd_clear_task_set(struct domain_device *, u8 *lun);
|
||||
int asd_lu_reset(struct domain_device *, u8 *lun);
|
||||
int asd_query_task(struct sas_task *);
|
||||
|
||||
/* ---------- Adapter and Port management ---------- */
|
||||
int asd_clear_nexus_port(struct asd_sas_port *port);
|
||||
int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha);
|
||||
|
||||
/* ---------- Phy Management ---------- */
|
||||
int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func);
|
||||
|
||||
#endif
|
||||
353
drivers/scsi/aic94xx/aic94xx_dev.c
Normal file
353
drivers/scsi/aic94xx/aic94xx_dev.c
Normal file
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA DDB management
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* $Id: //depot/aic94xx/aic94xx_dev.c#21 $
|
||||
*/
|
||||
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_hwi.h"
|
||||
#include "aic94xx_reg.h"
|
||||
#include "aic94xx_sas.h"
|
||||
|
||||
#define FIND_FREE_DDB(_ha) find_first_zero_bit((_ha)->hw_prof.ddb_bitmap, \
|
||||
(_ha)->hw_prof.max_ddbs)
|
||||
#define SET_DDB(_ddb, _ha) set_bit(_ddb, (_ha)->hw_prof.ddb_bitmap)
|
||||
#define CLEAR_DDB(_ddb, _ha) clear_bit(_ddb, (_ha)->hw_prof.ddb_bitmap)
|
||||
|
||||
static inline int asd_get_ddb(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ddb, i;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
|
||||
ddb = FIND_FREE_DDB(asd_ha);
|
||||
if (ddb >= asd_ha->hw_prof.max_ddbs) {
|
||||
ddb = -ENOMEM;
|
||||
spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
|
||||
goto out;
|
||||
}
|
||||
SET_DDB(ddb, asd_ha);
|
||||
spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
|
||||
|
||||
for (i = 0; i < sizeof(struct asd_ddb_ssp_smp_target_port); i+= 4)
|
||||
asd_ddbsite_write_dword(asd_ha, ddb, i, 0);
|
||||
out:
|
||||
return ddb;
|
||||
}
|
||||
|
||||
#define INIT_CONN_TAG offsetof(struct asd_ddb_ssp_smp_target_port, init_conn_tag)
|
||||
#define DEST_SAS_ADDR offsetof(struct asd_ddb_ssp_smp_target_port, dest_sas_addr)
|
||||
#define SEND_QUEUE_HEAD offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_head)
|
||||
#define DDB_TYPE offsetof(struct asd_ddb_ssp_smp_target_port, ddb_type)
|
||||
#define CONN_MASK offsetof(struct asd_ddb_ssp_smp_target_port, conn_mask)
|
||||
#define DDB_TARG_FLAGS offsetof(struct asd_ddb_ssp_smp_target_port, flags)
|
||||
#define DDB_TARG_FLAGS2 offsetof(struct asd_ddb_stp_sata_target_port, flags2)
|
||||
#define EXEC_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, exec_queue_tail)
|
||||
#define SEND_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_tail)
|
||||
#define SISTER_DDB offsetof(struct asd_ddb_ssp_smp_target_port, sister_ddb)
|
||||
#define MAX_CCONN offsetof(struct asd_ddb_ssp_smp_target_port, max_concurrent_conn)
|
||||
#define NUM_CTX offsetof(struct asd_ddb_ssp_smp_target_port, num_contexts)
|
||||
#define ATA_CMD_SCBPTR offsetof(struct asd_ddb_stp_sata_target_port, ata_cmd_scbptr)
|
||||
#define SATA_TAG_ALLOC_MASK offsetof(struct asd_ddb_stp_sata_target_port, sata_tag_alloc_mask)
|
||||
#define NUM_SATA_TAGS offsetof(struct asd_ddb_stp_sata_target_port, num_sata_tags)
|
||||
#define SATA_STATUS offsetof(struct asd_ddb_stp_sata_target_port, sata_status)
|
||||
#define NCQ_DATA_SCB_PTR offsetof(struct asd_ddb_stp_sata_target_port, ncq_data_scb_ptr)
|
||||
#define ITNL_TIMEOUT offsetof(struct asd_ddb_ssp_smp_target_port, itnl_timeout)
|
||||
|
||||
static inline void asd_free_ddb(struct asd_ha_struct *asd_ha, int ddb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!ddb || ddb >= 0xFFFF)
|
||||
return;
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, DDB_TYPE, DDB_TYPE_UNUSED);
|
||||
spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
|
||||
CLEAR_DDB(ddb, asd_ha);
|
||||
spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
|
||||
}
|
||||
|
||||
static inline void asd_set_ddb_type(struct domain_device *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
int ddb = (int) (unsigned long) dev->lldd_dev;
|
||||
|
||||
if (dev->dev_type == SATA_PM_PORT)
|
||||
asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_PM_PORT);
|
||||
else if (dev->tproto)
|
||||
asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_TARGET);
|
||||
else
|
||||
asd_ddbsite_write_byte(asd_ha,ddb,DDB_TYPE,DDB_TYPE_INITIATOR);
|
||||
}
|
||||
|
||||
static int asd_init_sata_tag_ddb(struct domain_device *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
int ddb, i;
|
||||
|
||||
ddb = asd_get_ddb(asd_ha);
|
||||
if (ddb < 0)
|
||||
return ddb;
|
||||
|
||||
for (i = 0; i < sizeof(struct asd_ddb_sata_tag); i += 2)
|
||||
asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF);
|
||||
|
||||
asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev,
|
||||
SISTER_DDB, ddb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int asd_init_sata(struct domain_device *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
int ddb = (int) (unsigned long) dev->lldd_dev;
|
||||
u32 qdepth = 0;
|
||||
int res = 0;
|
||||
|
||||
asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
|
||||
if ((dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) &&
|
||||
dev->sata_dev.identify_device &&
|
||||
dev->sata_dev.identify_device[10] != 0) {
|
||||
u16 w75 = le16_to_cpu(dev->sata_dev.identify_device[75]);
|
||||
u16 w76 = le16_to_cpu(dev->sata_dev.identify_device[76]);
|
||||
|
||||
if (w76 & 0x100) /* NCQ? */
|
||||
qdepth = (w75 & 0x1F) + 1;
|
||||
asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK,
|
||||
(1<<qdepth)-1);
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, NUM_SATA_TAGS, qdepth);
|
||||
}
|
||||
if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM ||
|
||||
dev->dev_type == SATA_PM_PORT) {
|
||||
struct dev_to_host_fis *fis = (struct dev_to_host_fis *)
|
||||
dev->frame_rcvd;
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, SATA_STATUS, fis->status);
|
||||
}
|
||||
asd_ddbsite_write_word(asd_ha, ddb, NCQ_DATA_SCB_PTR, 0xFFFF);
|
||||
if (qdepth > 0)
|
||||
res = asd_init_sata_tag_ddb(dev);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int asd_init_target_ddb(struct domain_device *dev)
|
||||
{
|
||||
int ddb, i;
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
u8 flags = 0;
|
||||
|
||||
ddb = asd_get_ddb(asd_ha);
|
||||
if (ddb < 0)
|
||||
return ddb;
|
||||
|
||||
dev->lldd_dev = (void *) (unsigned long) ddb;
|
||||
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, 0, DDB_TP_CONN_TYPE);
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, 1, 0);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, INIT_CONN_TAG, 0xFFFF);
|
||||
for (i = 0; i < SAS_ADDR_SIZE; i++)
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, DEST_SAS_ADDR+i,
|
||||
dev->sas_addr[i]);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_HEAD, 0xFFFF);
|
||||
asd_set_ddb_type(dev);
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, CONN_MASK, dev->port->phy_mask);
|
||||
if (dev->port->oob_mode != SATA_OOB_MODE) {
|
||||
flags |= OPEN_REQUIRED;
|
||||
if ((dev->dev_type == SATA_DEV) ||
|
||||
(dev->tproto & SAS_PROTO_STP)) {
|
||||
struct smp_resp *rps_resp = &dev->sata_dev.rps_resp;
|
||||
if (rps_resp->frame_type == SMP_RESPONSE &&
|
||||
rps_resp->function == SMP_REPORT_PHY_SATA &&
|
||||
rps_resp->result == SMP_RESP_FUNC_ACC) {
|
||||
if (rps_resp->rps.affil_valid)
|
||||
flags |= STP_AFFIL_POL;
|
||||
if (rps_resp->rps.affil_supp)
|
||||
flags |= SUPPORTS_AFFIL;
|
||||
}
|
||||
} else {
|
||||
flags |= CONCURRENT_CONN_SUPP;
|
||||
if (!dev->parent &&
|
||||
(dev->dev_type == EDGE_DEV ||
|
||||
dev->dev_type == FANOUT_DEV))
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN,
|
||||
4);
|
||||
else
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN,
|
||||
dev->pathways);
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, NUM_CTX, 1);
|
||||
}
|
||||
}
|
||||
if (dev->dev_type == SATA_PM)
|
||||
flags |= SATA_MULTIPORT;
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS, flags);
|
||||
|
||||
flags = 0;
|
||||
if (dev->tproto & SAS_PROTO_STP)
|
||||
flags |= STP_CL_POL_NO_TX;
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS2, flags);
|
||||
|
||||
asd_ddbsite_write_word(asd_ha, ddb, EXEC_QUEUE_TAIL, 0xFFFF);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_TAIL, 0xFFFF);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF);
|
||||
|
||||
if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTO_STP)) {
|
||||
i = asd_init_sata(dev);
|
||||
if (i < 0) {
|
||||
asd_free_ddb(asd_ha, ddb);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->dev_type == SAS_END_DEV) {
|
||||
struct sas_end_device *rdev = rphy_to_end_device(dev->rphy);
|
||||
if (rdev->I_T_nexus_loss_timeout > 0)
|
||||
asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT,
|
||||
min(rdev->I_T_nexus_loss_timeout,
|
||||
(u16)ITNL_TIMEOUT_CONST));
|
||||
else
|
||||
asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT,
|
||||
(u16)ITNL_TIMEOUT_CONST);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asd_init_sata_pm_table_ddb(struct domain_device *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
int ddb, i;
|
||||
|
||||
ddb = asd_get_ddb(asd_ha);
|
||||
if (ddb < 0)
|
||||
return ddb;
|
||||
|
||||
for (i = 0; i < 32; i += 2)
|
||||
asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF);
|
||||
|
||||
asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev,
|
||||
SISTER_DDB, ddb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PM_PORT_FLAGS offsetof(struct asd_ddb_sata_pm_port, pm_port_flags)
|
||||
#define PARENT_DDB offsetof(struct asd_ddb_sata_pm_port, parent_ddb)
|
||||
|
||||
/**
|
||||
* asd_init_sata_pm_port_ddb -- SATA Port Multiplier Port
|
||||
* dev: pointer to domain device
|
||||
*
|
||||
* For SATA Port Multiplier Ports we need to allocate one SATA Port
|
||||
* Multiplier Port DDB and depending on whether the target on it
|
||||
* supports SATA II NCQ, one SATA Tag DDB.
|
||||
*/
|
||||
static int asd_init_sata_pm_port_ddb(struct domain_device *dev)
|
||||
{
|
||||
int ddb, i, parent_ddb, pmtable_ddb;
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
u8 flags;
|
||||
|
||||
ddb = asd_get_ddb(asd_ha);
|
||||
if (ddb < 0)
|
||||
return ddb;
|
||||
|
||||
asd_set_ddb_type(dev);
|
||||
flags = (dev->sata_dev.port_no << 4) | PM_PORT_SET;
|
||||
asd_ddbsite_write_byte(asd_ha, ddb, PM_PORT_FLAGS, flags);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF);
|
||||
asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
|
||||
asd_init_sata(dev);
|
||||
|
||||
parent_ddb = (int) (unsigned long) dev->parent->lldd_dev;
|
||||
asd_ddbsite_write_word(asd_ha, ddb, PARENT_DDB, parent_ddb);
|
||||
pmtable_ddb = asd_ddbsite_read_word(asd_ha, parent_ddb, SISTER_DDB);
|
||||
asd_ddbsite_write_word(asd_ha, pmtable_ddb, dev->sata_dev.port_no,ddb);
|
||||
|
||||
if (asd_ddbsite_read_byte(asd_ha, ddb, NUM_SATA_TAGS) > 0) {
|
||||
i = asd_init_sata_tag_ddb(dev);
|
||||
if (i < 0) {
|
||||
asd_free_ddb(asd_ha, ddb);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asd_init_initiator_ddb(struct domain_device *dev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_init_sata_pm_ddb -- SATA Port Multiplier
|
||||
* dev: pointer to domain device
|
||||
*
|
||||
* For STP and direct-attached SATA Port Multipliers we need
|
||||
* one target port DDB entry and one SATA PM table DDB entry.
|
||||
*/
|
||||
static int asd_init_sata_pm_ddb(struct domain_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
res = asd_init_target_ddb(dev);
|
||||
if (res)
|
||||
goto out;
|
||||
res = asd_init_sata_pm_table_ddb(dev);
|
||||
if (res)
|
||||
asd_free_ddb(dev->port->ha->lldd_ha,
|
||||
(int) (unsigned long) dev->lldd_dev);
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_dev_found(struct domain_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
switch (dev->dev_type) {
|
||||
case SATA_PM:
|
||||
res = asd_init_sata_pm_ddb(dev);
|
||||
break;
|
||||
case SATA_PM_PORT:
|
||||
res = asd_init_sata_pm_port_ddb(dev);
|
||||
break;
|
||||
default:
|
||||
if (dev->tproto)
|
||||
res = asd_init_target_ddb(dev);
|
||||
else
|
||||
res = asd_init_initiator_ddb(dev);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void asd_dev_gone(struct domain_device *dev)
|
||||
{
|
||||
int ddb, sister_ddb;
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
|
||||
ddb = (int) (unsigned long) dev->lldd_dev;
|
||||
sister_ddb = asd_ddbsite_read_word(asd_ha, ddb, SISTER_DDB);
|
||||
|
||||
if (sister_ddb != 0xFFFF)
|
||||
asd_free_ddb(asd_ha, sister_ddb);
|
||||
asd_free_ddb(asd_ha, ddb);
|
||||
dev->lldd_dev = NULL;
|
||||
}
|
||||
959
drivers/scsi/aic94xx/aic94xx_dump.c
Normal file
959
drivers/scsi/aic94xx/aic94xx_dump.c
Normal file
@@ -0,0 +1,959 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver dump interface.
|
||||
*
|
||||
* Copyright (C) 2004 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2004 David Chaw <david_chaw@adaptec.com>
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* 2005/07/14/LT Complete overhaul of this file. Update pages, register
|
||||
* locations, names, etc. Make use of macros. Print more information.
|
||||
* Print all cseq and lseq mip and mdp.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "linux/pci.h"
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_reg.h"
|
||||
#include "aic94xx_reg_def.h"
|
||||
#include "aic94xx_sas.h"
|
||||
|
||||
#include "aic94xx_dump.h"
|
||||
|
||||
#ifdef ASD_DEBUG
|
||||
|
||||
#define MD(x) (1 << (x))
|
||||
#define MODE_COMMON (1 << 31)
|
||||
#define MODE_0_7 (0xFF)
|
||||
|
||||
static const struct lseq_cio_regs {
|
||||
char *name;
|
||||
u32 offs;
|
||||
u8 width;
|
||||
u32 mode;
|
||||
} LSEQmCIOREGS[] = {
|
||||
{"LmMnSCBPTR", 0x20, 16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4) },
|
||||
{"LmMnDDBPTR", 0x22, 16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4) },
|
||||
{"LmREQMBX", 0x30, 32, MODE_COMMON },
|
||||
{"LmRSPMBX", 0x34, 32, MODE_COMMON },
|
||||
{"LmMnINT", 0x38, 32, MODE_0_7 },
|
||||
{"LmMnINTEN", 0x3C, 32, MODE_0_7 },
|
||||
{"LmXMTPRIMD", 0x40, 32, MODE_COMMON },
|
||||
{"LmXMTPRIMCS", 0x44, 8, MODE_COMMON },
|
||||
{"LmCONSTAT", 0x45, 8, MODE_COMMON },
|
||||
{"LmMnDMAERRS", 0x46, 8, MD(0)|MD(1) },
|
||||
{"LmMnSGDMAERRS", 0x47, 8, MD(0)|MD(1) },
|
||||
{"LmMnEXPHDRP", 0x48, 8, MD(0) },
|
||||
{"LmMnSASAALIGN", 0x48, 8, MD(1) },
|
||||
{"LmMnMSKHDRP", 0x49, 8, MD(0) },
|
||||
{"LmMnSTPALIGN", 0x49, 8, MD(1) },
|
||||
{"LmMnRCVHDRP", 0x4A, 8, MD(0) },
|
||||
{"LmMnXMTHDRP", 0x4A, 8, MD(1) },
|
||||
{"LmALIGNMODE", 0x4B, 8, MD(1) },
|
||||
{"LmMnEXPRCVCNT", 0x4C, 32, MD(0) },
|
||||
{"LmMnXMTCNT", 0x4C, 32, MD(1) },
|
||||
{"LmMnCURRTAG", 0x54, 16, MD(0) },
|
||||
{"LmMnPREVTAG", 0x56, 16, MD(0) },
|
||||
{"LmMnACKOFS", 0x58, 8, MD(1) },
|
||||
{"LmMnXFRLVL", 0x59, 8, MD(0)|MD(1) },
|
||||
{"LmMnSGDMACTL", 0x5A, 8, MD(0)|MD(1) },
|
||||
{"LmMnSGDMASTAT", 0x5B, 8, MD(0)|MD(1) },
|
||||
{"LmMnDDMACTL", 0x5C, 8, MD(0)|MD(1) },
|
||||
{"LmMnDDMASTAT", 0x5D, 8, MD(0)|MD(1) },
|
||||
{"LmMnDDMAMODE", 0x5E, 16, MD(0)|MD(1) },
|
||||
{"LmMnPIPECTL", 0x61, 8, MD(0)|MD(1) },
|
||||
{"LmMnACTSCB", 0x62, 16, MD(0)|MD(1) },
|
||||
{"LmMnSGBHADR", 0x64, 8, MD(0)|MD(1) },
|
||||
{"LmMnSGBADR", 0x65, 8, MD(0)|MD(1) },
|
||||
{"LmMnSGDCNT", 0x66, 8, MD(0)|MD(1) },
|
||||
{"LmMnSGDMADR", 0x68, 32, MD(0)|MD(1) },
|
||||
{"LmMnSGDMADR", 0x6C, 32, MD(0)|MD(1) },
|
||||
{"LmMnXFRCNT", 0x70, 32, MD(0)|MD(1) },
|
||||
{"LmMnXMTCRC", 0x74, 32, MD(1) },
|
||||
{"LmCURRTAG", 0x74, 16, MD(0) },
|
||||
{"LmPREVTAG", 0x76, 16, MD(0) },
|
||||
{"LmMnDPSEL", 0x7B, 8, MD(0)|MD(1) },
|
||||
{"LmDPTHSTAT", 0x7C, 8, MODE_COMMON },
|
||||
{"LmMnHOLDLVL", 0x7D, 8, MD(0) },
|
||||
{"LmMnSATAFS", 0x7E, 8, MD(1) },
|
||||
{"LmMnCMPLTSTAT", 0x7F, 8, MD(0)|MD(1) },
|
||||
{"LmPRMSTAT0", 0x80, 32, MODE_COMMON },
|
||||
{"LmPRMSTAT1", 0x84, 32, MODE_COMMON },
|
||||
{"LmGPRMINT", 0x88, 8, MODE_COMMON },
|
||||
{"LmMnCURRSCB", 0x8A, 16, MD(0) },
|
||||
{"LmPRMICODE", 0x8C, 32, MODE_COMMON },
|
||||
{"LmMnRCVCNT", 0x90, 16, MD(0) },
|
||||
{"LmMnBUFSTAT", 0x92, 16, MD(0) },
|
||||
{"LmMnXMTHDRSIZE",0x92, 8, MD(1) },
|
||||
{"LmMnXMTSIZE", 0x93, 8, MD(1) },
|
||||
{"LmMnTGTXFRCNT", 0x94, 32, MD(0) },
|
||||
{"LmMnEXPROFS", 0x98, 32, MD(0) },
|
||||
{"LmMnXMTROFS", 0x98, 32, MD(1) },
|
||||
{"LmMnRCVROFS", 0x9C, 32, MD(0) },
|
||||
{"LmCONCTL", 0xA0, 16, MODE_COMMON },
|
||||
{"LmBITLTIMER", 0xA2, 16, MODE_COMMON },
|
||||
{"LmWWNLOW", 0xA8, 32, MODE_COMMON },
|
||||
{"LmWWNHIGH", 0xAC, 32, MODE_COMMON },
|
||||
{"LmMnFRMERR", 0xB0, 32, MD(0) },
|
||||
{"LmMnFRMERREN", 0xB4, 32, MD(0) },
|
||||
{"LmAWTIMER", 0xB8, 16, MODE_COMMON },
|
||||
{"LmAWTCTL", 0xBA, 8, MODE_COMMON },
|
||||
{"LmMnHDRCMPS", 0xC0, 32, MD(0) },
|
||||
{"LmMnXMTSTAT", 0xC4, 8, MD(1) },
|
||||
{"LmHWTSTATEN", 0xC5, 8, MODE_COMMON },
|
||||
{"LmMnRRDYRC", 0xC6, 8, MD(0) },
|
||||
{"LmMnRRDYTC", 0xC6, 8, MD(1) },
|
||||
{"LmHWTSTAT", 0xC7, 8, MODE_COMMON },
|
||||
{"LmMnDATABUFADR",0xC8, 16, MD(0)|MD(1) },
|
||||
{"LmDWSSTATUS", 0xCB, 8, MODE_COMMON },
|
||||
{"LmMnACTSTAT", 0xCE, 16, MD(0)|MD(1) },
|
||||
{"LmMnREQSCB", 0xD2, 16, MD(0)|MD(1) },
|
||||
{"LmXXXPRIM", 0xD4, 32, MODE_COMMON },
|
||||
{"LmRCVASTAT", 0xD9, 8, MODE_COMMON },
|
||||
{"LmINTDIS1", 0xDA, 8, MODE_COMMON },
|
||||
{"LmPSTORESEL", 0xDB, 8, MODE_COMMON },
|
||||
{"LmPSTORE", 0xDC, 32, MODE_COMMON },
|
||||
{"LmPRIMSTAT0EN", 0xE0, 32, MODE_COMMON },
|
||||
{"LmPRIMSTAT1EN", 0xE4, 32, MODE_COMMON },
|
||||
{"LmDONETCTL", 0xF2, 16, MODE_COMMON },
|
||||
{NULL, 0, 0, 0 }
|
||||
};
|
||||
/*
|
||||
static struct lseq_cio_regs LSEQmOOBREGS[] = {
|
||||
{"OOB_BFLTR" ,0x100, 8, MD(5)},
|
||||
{"OOB_INIT_MIN" ,0x102,16, MD(5)},
|
||||
{"OOB_INIT_MAX" ,0x104,16, MD(5)},
|
||||
{"OOB_INIT_NEG" ,0x106,16, MD(5)},
|
||||
{"OOB_SAS_MIN" ,0x108,16, MD(5)},
|
||||
{"OOB_SAS_MAX" ,0x10A,16, MD(5)},
|
||||
{"OOB_SAS_NEG" ,0x10C,16, MD(5)},
|
||||
{"OOB_WAKE_MIN" ,0x10E,16, MD(5)},
|
||||
{"OOB_WAKE_MAX" ,0x110,16, MD(5)},
|
||||
{"OOB_WAKE_NEG" ,0x112,16, MD(5)},
|
||||
{"OOB_IDLE_MAX" ,0x114,16, MD(5)},
|
||||
{"OOB_BURST_MAX" ,0x116,16, MD(5)},
|
||||
{"OOB_XMIT_BURST" ,0x118, 8, MD(5)},
|
||||
{"OOB_SEND_PAIRS" ,0x119, 8, MD(5)},
|
||||
{"OOB_INIT_IDLE" ,0x11A, 8, MD(5)},
|
||||
{"OOB_INIT_NEGO" ,0x11C, 8, MD(5)},
|
||||
{"OOB_SAS_IDLE" ,0x11E, 8, MD(5)},
|
||||
{"OOB_SAS_NEGO" ,0x120, 8, MD(5)},
|
||||
{"OOB_WAKE_IDLE" ,0x122, 8, MD(5)},
|
||||
{"OOB_WAKE_NEGO" ,0x124, 8, MD(5)},
|
||||
{"OOB_DATA_KBITS" ,0x126, 8, MD(5)},
|
||||
{"OOB_BURST_DATA" ,0x128,32, MD(5)},
|
||||
{"OOB_ALIGN_0_DATA" ,0x12C,32, MD(5)},
|
||||
{"OOB_ALIGN_1_DATA" ,0x130,32, MD(5)},
|
||||
{"OOB_SYNC_DATA" ,0x134,32, MD(5)},
|
||||
{"OOB_D10_2_DATA" ,0x138,32, MD(5)},
|
||||
{"OOB_PHY_RST_CNT" ,0x13C,32, MD(5)},
|
||||
{"OOB_SIG_GEN" ,0x140, 8, MD(5)},
|
||||
{"OOB_XMIT" ,0x141, 8, MD(5)},
|
||||
{"FUNCTION_MAKS" ,0x142, 8, MD(5)},
|
||||
{"OOB_MODE" ,0x143, 8, MD(5)},
|
||||
{"CURRENT_STATUS" ,0x144, 8, MD(5)},
|
||||
{"SPEED_MASK" ,0x145, 8, MD(5)},
|
||||
{"PRIM_COUNT" ,0x146, 8, MD(5)},
|
||||
{"OOB_SIGNALS" ,0x148, 8, MD(5)},
|
||||
{"OOB_DATA_DET" ,0x149, 8, MD(5)},
|
||||
{"OOB_TIME_OUT" ,0x14C, 8, MD(5)},
|
||||
{"OOB_TIMER_ENABLE" ,0x14D, 8, MD(5)},
|
||||
{"OOB_STATUS" ,0x14E, 8, MD(5)},
|
||||
{"HOT_PLUG_DELAY" ,0x150, 8, MD(5)},
|
||||
{"RCD_DELAY" ,0x151, 8, MD(5)},
|
||||
{"COMSAS_TIMER" ,0x152, 8, MD(5)},
|
||||
{"SNTT_DELAY" ,0x153, 8, MD(5)},
|
||||
{"SPD_CHNG_DELAY" ,0x154, 8, MD(5)},
|
||||
{"SNLT_DELAY" ,0x155, 8, MD(5)},
|
||||
{"SNWT_DELAY" ,0x156, 8, MD(5)},
|
||||
{"ALIGN_DELAY" ,0x157, 8, MD(5)},
|
||||
{"INT_ENABLE_0" ,0x158, 8, MD(5)},
|
||||
{"INT_ENABLE_1" ,0x159, 8, MD(5)},
|
||||
{"INT_ENABLE_2" ,0x15A, 8, MD(5)},
|
||||
{"INT_ENABLE_3" ,0x15B, 8, MD(5)},
|
||||
{"OOB_TEST_REG" ,0x15C, 8, MD(5)},
|
||||
{"PHY_CONTROL_0" ,0x160, 8, MD(5)},
|
||||
{"PHY_CONTROL_1" ,0x161, 8, MD(5)},
|
||||
{"PHY_CONTROL_2" ,0x162, 8, MD(5)},
|
||||
{"PHY_CONTROL_3" ,0x163, 8, MD(5)},
|
||||
{"PHY_OOB_CAL_TX" ,0x164, 8, MD(5)},
|
||||
{"PHY_OOB_CAL_RX" ,0x165, 8, MD(5)},
|
||||
{"OOB_PHY_CAL_TX" ,0x166, 8, MD(5)},
|
||||
{"OOB_PHY_CAL_RX" ,0x167, 8, MD(5)},
|
||||
{"PHY_CONTROL_4" ,0x168, 8, MD(5)},
|
||||
{"PHY_TEST" ,0x169, 8, MD(5)},
|
||||
{"PHY_PWR_CTL" ,0x16A, 8, MD(5)},
|
||||
{"PHY_PWR_DELAY" ,0x16B, 8, MD(5)},
|
||||
{"OOB_SM_CON" ,0x16C, 8, MD(5)},
|
||||
{"ADDR_TRAP_1" ,0x16D, 8, MD(5)},
|
||||
{"ADDR_NEXT_1" ,0x16E, 8, MD(5)},
|
||||
{"NEXT_ST_1" ,0x16F, 8, MD(5)},
|
||||
{"OOB_SM_STATE" ,0x170, 8, MD(5)},
|
||||
{"ADDR_TRAP_2" ,0x171, 8, MD(5)},
|
||||
{"ADDR_NEXT_2" ,0x172, 8, MD(5)},
|
||||
{"NEXT_ST_2" ,0x173, 8, MD(5)},
|
||||
{NULL, 0, 0, 0 }
|
||||
};
|
||||
*/
|
||||
#define STR_8BIT " %30s[0x%04x]:0x%02x\n"
|
||||
#define STR_16BIT " %30s[0x%04x]:0x%04x\n"
|
||||
#define STR_32BIT " %30s[0x%04x]:0x%08x\n"
|
||||
#define STR_64BIT " %30s[0x%04x]:0x%llx\n"
|
||||
|
||||
#define PRINT_REG_8bit(_ha, _n, _r) asd_printk(STR_8BIT, #_n, _n, \
|
||||
asd_read_reg_byte(_ha, _r))
|
||||
#define PRINT_REG_16bit(_ha, _n, _r) asd_printk(STR_16BIT, #_n, _n, \
|
||||
asd_read_reg_word(_ha, _r))
|
||||
#define PRINT_REG_32bit(_ha, _n, _r) asd_printk(STR_32BIT, #_n, _n, \
|
||||
asd_read_reg_dword(_ha, _r))
|
||||
|
||||
#define PRINT_CREG_8bit(_ha, _n) asd_printk(STR_8BIT, #_n, _n, \
|
||||
asd_read_reg_byte(_ha, C##_n))
|
||||
#define PRINT_CREG_16bit(_ha, _n) asd_printk(STR_16BIT, #_n, _n, \
|
||||
asd_read_reg_word(_ha, C##_n))
|
||||
#define PRINT_CREG_32bit(_ha, _n) asd_printk(STR_32BIT, #_n, _n, \
|
||||
asd_read_reg_dword(_ha, C##_n))
|
||||
|
||||
#define MSTR_8BIT " Mode:%02d %30s[0x%04x]:0x%02x\n"
|
||||
#define MSTR_16BIT " Mode:%02d %30s[0x%04x]:0x%04x\n"
|
||||
#define MSTR_32BIT " Mode:%02d %30s[0x%04x]:0x%08x\n"
|
||||
|
||||
#define PRINT_MREG_8bit(_ha, _m, _n, _r) asd_printk(MSTR_8BIT, _m, #_n, _n, \
|
||||
asd_read_reg_byte(_ha, _r))
|
||||
#define PRINT_MREG_16bit(_ha, _m, _n, _r) asd_printk(MSTR_16BIT, _m, #_n, _n, \
|
||||
asd_read_reg_word(_ha, _r))
|
||||
#define PRINT_MREG_32bit(_ha, _m, _n, _r) asd_printk(MSTR_32BIT, _m, #_n, _n, \
|
||||
asd_read_reg_dword(_ha, _r))
|
||||
|
||||
/* can also be used for MD when the register is mode aware already */
|
||||
#define PRINT_MIS_byte(_ha, _n) asd_printk(STR_8BIT, #_n,CSEQ_##_n-CMAPPEDSCR,\
|
||||
asd_read_reg_byte(_ha, CSEQ_##_n))
|
||||
#define PRINT_MIS_word(_ha, _n) asd_printk(STR_16BIT,#_n,CSEQ_##_n-CMAPPEDSCR,\
|
||||
asd_read_reg_word(_ha, CSEQ_##_n))
|
||||
#define PRINT_MIS_dword(_ha, _n) \
|
||||
asd_printk(STR_32BIT,#_n,CSEQ_##_n-CMAPPEDSCR,\
|
||||
asd_read_reg_dword(_ha, CSEQ_##_n))
|
||||
#define PRINT_MIS_qword(_ha, _n) \
|
||||
asd_printk(STR_64BIT, #_n,CSEQ_##_n-CMAPPEDSCR, \
|
||||
(unsigned long long)(((u64)asd_read_reg_dword(_ha, CSEQ_##_n)) \
|
||||
| (((u64)asd_read_reg_dword(_ha, (CSEQ_##_n)+4))<<32)))
|
||||
|
||||
#define CMDP_REG(_n, _m) (_m*(CSEQ_PAGE_SIZE*2)+CSEQ_##_n)
|
||||
#define PRINT_CMDP_word(_ha, _n) \
|
||||
asd_printk("%20s 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", \
|
||||
#_n, \
|
||||
asd_read_reg_word(_ha, CMDP_REG(_n, 0)), \
|
||||
asd_read_reg_word(_ha, CMDP_REG(_n, 1)), \
|
||||
asd_read_reg_word(_ha, CMDP_REG(_n, 2)), \
|
||||
asd_read_reg_word(_ha, CMDP_REG(_n, 3)), \
|
||||
asd_read_reg_word(_ha, CMDP_REG(_n, 4)), \
|
||||
asd_read_reg_word(_ha, CMDP_REG(_n, 5)), \
|
||||
asd_read_reg_word(_ha, CMDP_REG(_n, 6)), \
|
||||
asd_read_reg_word(_ha, CMDP_REG(_n, 7)))
|
||||
|
||||
#define PRINT_CMDP_byte(_ha, _n) \
|
||||
asd_printk("%20s 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", \
|
||||
#_n, \
|
||||
asd_read_reg_byte(_ha, CMDP_REG(_n, 0)), \
|
||||
asd_read_reg_byte(_ha, CMDP_REG(_n, 1)), \
|
||||
asd_read_reg_byte(_ha, CMDP_REG(_n, 2)), \
|
||||
asd_read_reg_byte(_ha, CMDP_REG(_n, 3)), \
|
||||
asd_read_reg_byte(_ha, CMDP_REG(_n, 4)), \
|
||||
asd_read_reg_byte(_ha, CMDP_REG(_n, 5)), \
|
||||
asd_read_reg_byte(_ha, CMDP_REG(_n, 6)), \
|
||||
asd_read_reg_byte(_ha, CMDP_REG(_n, 7)))
|
||||
|
||||
static void asd_dump_cseq_state(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int mode;
|
||||
|
||||
asd_printk("CSEQ STATE\n");
|
||||
|
||||
asd_printk("ARP2 REGISTERS\n");
|
||||
|
||||
PRINT_CREG_32bit(asd_ha, ARP2CTL);
|
||||
PRINT_CREG_32bit(asd_ha, ARP2INT);
|
||||
PRINT_CREG_32bit(asd_ha, ARP2INTEN);
|
||||
PRINT_CREG_8bit(asd_ha, MODEPTR);
|
||||
PRINT_CREG_8bit(asd_ha, ALTMODE);
|
||||
PRINT_CREG_8bit(asd_ha, FLAG);
|
||||
PRINT_CREG_8bit(asd_ha, ARP2INTCTL);
|
||||
PRINT_CREG_16bit(asd_ha, STACK);
|
||||
PRINT_CREG_16bit(asd_ha, PRGMCNT);
|
||||
PRINT_CREG_16bit(asd_ha, ACCUM);
|
||||
PRINT_CREG_16bit(asd_ha, SINDEX);
|
||||
PRINT_CREG_16bit(asd_ha, DINDEX);
|
||||
PRINT_CREG_8bit(asd_ha, SINDIR);
|
||||
PRINT_CREG_8bit(asd_ha, DINDIR);
|
||||
PRINT_CREG_8bit(asd_ha, JUMLDIR);
|
||||
PRINT_CREG_8bit(asd_ha, ARP2HALTCODE);
|
||||
PRINT_CREG_16bit(asd_ha, CURRADDR);
|
||||
PRINT_CREG_16bit(asd_ha, LASTADDR);
|
||||
PRINT_CREG_16bit(asd_ha, NXTLADDR);
|
||||
|
||||
asd_printk("IOP REGISTERS\n");
|
||||
|
||||
PRINT_REG_32bit(asd_ha, BISTCTL1, CBISTCTL);
|
||||
PRINT_CREG_32bit(asd_ha, MAPPEDSCR);
|
||||
|
||||
asd_printk("CIO REGISTERS\n");
|
||||
|
||||
for (mode = 0; mode < 9; mode++)
|
||||
PRINT_MREG_16bit(asd_ha, mode, MnSCBPTR, CMnSCBPTR(mode));
|
||||
PRINT_MREG_16bit(asd_ha, 15, MnSCBPTR, CMnSCBPTR(15));
|
||||
|
||||
for (mode = 0; mode < 9; mode++)
|
||||
PRINT_MREG_16bit(asd_ha, mode, MnDDBPTR, CMnDDBPTR(mode));
|
||||
PRINT_MREG_16bit(asd_ha, 15, MnDDBPTR, CMnDDBPTR(15));
|
||||
|
||||
for (mode = 0; mode < 8; mode++)
|
||||
PRINT_MREG_32bit(asd_ha, mode, MnREQMBX, CMnREQMBX(mode));
|
||||
for (mode = 0; mode < 8; mode++)
|
||||
PRINT_MREG_32bit(asd_ha, mode, MnRSPMBX, CMnRSPMBX(mode));
|
||||
for (mode = 0; mode < 8; mode++)
|
||||
PRINT_MREG_32bit(asd_ha, mode, MnINT, CMnINT(mode));
|
||||
for (mode = 0; mode < 8; mode++)
|
||||
PRINT_MREG_32bit(asd_ha, mode, MnINTEN, CMnINTEN(mode));
|
||||
|
||||
PRINT_CREG_8bit(asd_ha, SCRATCHPAGE);
|
||||
for (mode = 0; mode < 8; mode++)
|
||||
PRINT_MREG_8bit(asd_ha, mode, MnSCRATCHPAGE,
|
||||
CMnSCRATCHPAGE(mode));
|
||||
|
||||
PRINT_REG_32bit(asd_ha, CLINKCON, CLINKCON);
|
||||
PRINT_REG_8bit(asd_ha, CCONMSK, CCONMSK);
|
||||
PRINT_REG_8bit(asd_ha, CCONEXIST, CCONEXIST);
|
||||
PRINT_REG_16bit(asd_ha, CCONMODE, CCONMODE);
|
||||
PRINT_REG_32bit(asd_ha, CTIMERCALC, CTIMERCALC);
|
||||
PRINT_REG_8bit(asd_ha, CINTDIS, CINTDIS);
|
||||
|
||||
asd_printk("SCRATCH MEMORY\n");
|
||||
|
||||
asd_printk("MIP 4 >>>>>\n");
|
||||
PRINT_MIS_word(asd_ha, Q_EXE_HEAD);
|
||||
PRINT_MIS_word(asd_ha, Q_EXE_TAIL);
|
||||
PRINT_MIS_word(asd_ha, Q_DONE_HEAD);
|
||||
PRINT_MIS_word(asd_ha, Q_DONE_TAIL);
|
||||
PRINT_MIS_word(asd_ha, Q_SEND_HEAD);
|
||||
PRINT_MIS_word(asd_ha, Q_SEND_TAIL);
|
||||
PRINT_MIS_word(asd_ha, Q_DMA2CHIM_HEAD);
|
||||
PRINT_MIS_word(asd_ha, Q_DMA2CHIM_TAIL);
|
||||
PRINT_MIS_word(asd_ha, Q_COPY_HEAD);
|
||||
PRINT_MIS_word(asd_ha, Q_COPY_TAIL);
|
||||
PRINT_MIS_word(asd_ha, REG0);
|
||||
PRINT_MIS_word(asd_ha, REG1);
|
||||
PRINT_MIS_dword(asd_ha, REG2);
|
||||
PRINT_MIS_byte(asd_ha, LINK_CTL_Q_MAP);
|
||||
PRINT_MIS_byte(asd_ha, MAX_CSEQ_MODE);
|
||||
PRINT_MIS_byte(asd_ha, FREE_LIST_HACK_COUNT);
|
||||
|
||||
asd_printk("MIP 5 >>>>\n");
|
||||
PRINT_MIS_qword(asd_ha, EST_NEXUS_REQ_QUEUE);
|
||||
PRINT_MIS_qword(asd_ha, EST_NEXUS_REQ_COUNT);
|
||||
PRINT_MIS_word(asd_ha, Q_EST_NEXUS_HEAD);
|
||||
PRINT_MIS_word(asd_ha, Q_EST_NEXUS_TAIL);
|
||||
PRINT_MIS_word(asd_ha, NEED_EST_NEXUS_SCB);
|
||||
PRINT_MIS_byte(asd_ha, EST_NEXUS_REQ_HEAD);
|
||||
PRINT_MIS_byte(asd_ha, EST_NEXUS_REQ_TAIL);
|
||||
PRINT_MIS_byte(asd_ha, EST_NEXUS_SCB_OFFSET);
|
||||
|
||||
asd_printk("MIP 6 >>>>\n");
|
||||
PRINT_MIS_word(asd_ha, INT_ROUT_RET_ADDR0);
|
||||
PRINT_MIS_word(asd_ha, INT_ROUT_RET_ADDR1);
|
||||
PRINT_MIS_word(asd_ha, INT_ROUT_SCBPTR);
|
||||
PRINT_MIS_byte(asd_ha, INT_ROUT_MODE);
|
||||
PRINT_MIS_byte(asd_ha, ISR_SCRATCH_FLAGS);
|
||||
PRINT_MIS_word(asd_ha, ISR_SAVE_SINDEX);
|
||||
PRINT_MIS_word(asd_ha, ISR_SAVE_DINDEX);
|
||||
PRINT_MIS_word(asd_ha, Q_MONIRTT_HEAD);
|
||||
PRINT_MIS_word(asd_ha, Q_MONIRTT_TAIL);
|
||||
PRINT_MIS_byte(asd_ha, FREE_SCB_MASK);
|
||||
PRINT_MIS_word(asd_ha, BUILTIN_FREE_SCB_HEAD);
|
||||
PRINT_MIS_word(asd_ha, BUILTIN_FREE_SCB_TAIL);
|
||||
PRINT_MIS_word(asd_ha, EXTENDED_FREE_SCB_HEAD);
|
||||
PRINT_MIS_word(asd_ha, EXTENDED_FREE_SCB_TAIL);
|
||||
|
||||
asd_printk("MIP 7 >>>>\n");
|
||||
PRINT_MIS_qword(asd_ha, EMPTY_REQ_QUEUE);
|
||||
PRINT_MIS_qword(asd_ha, EMPTY_REQ_COUNT);
|
||||
PRINT_MIS_word(asd_ha, Q_EMPTY_HEAD);
|
||||
PRINT_MIS_word(asd_ha, Q_EMPTY_TAIL);
|
||||
PRINT_MIS_word(asd_ha, NEED_EMPTY_SCB);
|
||||
PRINT_MIS_byte(asd_ha, EMPTY_REQ_HEAD);
|
||||
PRINT_MIS_byte(asd_ha, EMPTY_REQ_TAIL);
|
||||
PRINT_MIS_byte(asd_ha, EMPTY_SCB_OFFSET);
|
||||
PRINT_MIS_word(asd_ha, PRIMITIVE_DATA);
|
||||
PRINT_MIS_dword(asd_ha, TIMEOUT_CONST);
|
||||
|
||||
asd_printk("MDP 0 >>>>\n");
|
||||
asd_printk("%-20s %6s %6s %6s %6s %6s %6s %6s %6s\n",
|
||||
"Mode: ", "0", "1", "2", "3", "4", "5", "6", "7");
|
||||
PRINT_CMDP_word(asd_ha, LRM_SAVE_SINDEX);
|
||||
PRINT_CMDP_word(asd_ha, LRM_SAVE_SCBPTR);
|
||||
PRINT_CMDP_word(asd_ha, Q_LINK_HEAD);
|
||||
PRINT_CMDP_word(asd_ha, Q_LINK_TAIL);
|
||||
PRINT_CMDP_byte(asd_ha, LRM_SAVE_SCRPAGE);
|
||||
|
||||
asd_printk("MDP 0 Mode 8 >>>>\n");
|
||||
PRINT_MIS_word(asd_ha, RET_ADDR);
|
||||
PRINT_MIS_word(asd_ha, RET_SCBPTR);
|
||||
PRINT_MIS_word(asd_ha, SAVE_SCBPTR);
|
||||
PRINT_MIS_word(asd_ha, EMPTY_TRANS_CTX);
|
||||
PRINT_MIS_word(asd_ha, RESP_LEN);
|
||||
PRINT_MIS_word(asd_ha, TMF_SCBPTR);
|
||||
PRINT_MIS_word(asd_ha, GLOBAL_PREV_SCB);
|
||||
PRINT_MIS_word(asd_ha, GLOBAL_HEAD);
|
||||
PRINT_MIS_word(asd_ha, CLEAR_LU_HEAD);
|
||||
PRINT_MIS_byte(asd_ha, TMF_OPCODE);
|
||||
PRINT_MIS_byte(asd_ha, SCRATCH_FLAGS);
|
||||
PRINT_MIS_word(asd_ha, HSB_SITE);
|
||||
PRINT_MIS_word(asd_ha, FIRST_INV_SCB_SITE);
|
||||
PRINT_MIS_word(asd_ha, FIRST_INV_DDB_SITE);
|
||||
|
||||
asd_printk("MDP 1 Mode 8 >>>>\n");
|
||||
PRINT_MIS_qword(asd_ha, LUN_TO_CLEAR);
|
||||
PRINT_MIS_qword(asd_ha, LUN_TO_CHECK);
|
||||
|
||||
asd_printk("MDP 2 Mode 8 >>>>\n");
|
||||
PRINT_MIS_qword(asd_ha, HQ_NEW_POINTER);
|
||||
PRINT_MIS_qword(asd_ha, HQ_DONE_BASE);
|
||||
PRINT_MIS_dword(asd_ha, HQ_DONE_POINTER);
|
||||
PRINT_MIS_byte(asd_ha, HQ_DONE_PASS);
|
||||
}
|
||||
|
||||
#define PRINT_LREG_8bit(_h, _lseq, _n) \
|
||||
asd_printk(STR_8BIT, #_n, _n, asd_read_reg_byte(_h, Lm##_n(_lseq)))
|
||||
#define PRINT_LREG_16bit(_h, _lseq, _n) \
|
||||
asd_printk(STR_16BIT, #_n, _n, asd_read_reg_word(_h, Lm##_n(_lseq)))
|
||||
#define PRINT_LREG_32bit(_h, _lseq, _n) \
|
||||
asd_printk(STR_32BIT, #_n, _n, asd_read_reg_dword(_h, Lm##_n(_lseq)))
|
||||
|
||||
#define PRINT_LMIP_byte(_h, _lseq, _n) \
|
||||
asd_printk(STR_8BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
|
||||
asd_read_reg_byte(_h, LmSEQ_##_n(_lseq)))
|
||||
#define PRINT_LMIP_word(_h, _lseq, _n) \
|
||||
asd_printk(STR_16BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
|
||||
asd_read_reg_word(_h, LmSEQ_##_n(_lseq)))
|
||||
#define PRINT_LMIP_dword(_h, _lseq, _n) \
|
||||
asd_printk(STR_32BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
|
||||
asd_read_reg_dword(_h, LmSEQ_##_n(_lseq)))
|
||||
#define PRINT_LMIP_qword(_h, _lseq, _n) \
|
||||
asd_printk(STR_64BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
|
||||
(unsigned long long)(((unsigned long long) \
|
||||
asd_read_reg_dword(_h, LmSEQ_##_n(_lseq))) \
|
||||
| (((unsigned long long) \
|
||||
asd_read_reg_dword(_h, LmSEQ_##_n(_lseq)+4))<<32)))
|
||||
|
||||
static void asd_print_lseq_cio_reg(struct asd_ha_struct *asd_ha,
|
||||
u32 lseq_cio_addr, int i)
|
||||
{
|
||||
switch (LSEQmCIOREGS[i].width) {
|
||||
case 8:
|
||||
asd_printk("%20s[0x%x]: 0x%02x\n", LSEQmCIOREGS[i].name,
|
||||
LSEQmCIOREGS[i].offs,
|
||||
asd_read_reg_byte(asd_ha, lseq_cio_addr +
|
||||
LSEQmCIOREGS[i].offs));
|
||||
|
||||
break;
|
||||
case 16:
|
||||
asd_printk("%20s[0x%x]: 0x%04x\n", LSEQmCIOREGS[i].name,
|
||||
LSEQmCIOREGS[i].offs,
|
||||
asd_read_reg_word(asd_ha, lseq_cio_addr +
|
||||
LSEQmCIOREGS[i].offs));
|
||||
|
||||
break;
|
||||
case 32:
|
||||
asd_printk("%20s[0x%x]: 0x%08x\n", LSEQmCIOREGS[i].name,
|
||||
LSEQmCIOREGS[i].offs,
|
||||
asd_read_reg_dword(asd_ha, lseq_cio_addr +
|
||||
LSEQmCIOREGS[i].offs));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void asd_dump_lseq_state(struct asd_ha_struct *asd_ha, int lseq)
|
||||
{
|
||||
u32 moffs;
|
||||
int mode;
|
||||
|
||||
asd_printk("LSEQ %d STATE\n", lseq);
|
||||
|
||||
asd_printk("LSEQ%d: ARP2 REGISTERS\n", lseq);
|
||||
PRINT_LREG_32bit(asd_ha, lseq, ARP2CTL);
|
||||
PRINT_LREG_32bit(asd_ha, lseq, ARP2INT);
|
||||
PRINT_LREG_32bit(asd_ha, lseq, ARP2INTEN);
|
||||
PRINT_LREG_8bit(asd_ha, lseq, MODEPTR);
|
||||
PRINT_LREG_8bit(asd_ha, lseq, ALTMODE);
|
||||
PRINT_LREG_8bit(asd_ha, lseq, FLAG);
|
||||
PRINT_LREG_8bit(asd_ha, lseq, ARP2INTCTL);
|
||||
PRINT_LREG_16bit(asd_ha, lseq, STACK);
|
||||
PRINT_LREG_16bit(asd_ha, lseq, PRGMCNT);
|
||||
PRINT_LREG_16bit(asd_ha, lseq, ACCUM);
|
||||
PRINT_LREG_16bit(asd_ha, lseq, SINDEX);
|
||||
PRINT_LREG_16bit(asd_ha, lseq, DINDEX);
|
||||
PRINT_LREG_8bit(asd_ha, lseq, SINDIR);
|
||||
PRINT_LREG_8bit(asd_ha, lseq, DINDIR);
|
||||
PRINT_LREG_8bit(asd_ha, lseq, JUMLDIR);
|
||||
PRINT_LREG_8bit(asd_ha, lseq, ARP2HALTCODE);
|
||||
PRINT_LREG_16bit(asd_ha, lseq, CURRADDR);
|
||||
PRINT_LREG_16bit(asd_ha, lseq, LASTADDR);
|
||||
PRINT_LREG_16bit(asd_ha, lseq, NXTLADDR);
|
||||
|
||||
asd_printk("LSEQ%d: IOP REGISTERS\n", lseq);
|
||||
|
||||
PRINT_LREG_32bit(asd_ha, lseq, MODECTL);
|
||||
PRINT_LREG_32bit(asd_ha, lseq, DBGMODE);
|
||||
PRINT_LREG_32bit(asd_ha, lseq, CONTROL);
|
||||
PRINT_REG_32bit(asd_ha, BISTCTL0, LmBISTCTL0(lseq));
|
||||
PRINT_REG_32bit(asd_ha, BISTCTL1, LmBISTCTL1(lseq));
|
||||
|
||||
asd_printk("LSEQ%d: CIO REGISTERS\n", lseq);
|
||||
asd_printk("Mode common:\n");
|
||||
|
||||
for (mode = 0; mode < 8; mode++) {
|
||||
u32 lseq_cio_addr = LmSEQ_PHY_BASE(mode, lseq);
|
||||
int i;
|
||||
|
||||
for (i = 0; LSEQmCIOREGS[i].name; i++)
|
||||
if (LSEQmCIOREGS[i].mode == MODE_COMMON)
|
||||
asd_print_lseq_cio_reg(asd_ha,lseq_cio_addr,i);
|
||||
}
|
||||
|
||||
asd_printk("Mode unique:\n");
|
||||
for (mode = 0; mode < 8; mode++) {
|
||||
u32 lseq_cio_addr = LmSEQ_PHY_BASE(mode, lseq);
|
||||
int i;
|
||||
|
||||
asd_printk("Mode %d\n", mode);
|
||||
for (i = 0; LSEQmCIOREGS[i].name; i++) {
|
||||
if (!(LSEQmCIOREGS[i].mode & (1 << mode)))
|
||||
continue;
|
||||
asd_print_lseq_cio_reg(asd_ha, lseq_cio_addr, i);
|
||||
}
|
||||
}
|
||||
|
||||
asd_printk("SCRATCH MEMORY\n");
|
||||
|
||||
asd_printk("LSEQ%d MIP 0 >>>>\n", lseq);
|
||||
PRINT_LMIP_word(asd_ha, lseq, Q_TGTXFR_HEAD);
|
||||
PRINT_LMIP_word(asd_ha, lseq, Q_TGTXFR_TAIL);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, LINK_NUMBER);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, SCRATCH_FLAGS);
|
||||
PRINT_LMIP_qword(asd_ha, lseq, CONNECTION_STATE);
|
||||
PRINT_LMIP_word(asd_ha, lseq, CONCTL);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, CONSTAT);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, CONNECTION_MODES);
|
||||
PRINT_LMIP_word(asd_ha, lseq, REG1_ISR);
|
||||
PRINT_LMIP_word(asd_ha, lseq, REG2_ISR);
|
||||
PRINT_LMIP_word(asd_ha, lseq, REG3_ISR);
|
||||
PRINT_LMIP_qword(asd_ha, lseq,REG0_ISR);
|
||||
|
||||
asd_printk("LSEQ%d MIP 1 >>>>\n", lseq);
|
||||
PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR0);
|
||||
PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR1);
|
||||
PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR2);
|
||||
PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR3);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE0);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE1);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE2);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE3);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_HEAD);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_TAIL);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_BUF_AVAIL);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, TIMEOUT_CONST);
|
||||
PRINT_LMIP_word(asd_ha, lseq, ISR_SAVE_SINDEX);
|
||||
PRINT_LMIP_word(asd_ha, lseq, ISR_SAVE_DINDEX);
|
||||
|
||||
asd_printk("LSEQ%d MIP 2 >>>>\n", lseq);
|
||||
PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR0);
|
||||
PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR1);
|
||||
PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR2);
|
||||
PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR3);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD0);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD1);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD2);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD3);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_HEAD);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_TAIL);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, EMPTY_BUFS_AVAIL);
|
||||
|
||||
asd_printk("LSEQ%d MIP 3 >>>>\n", lseq);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, DEV_PRES_TMR_TOUT_CONST);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, SATA_INTERLOCK_TIMEOUT);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, SRST_ASSERT_TIMEOUT);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, RCV_FIS_TIMEOUT);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, ONE_MILLISEC_TIMEOUT);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, TEN_MS_COMINIT_TIMEOUT);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, SMP_RCV_TIMEOUT);
|
||||
|
||||
for (mode = 0; mode < 3; mode++) {
|
||||
asd_printk("LSEQ%d MDP 0 MODE %d >>>>\n", lseq, mode);
|
||||
moffs = mode * LSEQ_MODE_SCRATCH_SIZE;
|
||||
|
||||
asd_printk(STR_16BIT, "RET_ADDR", 0,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR(lseq)
|
||||
+ moffs));
|
||||
asd_printk(STR_16BIT, "REG0_MODE", 2,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_REG0_MODE(lseq)
|
||||
+ moffs));
|
||||
asd_printk(STR_16BIT, "MODE_FLAGS", 4,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_MODE_FLAGS(lseq)
|
||||
+ moffs));
|
||||
asd_printk(STR_16BIT, "RET_ADDR2", 0x6,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR2(lseq)
|
||||
+ moffs));
|
||||
asd_printk(STR_16BIT, "RET_ADDR1", 0x8,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR1(lseq)
|
||||
+ moffs));
|
||||
asd_printk(STR_8BIT, "OPCODE_TO_CSEQ", 0xB,
|
||||
asd_read_reg_byte(asd_ha, LmSEQ_OPCODE_TO_CSEQ(lseq)
|
||||
+ moffs));
|
||||
asd_printk(STR_16BIT, "DATA_TO_CSEQ", 0xC,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_DATA_TO_CSEQ(lseq)
|
||||
+ moffs));
|
||||
}
|
||||
|
||||
asd_printk("LSEQ%d MDP 0 MODE 5 >>>>\n", lseq);
|
||||
moffs = LSEQ_MODE5_PAGE0_OFFSET;
|
||||
asd_printk(STR_16BIT, "RET_ADDR", 0,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR(lseq) + moffs));
|
||||
asd_printk(STR_16BIT, "REG0_MODE", 2,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_REG0_MODE(lseq) + moffs));
|
||||
asd_printk(STR_16BIT, "MODE_FLAGS", 4,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_MODE_FLAGS(lseq) + moffs));
|
||||
asd_printk(STR_16BIT, "RET_ADDR2", 0x6,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR2(lseq) + moffs));
|
||||
asd_printk(STR_16BIT, "RET_ADDR1", 0x8,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR1(lseq) + moffs));
|
||||
asd_printk(STR_8BIT, "OPCODE_TO_CSEQ", 0xB,
|
||||
asd_read_reg_byte(asd_ha, LmSEQ_OPCODE_TO_CSEQ(lseq) + moffs));
|
||||
asd_printk(STR_16BIT, "DATA_TO_CSEQ", 0xC,
|
||||
asd_read_reg_word(asd_ha, LmSEQ_DATA_TO_CSEQ(lseq) + moffs));
|
||||
|
||||
asd_printk("LSEQ%d MDP 0 MODE 0 >>>>\n", lseq);
|
||||
PRINT_LMIP_word(asd_ha, lseq, FIRST_INV_DDB_SITE);
|
||||
PRINT_LMIP_word(asd_ha, lseq, EMPTY_TRANS_CTX);
|
||||
PRINT_LMIP_word(asd_ha, lseq, RESP_LEN);
|
||||
PRINT_LMIP_word(asd_ha, lseq, FIRST_INV_SCB_SITE);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, INTEN_SAVE);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_FRM_LEN);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_PROTOCOL);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, RESP_STATUS);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, LAST_LOADED_SGE);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, SAVE_SCBPTR);
|
||||
|
||||
asd_printk("LSEQ%d MDP 0 MODE 1 >>>>\n", lseq);
|
||||
PRINT_LMIP_word(asd_ha, lseq, Q_XMIT_HEAD);
|
||||
PRINT_LMIP_word(asd_ha, lseq, M1_EMPTY_TRANS_CTX);
|
||||
PRINT_LMIP_word(asd_ha, lseq, INI_CONN_TAG);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, FAILED_OPEN_STATUS);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, XMIT_REQUEST_TYPE);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, M1_RESP_STATUS);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, M1_LAST_LOADED_SGE);
|
||||
PRINT_LMIP_word(asd_ha, lseq, M1_SAVE_SCBPTR);
|
||||
|
||||
asd_printk("LSEQ%d MDP 0 MODE 2 >>>>\n", lseq);
|
||||
PRINT_LMIP_word(asd_ha, lseq, PORT_COUNTER);
|
||||
PRINT_LMIP_word(asd_ha, lseq, PM_TABLE_PTR);
|
||||
PRINT_LMIP_word(asd_ha, lseq, SATA_INTERLOCK_TMR_SAVE);
|
||||
PRINT_LMIP_word(asd_ha, lseq, IP_BITL);
|
||||
PRINT_LMIP_word(asd_ha, lseq, COPY_SMP_CONN_TAG);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, P0M2_OFFS1AH);
|
||||
|
||||
asd_printk("LSEQ%d MDP 0 MODE 4/5 >>>>\n", lseq);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_STATUS);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_MODE);
|
||||
PRINT_LMIP_word(asd_ha, lseq, Q_LINK_HEAD);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_ERR);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_SIGNALS);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, SAS_RESET_MODE);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, LINK_RESET_RETRY_COUNT);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, NUM_LINK_RESET_RETRIES);
|
||||
PRINT_LMIP_word(asd_ha, lseq, OOB_INT_ENABLES);
|
||||
PRINT_LMIP_word(asd_ha, lseq, NOTIFY_TIMER_TIMEOUT);
|
||||
PRINT_LMIP_word(asd_ha, lseq, NOTIFY_TIMER_DOWN_COUNT);
|
||||
|
||||
asd_printk("LSEQ%d MDP 1 MODE 0 >>>>\n", lseq);
|
||||
PRINT_LMIP_qword(asd_ha, lseq, SG_LIST_PTR_ADDR0);
|
||||
PRINT_LMIP_qword(asd_ha, lseq, SG_LIST_PTR_ADDR1);
|
||||
|
||||
asd_printk("LSEQ%d MDP 1 MODE 1 >>>>\n", lseq);
|
||||
PRINT_LMIP_qword(asd_ha, lseq, M1_SG_LIST_PTR_ADDR0);
|
||||
PRINT_LMIP_qword(asd_ha, lseq, M1_SG_LIST_PTR_ADDR1);
|
||||
|
||||
asd_printk("LSEQ%d MDP 1 MODE 2 >>>>\n", lseq);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, INVALID_DWORD_COUNT);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, DISPARITY_ERROR_COUNT);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, LOSS_OF_SYNC_COUNT);
|
||||
|
||||
asd_printk("LSEQ%d MDP 1 MODE 4/5 >>>>\n", lseq);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, FRAME_TYPE_MASK);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, HASHED_SRC_ADDR_MASK_PRINT);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, NUM_FILL_BYTES_MASK);
|
||||
PRINT_LMIP_word(asd_ha, lseq, TAG_MASK);
|
||||
PRINT_LMIP_word(asd_ha, lseq, TARGET_PORT_XFER_TAG);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, DATA_OFFSET);
|
||||
|
||||
asd_printk("LSEQ%d MDP 2 MODE 0 >>>>\n", lseq);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, SMP_RCV_TIMER_TERM_TS);
|
||||
PRINT_LMIP_byte(asd_ha, lseq, DEVICE_BITS);
|
||||
PRINT_LMIP_word(asd_ha, lseq, SDB_DDB);
|
||||
PRINT_LMIP_word(asd_ha, lseq, SDB_NUM_TAGS);
|
||||
PRINT_LMIP_word(asd_ha, lseq, SDB_CURR_TAG);
|
||||
|
||||
asd_printk("LSEQ%d MDP 2 MODE 1 >>>>\n", lseq);
|
||||
PRINT_LMIP_qword(asd_ha, lseq, TX_ID_ADDR_FRAME);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, OPEN_TIMER_TERM_TS);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, SRST_AS_TIMER_TERM_TS);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, LAST_LOADED_SG_EL);
|
||||
|
||||
asd_printk("LSEQ%d MDP 2 MODE 2 >>>>\n", lseq);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, CLOSE_TIMER_TERM_TS);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, BREAK_TIMER_TERM_TS);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, DWS_RESET_TIMER_TERM_TS);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, SATA_INTERLOCK_TIMER_TERM_TS);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, MCTL_TIMER_TERM_TS);
|
||||
|
||||
asd_printk("LSEQ%d MDP 2 MODE 4/5 >>>>\n", lseq);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, COMINIT_TIMER_TERM_TS);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, RCV_ID_TIMER_TERM_TS);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, RCV_FIS_TIMER_TERM_TS);
|
||||
PRINT_LMIP_dword(asd_ha, lseq, DEV_PRES_TIMER_TERM_TS);
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_dump_ddb_site -- dump a CSEQ DDB site
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @site_no: site number of interest
|
||||
*/
|
||||
void asd_dump_target_ddb(struct asd_ha_struct *asd_ha, u16 site_no)
|
||||
{
|
||||
if (site_no >= asd_ha->hw_prof.max_ddbs)
|
||||
return;
|
||||
|
||||
#define DDB_FIELDB(__name) \
|
||||
asd_ddbsite_read_byte(asd_ha, site_no, \
|
||||
offsetof(struct asd_ddb_ssp_smp_target_port, __name))
|
||||
#define DDB2_FIELDB(__name) \
|
||||
asd_ddbsite_read_byte(asd_ha, site_no, \
|
||||
offsetof(struct asd_ddb_stp_sata_target_port, __name))
|
||||
#define DDB_FIELDW(__name) \
|
||||
asd_ddbsite_read_word(asd_ha, site_no, \
|
||||
offsetof(struct asd_ddb_ssp_smp_target_port, __name))
|
||||
|
||||
#define DDB_FIELDD(__name) \
|
||||
asd_ddbsite_read_dword(asd_ha, site_no, \
|
||||
offsetof(struct asd_ddb_ssp_smp_target_port, __name))
|
||||
|
||||
asd_printk("DDB: 0x%02x\n", site_no);
|
||||
asd_printk("conn_type: 0x%02x\n", DDB_FIELDB(conn_type));
|
||||
asd_printk("conn_rate: 0x%02x\n", DDB_FIELDB(conn_rate));
|
||||
asd_printk("init_conn_tag: 0x%04x\n", be16_to_cpu(DDB_FIELDW(init_conn_tag)));
|
||||
asd_printk("send_queue_head: 0x%04x\n", be16_to_cpu(DDB_FIELDW(send_queue_head)));
|
||||
asd_printk("sq_suspended: 0x%02x\n", DDB_FIELDB(sq_suspended));
|
||||
asd_printk("DDB Type: 0x%02x\n", DDB_FIELDB(ddb_type));
|
||||
asd_printk("AWT Default: 0x%04x\n", DDB_FIELDW(awt_def));
|
||||
asd_printk("compat_features: 0x%02x\n", DDB_FIELDB(compat_features));
|
||||
asd_printk("Pathway Blocked Count: 0x%02x\n",
|
||||
DDB_FIELDB(pathway_blocked_count));
|
||||
asd_printk("arb_wait_time: 0x%04x\n", DDB_FIELDW(arb_wait_time));
|
||||
asd_printk("more_compat_features: 0x%08x\n",
|
||||
DDB_FIELDD(more_compat_features));
|
||||
asd_printk("Conn Mask: 0x%02x\n", DDB_FIELDB(conn_mask));
|
||||
asd_printk("flags: 0x%02x\n", DDB_FIELDB(flags));
|
||||
asd_printk("flags2: 0x%02x\n", DDB2_FIELDB(flags2));
|
||||
asd_printk("ExecQ Tail: 0x%04x\n",DDB_FIELDW(exec_queue_tail));
|
||||
asd_printk("SendQ Tail: 0x%04x\n",DDB_FIELDW(send_queue_tail));
|
||||
asd_printk("Active Task Count: 0x%04x\n",
|
||||
DDB_FIELDW(active_task_count));
|
||||
asd_printk("ITNL Reason: 0x%02x\n", DDB_FIELDB(itnl_reason));
|
||||
asd_printk("ITNL Timeout Const: 0x%04x\n", DDB_FIELDW(itnl_timeout));
|
||||
asd_printk("ITNL timestamp: 0x%08x\n", DDB_FIELDD(itnl_timestamp));
|
||||
}
|
||||
|
||||
void asd_dump_ddb_0(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
#define DDB0_FIELDB(__name) \
|
||||
asd_ddbsite_read_byte(asd_ha, 0, \
|
||||
offsetof(struct asd_ddb_seq_shared, __name))
|
||||
#define DDB0_FIELDW(__name) \
|
||||
asd_ddbsite_read_word(asd_ha, 0, \
|
||||
offsetof(struct asd_ddb_seq_shared, __name))
|
||||
|
||||
#define DDB0_FIELDD(__name) \
|
||||
asd_ddbsite_read_dword(asd_ha,0 , \
|
||||
offsetof(struct asd_ddb_seq_shared, __name))
|
||||
|
||||
#define DDB0_FIELDA(__name, _o) \
|
||||
asd_ddbsite_read_byte(asd_ha, 0, \
|
||||
offsetof(struct asd_ddb_seq_shared, __name)+_o)
|
||||
|
||||
|
||||
asd_printk("DDB: 0\n");
|
||||
asd_printk("q_free_ddb_head:%04x\n", DDB0_FIELDW(q_free_ddb_head));
|
||||
asd_printk("q_free_ddb_tail:%04x\n", DDB0_FIELDW(q_free_ddb_tail));
|
||||
asd_printk("q_free_ddb_cnt:%04x\n", DDB0_FIELDW(q_free_ddb_cnt));
|
||||
asd_printk("q_used_ddb_head:%04x\n", DDB0_FIELDW(q_used_ddb_head));
|
||||
asd_printk("q_used_ddb_tail:%04x\n", DDB0_FIELDW(q_used_ddb_tail));
|
||||
asd_printk("shared_mem_lock:%04x\n", DDB0_FIELDW(shared_mem_lock));
|
||||
asd_printk("smp_conn_tag:%04x\n", DDB0_FIELDW(smp_conn_tag));
|
||||
asd_printk("est_nexus_buf_cnt:%04x\n", DDB0_FIELDW(est_nexus_buf_cnt));
|
||||
asd_printk("est_nexus_buf_thresh:%04x\n",
|
||||
DDB0_FIELDW(est_nexus_buf_thresh));
|
||||
asd_printk("conn_not_active:%02x\n", DDB0_FIELDB(conn_not_active));
|
||||
asd_printk("phy_is_up:%02x\n", DDB0_FIELDB(phy_is_up));
|
||||
asd_printk("port_map_by_links:%02x %02x %02x %02x "
|
||||
"%02x %02x %02x %02x\n",
|
||||
DDB0_FIELDA(port_map_by_links, 0),
|
||||
DDB0_FIELDA(port_map_by_links, 1),
|
||||
DDB0_FIELDA(port_map_by_links, 2),
|
||||
DDB0_FIELDA(port_map_by_links, 3),
|
||||
DDB0_FIELDA(port_map_by_links, 4),
|
||||
DDB0_FIELDA(port_map_by_links, 5),
|
||||
DDB0_FIELDA(port_map_by_links, 6),
|
||||
DDB0_FIELDA(port_map_by_links, 7));
|
||||
}
|
||||
|
||||
static void asd_dump_scb_site(struct asd_ha_struct *asd_ha, u16 site_no)
|
||||
{
|
||||
|
||||
#define SCB_FIELDB(__name) \
|
||||
asd_scbsite_read_byte(asd_ha, site_no, sizeof(struct scb_header) \
|
||||
+ offsetof(struct initiate_ssp_task, __name))
|
||||
#define SCB_FIELDW(__name) \
|
||||
asd_scbsite_read_word(asd_ha, site_no, sizeof(struct scb_header) \
|
||||
+ offsetof(struct initiate_ssp_task, __name))
|
||||
#define SCB_FIELDD(__name) \
|
||||
asd_scbsite_read_dword(asd_ha, site_no, sizeof(struct scb_header) \
|
||||
+ offsetof(struct initiate_ssp_task, __name))
|
||||
|
||||
asd_printk("Total Xfer Len: 0x%08x.\n", SCB_FIELDD(total_xfer_len));
|
||||
asd_printk("Frame Type: 0x%02x.\n", SCB_FIELDB(ssp_frame.frame_type));
|
||||
asd_printk("Tag: 0x%04x.\n", SCB_FIELDW(ssp_frame.tag));
|
||||
asd_printk("Target Port Xfer Tag: 0x%04x.\n",
|
||||
SCB_FIELDW(ssp_frame.tptt));
|
||||
asd_printk("Data Offset: 0x%08x.\n", SCB_FIELDW(ssp_frame.data_offs));
|
||||
asd_printk("Retry Count: 0x%02x.\n", SCB_FIELDB(retry_count));
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_dump_scb_sites -- dump currently used CSEQ SCB sites
|
||||
* @asd_ha: pointer to host adapter struct
|
||||
*/
|
||||
void asd_dump_scb_sites(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
u16 site_no;
|
||||
|
||||
for (site_no = 0; site_no < asd_ha->hw_prof.max_scbs; site_no++) {
|
||||
u8 opcode;
|
||||
|
||||
if (!SCB_SITE_VALID(site_no))
|
||||
continue;
|
||||
|
||||
/* We are only interested in SCB sites currently used.
|
||||
*/
|
||||
opcode = asd_scbsite_read_byte(asd_ha, site_no,
|
||||
offsetof(struct scb_header,
|
||||
opcode));
|
||||
if (opcode == 0xFF)
|
||||
continue;
|
||||
|
||||
asd_printk("\nSCB: 0x%x\n", site_no);
|
||||
asd_dump_scb_site(asd_ha, site_no);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ads_dump_seq_state -- dump CSEQ and LSEQ states
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @lseq_mask: mask of LSEQs of interest
|
||||
*/
|
||||
void asd_dump_seq_state(struct asd_ha_struct *asd_ha, u8 lseq_mask)
|
||||
{
|
||||
int lseq;
|
||||
|
||||
asd_dump_cseq_state(asd_ha);
|
||||
|
||||
if (lseq_mask != 0)
|
||||
for_each_sequencer(lseq_mask, lseq_mask, lseq)
|
||||
asd_dump_lseq_state(asd_ha, lseq);
|
||||
}
|
||||
|
||||
void asd_dump_frame_rcvd(struct asd_phy *phy,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
switch ((dl->status_block[1] & 0x70) >> 3) {
|
||||
case SAS_PROTO_STP:
|
||||
ASD_DPRINTK("STP proto device-to-host FIS:\n");
|
||||
break;
|
||||
default:
|
||||
case SAS_PROTO_SSP:
|
||||
ASD_DPRINTK("SAS proto IDENTIFY:\n");
|
||||
break;
|
||||
}
|
||||
spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
|
||||
for (i = 0; i < phy->sas_phy.frame_rcvd_size; i+=4)
|
||||
ASD_DPRINTK("%02x: %02x %02x %02x %02x\n",
|
||||
i,
|
||||
phy->frame_rcvd[i],
|
||||
phy->frame_rcvd[i+1],
|
||||
phy->frame_rcvd[i+2],
|
||||
phy->frame_rcvd[i+3]);
|
||||
spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
|
||||
}
|
||||
|
||||
static inline void asd_dump_scb(struct asd_ascb *ascb, int ind)
|
||||
{
|
||||
asd_printk("scb%d: vaddr: 0x%p, dma_handle: 0x%llx, next: 0x%llx, "
|
||||
"index:%d, opcode:0x%02x\n",
|
||||
ind, ascb->dma_scb.vaddr,
|
||||
(unsigned long long)ascb->dma_scb.dma_handle,
|
||||
(unsigned long long)
|
||||
le64_to_cpu(ascb->scb->header.next_scb),
|
||||
le16_to_cpu(ascb->scb->header.index),
|
||||
ascb->scb->header.opcode);
|
||||
}
|
||||
|
||||
void asd_dump_scb_list(struct asd_ascb *ascb, int num)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
asd_printk("dumping %d scbs:\n", num);
|
||||
|
||||
asd_dump_scb(ascb, i++);
|
||||
--num;
|
||||
|
||||
if (num > 0 && !list_empty(&ascb->list)) {
|
||||
struct list_head *el;
|
||||
|
||||
list_for_each(el, &ascb->list) {
|
||||
struct asd_ascb *s = list_entry(el, struct asd_ascb,
|
||||
list);
|
||||
asd_dump_scb(s, i++);
|
||||
if (--num <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ASD_DEBUG */
|
||||
52
drivers/scsi/aic94xx/aic94xx_dump.h
Normal file
52
drivers/scsi/aic94xx/aic94xx_dump.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver dump header file.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AIC94XX_DUMP_H_
|
||||
#define _AIC94XX_DUMP_H_
|
||||
|
||||
#ifdef ASD_DEBUG
|
||||
|
||||
void asd_dump_ddb_0(struct asd_ha_struct *asd_ha);
|
||||
void asd_dump_target_ddb(struct asd_ha_struct *asd_ha, u16 site_no);
|
||||
void asd_dump_scb_sites(struct asd_ha_struct *asd_ha);
|
||||
void asd_dump_seq_state(struct asd_ha_struct *asd_ha, u8 lseq_mask);
|
||||
void asd_dump_frame_rcvd(struct asd_phy *phy,
|
||||
struct done_list_struct *dl);
|
||||
void asd_dump_scb_list(struct asd_ascb *ascb, int num);
|
||||
#else /* ASD_DEBUG */
|
||||
|
||||
static inline void asd_dump_ddb_0(struct asd_ha_struct *asd_ha) { }
|
||||
static inline void asd_dump_target_ddb(struct asd_ha_struct *asd_ha,
|
||||
u16 site_no) { }
|
||||
static inline void asd_dump_scb_sites(struct asd_ha_struct *asd_ha) { }
|
||||
static inline void asd_dump_seq_state(struct asd_ha_struct *asd_ha,
|
||||
u8 lseq_mask) { }
|
||||
static inline void asd_dump_frame_rcvd(struct asd_phy *phy,
|
||||
struct done_list_struct *dl) { }
|
||||
static inline void asd_dump_scb_list(struct asd_ascb *ascb, int num) { }
|
||||
#endif /* ASD_DEBUG */
|
||||
|
||||
#endif /* _AIC94XX_DUMP_H_ */
|
||||
1376
drivers/scsi/aic94xx/aic94xx_hwi.c
Normal file
1376
drivers/scsi/aic94xx/aic94xx_hwi.c
Normal file
File diff suppressed because it is too large
Load Diff
397
drivers/scsi/aic94xx/aic94xx_hwi.h
Normal file
397
drivers/scsi/aic94xx/aic94xx_hwi.h
Normal file
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver hardware interface header file.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AIC94XX_HWI_H_
|
||||
#define _AIC94XX_HWI_H_
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <scsi/libsas.h>
|
||||
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_sas.h"
|
||||
|
||||
/* Define ASD_MAX_PHYS to the maximum phys ever. Currently 8. */
|
||||
#define ASD_MAX_PHYS 8
|
||||
#define ASD_PCBA_SN_SIZE 12
|
||||
|
||||
/* Those are to be further named properly, the "RAZORx" part, and
|
||||
* subsequently included in include/linux/pci_ids.h.
|
||||
*/
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR10 0x410
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR12 0x412
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR1E 0x41E
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR30 0x430
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR32 0x432
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR3E 0x43E
|
||||
#define PCI_DEVICE_ID_ADAPTEC2_RAZOR3F 0x43F
|
||||
|
||||
struct asd_ha_addrspace {
|
||||
void __iomem *addr;
|
||||
unsigned long start; /* pci resource start */
|
||||
unsigned long len; /* pci resource len */
|
||||
unsigned long flags; /* pci resource flags */
|
||||
|
||||
/* addresses internal to the host adapter */
|
||||
u32 swa_base; /* mmspace 1 (MBAR1) uses this only */
|
||||
u32 swb_base;
|
||||
u32 swc_base;
|
||||
};
|
||||
|
||||
struct bios_struct {
|
||||
int present;
|
||||
u8 maj;
|
||||
u8 min;
|
||||
u32 bld;
|
||||
};
|
||||
|
||||
struct unit_element_struct {
|
||||
u16 num;
|
||||
u16 size;
|
||||
void *area;
|
||||
};
|
||||
|
||||
struct flash_struct {
|
||||
u32 bar;
|
||||
int present;
|
||||
int wide;
|
||||
u8 manuf;
|
||||
u8 dev_id;
|
||||
u8 sec_prot;
|
||||
|
||||
u32 dir_offs;
|
||||
};
|
||||
|
||||
struct asd_phy_desc {
|
||||
/* From CTRL-A settings, then set to what is appropriate */
|
||||
u8 sas_addr[SAS_ADDR_SIZE];
|
||||
u8 max_sas_lrate;
|
||||
u8 min_sas_lrate;
|
||||
u8 max_sata_lrate;
|
||||
u8 min_sata_lrate;
|
||||
u8 flags;
|
||||
#define ASD_CRC_DIS 1
|
||||
#define ASD_SATA_SPINUP_HOLD 2
|
||||
|
||||
u8 phy_control_0; /* mode 5 reg 0x160 */
|
||||
u8 phy_control_1; /* mode 5 reg 0x161 */
|
||||
u8 phy_control_2; /* mode 5 reg 0x162 */
|
||||
u8 phy_control_3; /* mode 5 reg 0x163 */
|
||||
};
|
||||
|
||||
struct asd_dma_tok {
|
||||
void *vaddr;
|
||||
dma_addr_t dma_handle;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct hw_profile {
|
||||
struct bios_struct bios;
|
||||
struct unit_element_struct ue;
|
||||
struct flash_struct flash;
|
||||
|
||||
u8 sas_addr[SAS_ADDR_SIZE];
|
||||
char pcba_sn[ASD_PCBA_SN_SIZE+1];
|
||||
|
||||
u8 enabled_phys; /* mask of enabled phys */
|
||||
struct asd_phy_desc phy_desc[ASD_MAX_PHYS];
|
||||
u32 max_scbs; /* absolute sequencer scb queue size */
|
||||
struct asd_dma_tok *scb_ext;
|
||||
u32 max_ddbs;
|
||||
struct asd_dma_tok *ddb_ext;
|
||||
|
||||
spinlock_t ddb_lock;
|
||||
void *ddb_bitmap;
|
||||
|
||||
int num_phys; /* ENABLEABLE */
|
||||
int max_phys; /* REPORTED + ENABLEABLE */
|
||||
|
||||
unsigned addr_range; /* max # of addrs; max # of possible ports */
|
||||
unsigned port_name_base;
|
||||
unsigned dev_name_base;
|
||||
unsigned sata_name_base;
|
||||
};
|
||||
|
||||
struct asd_ascb {
|
||||
struct list_head list;
|
||||
struct asd_ha_struct *ha;
|
||||
|
||||
struct scb *scb; /* equals dma_scb->vaddr */
|
||||
struct asd_dma_tok dma_scb;
|
||||
struct asd_dma_tok *sg_arr;
|
||||
|
||||
void (*tasklet_complete)(struct asd_ascb *, struct done_list_struct *);
|
||||
u8 uldd_timer:1;
|
||||
|
||||
/* internally generated command */
|
||||
struct timer_list timer;
|
||||
struct completion completion;
|
||||
u8 tag_valid:1;
|
||||
__be16 tag; /* error recovery only */
|
||||
|
||||
/* If this is an Empty SCB, index of first edb in seq->edb_arr. */
|
||||
int edb_index;
|
||||
|
||||
/* Used by the timer timeout function. */
|
||||
int tc_index;
|
||||
|
||||
void *uldd_task;
|
||||
};
|
||||
|
||||
#define ASD_DL_SIZE_BITS 0x8
|
||||
#define ASD_DL_SIZE (1<<(2+ASD_DL_SIZE_BITS))
|
||||
#define ASD_DEF_DL_TOGGLE 0x01
|
||||
|
||||
struct asd_seq_data {
|
||||
spinlock_t pend_q_lock;
|
||||
u16 scbpro;
|
||||
int pending;
|
||||
struct list_head pend_q;
|
||||
int can_queue; /* per adapter */
|
||||
struct asd_dma_tok next_scb; /* next scb to be delivered to CSEQ */
|
||||
|
||||
spinlock_t tc_index_lock;
|
||||
void **tc_index_array;
|
||||
void *tc_index_bitmap;
|
||||
int tc_index_bitmap_bits;
|
||||
|
||||
struct tasklet_struct dl_tasklet;
|
||||
struct done_list_struct *dl; /* array of done list entries, equals */
|
||||
struct asd_dma_tok *actual_dl; /* actual_dl->vaddr */
|
||||
int dl_toggle;
|
||||
int dl_next;
|
||||
|
||||
int num_edbs;
|
||||
struct asd_dma_tok **edb_arr;
|
||||
int num_escbs;
|
||||
struct asd_ascb **escb_arr; /* array of pointers to escbs */
|
||||
};
|
||||
|
||||
/* This is the Host Adapter structure. It describes the hardware
|
||||
* SAS adapter.
|
||||
*/
|
||||
struct asd_ha_struct {
|
||||
struct pci_dev *pcidev;
|
||||
const char *name;
|
||||
|
||||
struct sas_ha_struct sas_ha;
|
||||
|
||||
u8 revision_id;
|
||||
|
||||
int iospace;
|
||||
spinlock_t iolock;
|
||||
struct asd_ha_addrspace io_handle[2];
|
||||
|
||||
struct hw_profile hw_prof;
|
||||
|
||||
struct asd_phy phys[ASD_MAX_PHYS];
|
||||
struct asd_sas_port ports[ASD_MAX_PHYS];
|
||||
|
||||
struct dma_pool *scb_pool;
|
||||
|
||||
struct asd_seq_data seq; /* sequencer related */
|
||||
};
|
||||
|
||||
/* ---------- Common macros ---------- */
|
||||
|
||||
#define ASD_BUSADDR_LO(__dma_handle) ((u32)(__dma_handle))
|
||||
#define ASD_BUSADDR_HI(__dma_handle) (((sizeof(dma_addr_t))==8) \
|
||||
? ((u32)((__dma_handle) >> 32)) \
|
||||
: ((u32)0))
|
||||
|
||||
#define dev_to_asd_ha(__dev) pci_get_drvdata(to_pci_dev(__dev))
|
||||
#define SCB_SITE_VALID(__site_no) (((__site_no) & 0xF0FF) != 0x00FF \
|
||||
&& ((__site_no) & 0xF0FF) > 0x001F)
|
||||
/* For each bit set in __lseq_mask, set __lseq to equal the bit
|
||||
* position of the set bit and execute the statement following.
|
||||
* __mc is the temporary mask, used as a mask "counter".
|
||||
*/
|
||||
#define for_each_sequencer(__lseq_mask, __mc, __lseq) \
|
||||
for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\
|
||||
if (((__mc) & 1))
|
||||
#define for_each_phy(__lseq_mask, __mc, __lseq) \
|
||||
for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\
|
||||
if (((__mc) & 1))
|
||||
|
||||
#define PHY_ENABLED(_HA, _I) ((_HA)->hw_prof.enabled_phys & (1<<(_I)))
|
||||
|
||||
/* ---------- DMA allocs ---------- */
|
||||
|
||||
static inline struct asd_dma_tok *asd_dmatok_alloc(unsigned int flags)
|
||||
{
|
||||
return kmem_cache_alloc(asd_dma_token_cache, flags);
|
||||
}
|
||||
|
||||
static inline void asd_dmatok_free(struct asd_dma_tok *token)
|
||||
{
|
||||
kmem_cache_free(asd_dma_token_cache, token);
|
||||
}
|
||||
|
||||
static inline struct asd_dma_tok *asd_alloc_coherent(struct asd_ha_struct *
|
||||
asd_ha, size_t size,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct asd_dma_tok *token = asd_dmatok_alloc(flags);
|
||||
if (token) {
|
||||
token->size = size;
|
||||
token->vaddr = dma_alloc_coherent(&asd_ha->pcidev->dev,
|
||||
token->size,
|
||||
&token->dma_handle,
|
||||
flags);
|
||||
if (!token->vaddr) {
|
||||
asd_dmatok_free(token);
|
||||
token = NULL;
|
||||
}
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
static inline void asd_free_coherent(struct asd_ha_struct *asd_ha,
|
||||
struct asd_dma_tok *token)
|
||||
{
|
||||
if (token) {
|
||||
dma_free_coherent(&asd_ha->pcidev->dev, token->size,
|
||||
token->vaddr, token->dma_handle);
|
||||
asd_dmatok_free(token);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void asd_init_ascb(struct asd_ha_struct *asd_ha,
|
||||
struct asd_ascb *ascb)
|
||||
{
|
||||
INIT_LIST_HEAD(&ascb->list);
|
||||
ascb->scb = ascb->dma_scb.vaddr;
|
||||
ascb->ha = asd_ha;
|
||||
ascb->timer.function = NULL;
|
||||
init_timer(&ascb->timer);
|
||||
ascb->tc_index = -1;
|
||||
init_completion(&ascb->completion);
|
||||
}
|
||||
|
||||
/* Must be called with the tc_index_lock held!
|
||||
*/
|
||||
static inline void asd_tc_index_release(struct asd_seq_data *seq, int index)
|
||||
{
|
||||
seq->tc_index_array[index] = NULL;
|
||||
clear_bit(index, seq->tc_index_bitmap);
|
||||
}
|
||||
|
||||
/* Must be called with the tc_index_lock held!
|
||||
*/
|
||||
static inline int asd_tc_index_get(struct asd_seq_data *seq, void *ptr)
|
||||
{
|
||||
int index;
|
||||
|
||||
index = find_first_zero_bit(seq->tc_index_bitmap,
|
||||
seq->tc_index_bitmap_bits);
|
||||
if (index == seq->tc_index_bitmap_bits)
|
||||
return -1;
|
||||
|
||||
seq->tc_index_array[index] = ptr;
|
||||
set_bit(index, seq->tc_index_bitmap);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/* Must be called with the tc_index_lock held!
|
||||
*/
|
||||
static inline void *asd_tc_index_find(struct asd_seq_data *seq, int index)
|
||||
{
|
||||
return seq->tc_index_array[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_ascb_free -- free a single aSCB after is has completed
|
||||
* @ascb: pointer to the aSCB of interest
|
||||
*
|
||||
* This frees an aSCB after it has been executed/completed by
|
||||
* the sequencer.
|
||||
*/
|
||||
static inline void asd_ascb_free(struct asd_ascb *ascb)
|
||||
{
|
||||
if (ascb) {
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
unsigned long flags;
|
||||
|
||||
BUG_ON(!list_empty(&ascb->list));
|
||||
spin_lock_irqsave(&ascb->ha->seq.tc_index_lock, flags);
|
||||
asd_tc_index_release(&ascb->ha->seq, ascb->tc_index);
|
||||
spin_unlock_irqrestore(&ascb->ha->seq.tc_index_lock, flags);
|
||||
dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr,
|
||||
ascb->dma_scb.dma_handle);
|
||||
kmem_cache_free(asd_ascb_cache, ascb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_ascb_list_free -- free a list of ascbs
|
||||
* @ascb_list: a list of ascbs
|
||||
*
|
||||
* This function will free a list of ascbs allocated by asd_ascb_alloc_list.
|
||||
* It is used when say the scb queueing function returned QUEUE_FULL,
|
||||
* and we do not need the ascbs any more.
|
||||
*/
|
||||
static inline void asd_ascb_free_list(struct asd_ascb *ascb_list)
|
||||
{
|
||||
LIST_HEAD(list);
|
||||
struct list_head *n, *pos;
|
||||
|
||||
__list_add(&list, ascb_list->list.prev, &ascb_list->list);
|
||||
list_for_each_safe(pos, n, &list) {
|
||||
list_del_init(pos);
|
||||
asd_ascb_free(list_entry(pos, struct asd_ascb, list));
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Function declarations ---------- */
|
||||
|
||||
int asd_init_hw(struct asd_ha_struct *asd_ha);
|
||||
irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs);
|
||||
|
||||
|
||||
struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct
|
||||
*asd_ha, int *num,
|
||||
unsigned int gfp_mask);
|
||||
|
||||
int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
|
||||
int num);
|
||||
int asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
|
||||
int num);
|
||||
|
||||
int asd_init_post_escbs(struct asd_ha_struct *asd_ha);
|
||||
void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc);
|
||||
void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op);
|
||||
void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op);
|
||||
int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask);
|
||||
void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
|
||||
u8 subfunc);
|
||||
|
||||
void asd_ascb_timedout(unsigned long data);
|
||||
int asd_chip_hardrst(struct asd_ha_struct *asd_ha);
|
||||
|
||||
#endif
|
||||
860
drivers/scsi/aic94xx/aic94xx_init.c
Normal file
860
drivers/scsi/aic94xx/aic94xx_init.c
Normal file
@@ -0,0 +1,860 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver initialization.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <scsi/scsi_host.h>
|
||||
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_reg.h"
|
||||
#include "aic94xx_hwi.h"
|
||||
#include "aic94xx_seq.h"
|
||||
|
||||
/* The format is "version.release.patchlevel" */
|
||||
#define ASD_DRIVER_VERSION "1.0.2"
|
||||
|
||||
static int use_msi = 0;
|
||||
module_param_named(use_msi, use_msi, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(use_msi, "\n"
|
||||
"\tEnable(1) or disable(0) using PCI MSI.\n"
|
||||
"\tDefault: 0");
|
||||
|
||||
static int lldd_max_execute_num = 0;
|
||||
module_param_named(collector, lldd_max_execute_num, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(collector, "\n"
|
||||
"\tIf greater than one, tells the SAS Layer to run in Task Collector\n"
|
||||
"\tMode. If 1 or 0, tells the SAS Layer to run in Direct Mode.\n"
|
||||
"\tThe aic94xx SAS LLDD supports both modes.\n"
|
||||
"\tDefault: 0 (Direct Mode).\n");
|
||||
|
||||
char sas_addr_str[2*SAS_ADDR_SIZE + 1] = "";
|
||||
|
||||
static struct scsi_transport_template *aic94xx_transport_template;
|
||||
|
||||
static struct scsi_host_template aic94xx_sht = {
|
||||
.module = THIS_MODULE,
|
||||
/* .name is initialized */
|
||||
.name = "aic94xx",
|
||||
.queuecommand = sas_queuecommand,
|
||||
.target_alloc = sas_target_alloc,
|
||||
.slave_configure = sas_slave_configure,
|
||||
.slave_destroy = sas_slave_destroy,
|
||||
.change_queue_depth = sas_change_queue_depth,
|
||||
.change_queue_type = sas_change_queue_type,
|
||||
.bios_param = sas_bios_param,
|
||||
.can_queue = 1,
|
||||
.cmd_per_lun = 1,
|
||||
.this_id = -1,
|
||||
.sg_tablesize = SG_ALL,
|
||||
.max_sectors = SCSI_DEFAULT_MAX_SECTORS,
|
||||
.use_clustering = ENABLE_CLUSTERING,
|
||||
};
|
||||
|
||||
static int __devinit asd_map_memio(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int err, i;
|
||||
struct asd_ha_addrspace *io_handle;
|
||||
|
||||
asd_ha->iospace = 0;
|
||||
for (i = 0; i < 3; i += 2) {
|
||||
io_handle = &asd_ha->io_handle[i==0?0:1];
|
||||
io_handle->start = pci_resource_start(asd_ha->pcidev, i);
|
||||
io_handle->len = pci_resource_len(asd_ha->pcidev, i);
|
||||
io_handle->flags = pci_resource_flags(asd_ha->pcidev, i);
|
||||
err = -ENODEV;
|
||||
if (!io_handle->start || !io_handle->len) {
|
||||
asd_printk("MBAR%d start or length for %s is 0.\n",
|
||||
i==0?0:1, pci_name(asd_ha->pcidev));
|
||||
goto Err;
|
||||
}
|
||||
err = pci_request_region(asd_ha->pcidev, i, ASD_DRIVER_NAME);
|
||||
if (err) {
|
||||
asd_printk("couldn't reserve memory region for %s\n",
|
||||
pci_name(asd_ha->pcidev));
|
||||
goto Err;
|
||||
}
|
||||
if (io_handle->flags & IORESOURCE_CACHEABLE)
|
||||
io_handle->addr = ioremap(io_handle->start,
|
||||
io_handle->len);
|
||||
else
|
||||
io_handle->addr = ioremap_nocache(io_handle->start,
|
||||
io_handle->len);
|
||||
if (!io_handle->addr) {
|
||||
asd_printk("couldn't map MBAR%d of %s\n", i==0?0:1,
|
||||
pci_name(asd_ha->pcidev));
|
||||
goto Err_unreq;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
Err_unreq:
|
||||
pci_release_region(asd_ha->pcidev, i);
|
||||
Err:
|
||||
if (i > 0) {
|
||||
io_handle = &asd_ha->io_handle[0];
|
||||
iounmap(io_handle->addr);
|
||||
pci_release_region(asd_ha->pcidev, 0);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __devexit asd_unmap_memio(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
struct asd_ha_addrspace *io_handle;
|
||||
|
||||
io_handle = &asd_ha->io_handle[1];
|
||||
iounmap(io_handle->addr);
|
||||
pci_release_region(asd_ha->pcidev, 2);
|
||||
|
||||
io_handle = &asd_ha->io_handle[0];
|
||||
iounmap(io_handle->addr);
|
||||
pci_release_region(asd_ha->pcidev, 0);
|
||||
}
|
||||
|
||||
static int __devinit asd_map_ioport(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int i = PCI_IOBAR_OFFSET, err;
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];
|
||||
|
||||
asd_ha->iospace = 1;
|
||||
io_handle->start = pci_resource_start(asd_ha->pcidev, i);
|
||||
io_handle->len = pci_resource_len(asd_ha->pcidev, i);
|
||||
io_handle->flags = pci_resource_flags(asd_ha->pcidev, i);
|
||||
io_handle->addr = (void __iomem *) io_handle->start;
|
||||
if (!io_handle->start || !io_handle->len) {
|
||||
asd_printk("couldn't get IO ports for %s\n",
|
||||
pci_name(asd_ha->pcidev));
|
||||
return -ENODEV;
|
||||
}
|
||||
err = pci_request_region(asd_ha->pcidev, i, ASD_DRIVER_NAME);
|
||||
if (err) {
|
||||
asd_printk("couldn't reserve io space for %s\n",
|
||||
pci_name(asd_ha->pcidev));
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __devexit asd_unmap_ioport(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
pci_release_region(asd_ha->pcidev, PCI_IOBAR_OFFSET);
|
||||
}
|
||||
|
||||
static int __devinit asd_map_ha(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int err;
|
||||
u16 cmd_reg;
|
||||
|
||||
err = pci_read_config_word(asd_ha->pcidev, PCI_COMMAND, &cmd_reg);
|
||||
if (err) {
|
||||
asd_printk("couldn't read command register of %s\n",
|
||||
pci_name(asd_ha->pcidev));
|
||||
goto Err;
|
||||
}
|
||||
|
||||
err = -ENODEV;
|
||||
if (cmd_reg & PCI_COMMAND_MEMORY) {
|
||||
if ((err = asd_map_memio(asd_ha)))
|
||||
goto Err;
|
||||
} else if (cmd_reg & PCI_COMMAND_IO) {
|
||||
if ((err = asd_map_ioport(asd_ha)))
|
||||
goto Err;
|
||||
asd_printk("%s ioport mapped -- upgrade your hardware\n",
|
||||
pci_name(asd_ha->pcidev));
|
||||
} else {
|
||||
asd_printk("no proper device access to %s\n",
|
||||
pci_name(asd_ha->pcidev));
|
||||
goto Err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
Err:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __devexit asd_unmap_ha(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
if (asd_ha->iospace)
|
||||
asd_unmap_ioport(asd_ha);
|
||||
else
|
||||
asd_unmap_memio(asd_ha);
|
||||
}
|
||||
|
||||
static const char *asd_dev_rev[30] = {
|
||||
[0] = "A0",
|
||||
[1] = "A1",
|
||||
[8] = "B0",
|
||||
};
|
||||
|
||||
static int __devinit asd_common_setup(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int err, i;
|
||||
|
||||
err = pci_read_config_byte(asd_ha->pcidev, PCI_REVISION_ID,
|
||||
&asd_ha->revision_id);
|
||||
if (err) {
|
||||
asd_printk("couldn't read REVISION ID register of %s\n",
|
||||
pci_name(asd_ha->pcidev));
|
||||
goto Err;
|
||||
}
|
||||
err = -ENODEV;
|
||||
if (asd_ha->revision_id < AIC9410_DEV_REV_B0) {
|
||||
asd_printk("%s is revision %s (%X), which is not supported\n",
|
||||
pci_name(asd_ha->pcidev),
|
||||
asd_dev_rev[asd_ha->revision_id],
|
||||
asd_ha->revision_id);
|
||||
goto Err;
|
||||
}
|
||||
/* Provide some sane default values. */
|
||||
asd_ha->hw_prof.max_scbs = 512;
|
||||
asd_ha->hw_prof.max_ddbs = 128;
|
||||
asd_ha->hw_prof.num_phys = ASD_MAX_PHYS;
|
||||
/* All phys are enabled, by default. */
|
||||
asd_ha->hw_prof.enabled_phys = 0xFF;
|
||||
for (i = 0; i < ASD_MAX_PHYS; i++) {
|
||||
asd_ha->hw_prof.phy_desc[i].max_sas_lrate = PHY_LINKRATE_3;
|
||||
asd_ha->hw_prof.phy_desc[i].min_sas_lrate = PHY_LINKRATE_1_5;
|
||||
asd_ha->hw_prof.phy_desc[i].max_sata_lrate= PHY_LINKRATE_1_5;
|
||||
asd_ha->hw_prof.phy_desc[i].min_sata_lrate= PHY_LINKRATE_1_5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
Err:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devinit asd_aic9410_setup(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int err = asd_common_setup(asd_ha);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
asd_ha->hw_prof.addr_range = 8;
|
||||
asd_ha->hw_prof.port_name_base = 0;
|
||||
asd_ha->hw_prof.dev_name_base = 8;
|
||||
asd_ha->hw_prof.sata_name_base = 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit asd_aic9405_setup(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int err = asd_common_setup(asd_ha);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
asd_ha->hw_prof.addr_range = 4;
|
||||
asd_ha->hw_prof.port_name_base = 0;
|
||||
asd_ha->hw_prof.dev_name_base = 4;
|
||||
asd_ha->hw_prof.sata_name_base = 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t asd_show_dev_rev(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
asd_dev_rev[asd_ha->revision_id]);
|
||||
}
|
||||
static DEVICE_ATTR(revision, S_IRUGO, asd_show_dev_rev, NULL);
|
||||
|
||||
static ssize_t asd_show_dev_bios_build(struct device *dev,
|
||||
struct device_attribute *attr,char *buf)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", asd_ha->hw_prof.bios.bld);
|
||||
}
|
||||
static DEVICE_ATTR(bios_build, S_IRUGO, asd_show_dev_bios_build, NULL);
|
||||
|
||||
static ssize_t asd_show_dev_pcba_sn(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", asd_ha->hw_prof.pcba_sn);
|
||||
}
|
||||
static DEVICE_ATTR(pcba_sn, S_IRUGO, asd_show_dev_pcba_sn, NULL);
|
||||
|
||||
static void asd_create_dev_attrs(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
device_create_file(&asd_ha->pcidev->dev, &dev_attr_revision);
|
||||
device_create_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
|
||||
device_create_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
|
||||
}
|
||||
|
||||
static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision);
|
||||
device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
|
||||
device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
|
||||
}
|
||||
|
||||
/* The first entry, 0, is used for dynamic ids, the rest for devices
|
||||
* we know about.
|
||||
*/
|
||||
static struct asd_pcidev_struct {
|
||||
const char * name;
|
||||
int (*setup)(struct asd_ha_struct *asd_ha);
|
||||
} asd_pcidev_data[] = {
|
||||
/* Id 0 is used for dynamic ids. */
|
||||
{ .name = "Adaptec AIC-94xx SAS/SATA Host Adapter",
|
||||
.setup = asd_aic9410_setup
|
||||
},
|
||||
{ .name = "Adaptec AIC-9410W SAS/SATA Host Adapter",
|
||||
.setup = asd_aic9410_setup
|
||||
},
|
||||
{ .name = "Adaptec AIC-9405W SAS/SATA Host Adapter",
|
||||
.setup = asd_aic9405_setup
|
||||
},
|
||||
};
|
||||
|
||||
static inline int asd_create_ha_caches(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
asd_ha->scb_pool = dma_pool_create(ASD_DRIVER_NAME "_scb_pool",
|
||||
&asd_ha->pcidev->dev,
|
||||
sizeof(struct scb),
|
||||
8, 0);
|
||||
if (!asd_ha->scb_pool) {
|
||||
asd_printk("couldn't create scb pool\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_free_edbs -- free empty data buffers
|
||||
* asd_ha: pointer to host adapter structure
|
||||
*/
|
||||
static inline void asd_free_edbs(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
struct asd_seq_data *seq = &asd_ha->seq;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < seq->num_edbs; i++)
|
||||
asd_free_coherent(asd_ha, seq->edb_arr[i]);
|
||||
kfree(seq->edb_arr);
|
||||
seq->edb_arr = NULL;
|
||||
}
|
||||
|
||||
static inline void asd_free_escbs(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
struct asd_seq_data *seq = &asd_ha->seq;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < seq->num_escbs; i++) {
|
||||
if (!list_empty(&seq->escb_arr[i]->list))
|
||||
list_del_init(&seq->escb_arr[i]->list);
|
||||
|
||||
asd_ascb_free(seq->escb_arr[i]);
|
||||
}
|
||||
kfree(seq->escb_arr);
|
||||
seq->escb_arr = NULL;
|
||||
}
|
||||
|
||||
static inline void asd_destroy_ha_caches(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (asd_ha->hw_prof.ddb_ext)
|
||||
asd_free_coherent(asd_ha, asd_ha->hw_prof.ddb_ext);
|
||||
if (asd_ha->hw_prof.scb_ext)
|
||||
asd_free_coherent(asd_ha, asd_ha->hw_prof.scb_ext);
|
||||
|
||||
if (asd_ha->hw_prof.ddb_bitmap)
|
||||
kfree(asd_ha->hw_prof.ddb_bitmap);
|
||||
asd_ha->hw_prof.ddb_bitmap = NULL;
|
||||
|
||||
for (i = 0; i < ASD_MAX_PHYS; i++) {
|
||||
struct asd_phy *phy = &asd_ha->phys[i];
|
||||
|
||||
asd_free_coherent(asd_ha, phy->id_frm_tok);
|
||||
}
|
||||
if (asd_ha->seq.escb_arr)
|
||||
asd_free_escbs(asd_ha);
|
||||
if (asd_ha->seq.edb_arr)
|
||||
asd_free_edbs(asd_ha);
|
||||
if (asd_ha->hw_prof.ue.area) {
|
||||
kfree(asd_ha->hw_prof.ue.area);
|
||||
asd_ha->hw_prof.ue.area = NULL;
|
||||
}
|
||||
if (asd_ha->seq.tc_index_array) {
|
||||
kfree(asd_ha->seq.tc_index_array);
|
||||
kfree(asd_ha->seq.tc_index_bitmap);
|
||||
asd_ha->seq.tc_index_array = NULL;
|
||||
asd_ha->seq.tc_index_bitmap = NULL;
|
||||
}
|
||||
if (asd_ha->seq.actual_dl) {
|
||||
asd_free_coherent(asd_ha, asd_ha->seq.actual_dl);
|
||||
asd_ha->seq.actual_dl = NULL;
|
||||
asd_ha->seq.dl = NULL;
|
||||
}
|
||||
if (asd_ha->seq.next_scb.vaddr) {
|
||||
dma_pool_free(asd_ha->scb_pool, asd_ha->seq.next_scb.vaddr,
|
||||
asd_ha->seq.next_scb.dma_handle);
|
||||
asd_ha->seq.next_scb.vaddr = NULL;
|
||||
}
|
||||
dma_pool_destroy(asd_ha->scb_pool);
|
||||
asd_ha->scb_pool = NULL;
|
||||
}
|
||||
|
||||
kmem_cache_t *asd_dma_token_cache;
|
||||
kmem_cache_t *asd_ascb_cache;
|
||||
|
||||
static int asd_create_global_caches(void)
|
||||
{
|
||||
if (!asd_dma_token_cache) {
|
||||
asd_dma_token_cache
|
||||
= kmem_cache_create(ASD_DRIVER_NAME "_dma_token",
|
||||
sizeof(struct asd_dma_tok),
|
||||
0,
|
||||
SLAB_HWCACHE_ALIGN,
|
||||
NULL, NULL);
|
||||
if (!asd_dma_token_cache) {
|
||||
asd_printk("couldn't create dma token cache\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (!asd_ascb_cache) {
|
||||
asd_ascb_cache = kmem_cache_create(ASD_DRIVER_NAME "_ascb",
|
||||
sizeof(struct asd_ascb),
|
||||
0,
|
||||
SLAB_HWCACHE_ALIGN,
|
||||
NULL, NULL);
|
||||
if (!asd_ascb_cache) {
|
||||
asd_printk("couldn't create ascb cache\n");
|
||||
goto Err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
Err:
|
||||
kmem_cache_destroy(asd_dma_token_cache);
|
||||
asd_dma_token_cache = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void asd_destroy_global_caches(void)
|
||||
{
|
||||
if (asd_dma_token_cache)
|
||||
kmem_cache_destroy(asd_dma_token_cache);
|
||||
asd_dma_token_cache = NULL;
|
||||
|
||||
if (asd_ascb_cache)
|
||||
kmem_cache_destroy(asd_ascb_cache);
|
||||
asd_ascb_cache = NULL;
|
||||
}
|
||||
|
||||
static int asd_register_sas_ha(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int i;
|
||||
struct asd_sas_phy **sas_phys =
|
||||
kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_phy), GFP_KERNEL);
|
||||
struct asd_sas_port **sas_ports =
|
||||
kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_port), GFP_KERNEL);
|
||||
|
||||
if (!sas_phys || !sas_ports) {
|
||||
kfree(sas_phys);
|
||||
kfree(sas_ports);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
asd_ha->sas_ha.sas_ha_name = (char *) asd_ha->name;
|
||||
asd_ha->sas_ha.lldd_module = THIS_MODULE;
|
||||
asd_ha->sas_ha.sas_addr = &asd_ha->hw_prof.sas_addr[0];
|
||||
|
||||
for (i = 0; i < ASD_MAX_PHYS; i++) {
|
||||
sas_phys[i] = &asd_ha->phys[i].sas_phy;
|
||||
sas_ports[i] = &asd_ha->ports[i];
|
||||
}
|
||||
|
||||
asd_ha->sas_ha.sas_phy = sas_phys;
|
||||
asd_ha->sas_ha.sas_port= sas_ports;
|
||||
asd_ha->sas_ha.num_phys= ASD_MAX_PHYS;
|
||||
|
||||
asd_ha->sas_ha.lldd_queue_size = asd_ha->seq.can_queue;
|
||||
|
||||
return sas_register_ha(&asd_ha->sas_ha);
|
||||
}
|
||||
|
||||
static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = sas_unregister_ha(&asd_ha->sas_ha);
|
||||
|
||||
sas_remove_host(asd_ha->sas_ha.core.shost);
|
||||
scsi_remove_host(asd_ha->sas_ha.core.shost);
|
||||
scsi_host_put(asd_ha->sas_ha.core.shost);
|
||||
|
||||
kfree(asd_ha->sas_ha.sas_phy);
|
||||
kfree(asd_ha->sas_ha.sas_port);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devinit asd_pci_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct asd_pcidev_struct *asd_dev;
|
||||
unsigned asd_id = (unsigned) id->driver_data;
|
||||
struct asd_ha_struct *asd_ha;
|
||||
struct Scsi_Host *shost;
|
||||
int err;
|
||||
|
||||
if (asd_id >= ARRAY_SIZE(asd_pcidev_data)) {
|
||||
asd_printk("wrong driver_data in PCI table\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if ((err = pci_enable_device(dev))) {
|
||||
asd_printk("couldn't enable device %s\n", pci_name(dev));
|
||||
return err;
|
||||
}
|
||||
|
||||
pci_set_master(dev);
|
||||
|
||||
err = -ENOMEM;
|
||||
|
||||
shost = scsi_host_alloc(&aic94xx_sht, sizeof(void *));
|
||||
if (!shost)
|
||||
goto Err;
|
||||
|
||||
asd_dev = &asd_pcidev_data[asd_id];
|
||||
|
||||
asd_ha = kzalloc(sizeof(*asd_ha), GFP_KERNEL);
|
||||
if (!asd_ha) {
|
||||
asd_printk("out of memory\n");
|
||||
goto Err;
|
||||
}
|
||||
asd_ha->pcidev = dev;
|
||||
asd_ha->sas_ha.pcidev = asd_ha->pcidev;
|
||||
asd_ha->sas_ha.lldd_ha = asd_ha;
|
||||
|
||||
asd_ha->name = asd_dev->name;
|
||||
asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev));
|
||||
|
||||
SHOST_TO_SAS_HA(shost) = &asd_ha->sas_ha;
|
||||
asd_ha->sas_ha.core.shost = shost;
|
||||
shost->transportt = aic94xx_transport_template;
|
||||
shost->max_id = ~0;
|
||||
shost->max_lun = ~0;
|
||||
shost->max_cmd_len = 16;
|
||||
|
||||
err = scsi_add_host(shost, &dev->dev);
|
||||
if (err) {
|
||||
scsi_host_put(shost);
|
||||
goto Err_free;
|
||||
}
|
||||
|
||||
|
||||
|
||||
err = asd_dev->setup(asd_ha);
|
||||
if (err)
|
||||
goto Err_free;
|
||||
|
||||
err = -ENODEV;
|
||||
if (!pci_set_dma_mask(dev, DMA_64BIT_MASK)
|
||||
&& !pci_set_consistent_dma_mask(dev, DMA_64BIT_MASK))
|
||||
;
|
||||
else if (!pci_set_dma_mask(dev, DMA_32BIT_MASK)
|
||||
&& !pci_set_consistent_dma_mask(dev, DMA_32BIT_MASK))
|
||||
;
|
||||
else {
|
||||
asd_printk("no suitable DMA mask for %s\n", pci_name(dev));
|
||||
goto Err_free;
|
||||
}
|
||||
|
||||
pci_set_drvdata(dev, asd_ha);
|
||||
|
||||
err = asd_map_ha(asd_ha);
|
||||
if (err)
|
||||
goto Err_free;
|
||||
|
||||
err = asd_create_ha_caches(asd_ha);
|
||||
if (err)
|
||||
goto Err_unmap;
|
||||
|
||||
err = asd_init_hw(asd_ha);
|
||||
if (err)
|
||||
goto Err_free_cache;
|
||||
|
||||
asd_printk("device %s: SAS addr %llx, PCBA SN %s, %d phys, %d enabled "
|
||||
"phys, flash %s, BIOS %s%d\n",
|
||||
pci_name(dev), SAS_ADDR(asd_ha->hw_prof.sas_addr),
|
||||
asd_ha->hw_prof.pcba_sn, asd_ha->hw_prof.max_phys,
|
||||
asd_ha->hw_prof.num_phys,
|
||||
asd_ha->hw_prof.flash.present ? "present" : "not present",
|
||||
asd_ha->hw_prof.bios.present ? "build " : "not present",
|
||||
asd_ha->hw_prof.bios.bld);
|
||||
|
||||
if (use_msi)
|
||||
pci_enable_msi(asd_ha->pcidev);
|
||||
|
||||
err = request_irq(asd_ha->pcidev->irq, asd_hw_isr, SA_SHIRQ,
|
||||
ASD_DRIVER_NAME, asd_ha);
|
||||
if (err) {
|
||||
asd_printk("couldn't get irq %d for %s\n",
|
||||
asd_ha->pcidev->irq, pci_name(asd_ha->pcidev));
|
||||
goto Err_irq;
|
||||
}
|
||||
asd_enable_ints(asd_ha);
|
||||
|
||||
err = asd_init_post_escbs(asd_ha);
|
||||
if (err) {
|
||||
asd_printk("couldn't post escbs for %s\n",
|
||||
pci_name(asd_ha->pcidev));
|
||||
goto Err_escbs;
|
||||
}
|
||||
ASD_DPRINTK("escbs posted\n");
|
||||
|
||||
asd_create_dev_attrs(asd_ha);
|
||||
|
||||
err = asd_register_sas_ha(asd_ha);
|
||||
if (err)
|
||||
goto Err_reg_sas;
|
||||
|
||||
err = asd_enable_phys(asd_ha, asd_ha->hw_prof.enabled_phys);
|
||||
if (err) {
|
||||
asd_printk("coudln't enable phys, err:%d\n", err);
|
||||
goto Err_en_phys;
|
||||
}
|
||||
ASD_DPRINTK("enabled phys\n");
|
||||
/* give the phy enabling interrupt event time to come in (1s
|
||||
* is empirically about all it takes) */
|
||||
ssleep(1);
|
||||
/* Wait for discovery to finish */
|
||||
scsi_flush_work(asd_ha->sas_ha.core.shost);
|
||||
|
||||
return 0;
|
||||
Err_en_phys:
|
||||
asd_unregister_sas_ha(asd_ha);
|
||||
Err_reg_sas:
|
||||
asd_remove_dev_attrs(asd_ha);
|
||||
Err_escbs:
|
||||
asd_disable_ints(asd_ha);
|
||||
free_irq(dev->irq, asd_ha);
|
||||
Err_irq:
|
||||
if (use_msi)
|
||||
pci_disable_msi(dev);
|
||||
asd_chip_hardrst(asd_ha);
|
||||
Err_free_cache:
|
||||
asd_destroy_ha_caches(asd_ha);
|
||||
Err_unmap:
|
||||
asd_unmap_ha(asd_ha);
|
||||
Err_free:
|
||||
kfree(asd_ha);
|
||||
scsi_remove_host(shost);
|
||||
Err:
|
||||
pci_disable_device(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void asd_free_queues(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
unsigned long flags;
|
||||
LIST_HEAD(pending);
|
||||
struct list_head *n, *pos;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
|
||||
asd_ha->seq.pending = 0;
|
||||
list_splice_init(&asd_ha->seq.pend_q, &pending);
|
||||
spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
|
||||
|
||||
if (!list_empty(&pending))
|
||||
ASD_DPRINTK("Uh-oh! Pending is not empty!\n");
|
||||
|
||||
list_for_each_safe(pos, n, &pending) {
|
||||
struct asd_ascb *ascb = list_entry(pos, struct asd_ascb, list);
|
||||
list_del_init(pos);
|
||||
ASD_DPRINTK("freeing from pending\n");
|
||||
asd_ascb_free(ascb);
|
||||
}
|
||||
}
|
||||
|
||||
static void asd_turn_off_leds(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
u8 phy_mask = asd_ha->hw_prof.enabled_phys;
|
||||
u8 i;
|
||||
|
||||
for_each_phy(phy_mask, phy_mask, i) {
|
||||
asd_turn_led(asd_ha, i, 0);
|
||||
asd_control_led(asd_ha, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void __devexit asd_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = pci_get_drvdata(dev);
|
||||
|
||||
if (!asd_ha)
|
||||
return;
|
||||
|
||||
asd_unregister_sas_ha(asd_ha);
|
||||
|
||||
asd_disable_ints(asd_ha);
|
||||
|
||||
asd_remove_dev_attrs(asd_ha);
|
||||
|
||||
/* XXX more here as needed */
|
||||
|
||||
free_irq(dev->irq, asd_ha);
|
||||
if (use_msi)
|
||||
pci_disable_msi(asd_ha->pcidev);
|
||||
asd_turn_off_leds(asd_ha);
|
||||
asd_chip_hardrst(asd_ha);
|
||||
asd_free_queues(asd_ha);
|
||||
asd_destroy_ha_caches(asd_ha);
|
||||
asd_unmap_ha(asd_ha);
|
||||
kfree(asd_ha);
|
||||
pci_disable_device(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
static ssize_t asd_version_show(struct device_driver *driver, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", ASD_DRIVER_VERSION);
|
||||
}
|
||||
static DRIVER_ATTR(version, S_IRUGO, asd_version_show, NULL);
|
||||
|
||||
static void asd_create_driver_attrs(struct device_driver *driver)
|
||||
{
|
||||
driver_create_file(driver, &driver_attr_version);
|
||||
}
|
||||
|
||||
static void asd_remove_driver_attrs(struct device_driver *driver)
|
||||
{
|
||||
driver_remove_file(driver, &driver_attr_version);
|
||||
}
|
||||
|
||||
static struct sas_domain_function_template aic94xx_transport_functions = {
|
||||
.lldd_port_formed = asd_update_port_links,
|
||||
|
||||
.lldd_dev_found = asd_dev_found,
|
||||
.lldd_dev_gone = asd_dev_gone,
|
||||
|
||||
.lldd_execute_task = asd_execute_task,
|
||||
|
||||
.lldd_abort_task = asd_abort_task,
|
||||
.lldd_abort_task_set = asd_abort_task_set,
|
||||
.lldd_clear_aca = asd_clear_aca,
|
||||
.lldd_clear_task_set = asd_clear_task_set,
|
||||
.lldd_I_T_nexus_reset = NULL,
|
||||
.lldd_lu_reset = asd_lu_reset,
|
||||
.lldd_query_task = asd_query_task,
|
||||
|
||||
.lldd_clear_nexus_port = asd_clear_nexus_port,
|
||||
.lldd_clear_nexus_ha = asd_clear_nexus_ha,
|
||||
|
||||
.lldd_control_phy = asd_control_phy,
|
||||
};
|
||||
|
||||
static const struct pci_device_id aic94xx_pci_table[] __devinitdata = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR10),
|
||||
0, 0, 1},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR12),
|
||||
0, 0, 1},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR1E),
|
||||
0, 0, 1},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR30),
|
||||
0, 0, 2},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR32),
|
||||
0, 0, 2},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR3E),
|
||||
0, 0, 2},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR3F),
|
||||
0, 0, 2},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, aic94xx_pci_table);
|
||||
|
||||
static struct pci_driver aic94xx_pci_driver = {
|
||||
.name = ASD_DRIVER_NAME,
|
||||
.id_table = aic94xx_pci_table,
|
||||
.probe = asd_pci_probe,
|
||||
.remove = __devexit_p(asd_pci_remove),
|
||||
};
|
||||
|
||||
static int __init aic94xx_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
||||
asd_printk("%s version %s loaded\n", ASD_DRIVER_DESCRIPTION,
|
||||
ASD_DRIVER_VERSION);
|
||||
|
||||
err = asd_create_global_caches();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
aic94xx_transport_template =
|
||||
sas_domain_attach_transport(&aic94xx_transport_functions);
|
||||
if (err)
|
||||
goto out_destroy_caches;
|
||||
|
||||
err = pci_register_driver(&aic94xx_pci_driver);
|
||||
if (err)
|
||||
goto out_release_transport;
|
||||
|
||||
asd_create_driver_attrs(&aic94xx_pci_driver.driver);
|
||||
|
||||
return err;
|
||||
|
||||
out_release_transport:
|
||||
sas_release_transport(aic94xx_transport_template);
|
||||
out_destroy_caches:
|
||||
asd_destroy_global_caches();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit aic94xx_exit(void)
|
||||
{
|
||||
asd_remove_driver_attrs(&aic94xx_pci_driver.driver);
|
||||
pci_unregister_driver(&aic94xx_pci_driver);
|
||||
sas_release_transport(aic94xx_transport_template);
|
||||
asd_destroy_global_caches();
|
||||
asd_printk("%s version %s unloaded\n", ASD_DRIVER_DESCRIPTION,
|
||||
ASD_DRIVER_VERSION);
|
||||
}
|
||||
|
||||
module_init(aic94xx_init);
|
||||
module_exit(aic94xx_exit);
|
||||
|
||||
MODULE_AUTHOR("Luben Tuikov <luben_tuikov@adaptec.com>");
|
||||
MODULE_DESCRIPTION(ASD_DRIVER_DESCRIPTION);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_VERSION(ASD_DRIVER_VERSION);
|
||||
332
drivers/scsi/aic94xx/aic94xx_reg.c
Normal file
332
drivers/scsi/aic94xx/aic94xx_reg.c
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver register access.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include "aic94xx_reg.h"
|
||||
#include "aic94xx.h"
|
||||
|
||||
/* Writing to device address space.
|
||||
* Offset comes before value to remind that the operation of
|
||||
* this function is *offs = val.
|
||||
*/
|
||||
static inline void asd_write_byte(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs, u8 val)
|
||||
{
|
||||
if (unlikely(asd_ha->iospace))
|
||||
outb(val,
|
||||
(unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
|
||||
else
|
||||
writeb(val, asd_ha->io_handle[0].addr + offs);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static inline void asd_write_word(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs, u16 val)
|
||||
{
|
||||
if (unlikely(asd_ha->iospace))
|
||||
outw(val,
|
||||
(unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
|
||||
else
|
||||
writew(val, asd_ha->io_handle[0].addr + offs);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static inline void asd_write_dword(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs, u32 val)
|
||||
{
|
||||
if (unlikely(asd_ha->iospace))
|
||||
outl(val,
|
||||
(unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
|
||||
else
|
||||
writel(val, asd_ha->io_handle[0].addr + offs);
|
||||
wmb();
|
||||
}
|
||||
|
||||
/* Reading from device address space.
|
||||
*/
|
||||
static inline u8 asd_read_byte(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs)
|
||||
{
|
||||
u8 val;
|
||||
if (unlikely(asd_ha->iospace))
|
||||
val = inb((unsigned long) asd_ha->io_handle[0].addr
|
||||
+ (offs & 0xFF));
|
||||
else
|
||||
val = readb(asd_ha->io_handle[0].addr + offs);
|
||||
rmb();
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u16 asd_read_word(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs)
|
||||
{
|
||||
u16 val;
|
||||
if (unlikely(asd_ha->iospace))
|
||||
val = inw((unsigned long)asd_ha->io_handle[0].addr
|
||||
+ (offs & 0xFF));
|
||||
else
|
||||
val = readw(asd_ha->io_handle[0].addr + offs);
|
||||
rmb();
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 asd_read_dword(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs)
|
||||
{
|
||||
u32 val;
|
||||
if (unlikely(asd_ha->iospace))
|
||||
val = inl((unsigned long) asd_ha->io_handle[0].addr
|
||||
+ (offs & 0xFF));
|
||||
else
|
||||
val = readl(asd_ha->io_handle[0].addr + offs);
|
||||
rmb();
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 asd_mem_offs_swa(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 asd_mem_offs_swc(void)
|
||||
{
|
||||
return asd_mem_offs_swa() + MBAR0_SWA_SIZE;
|
||||
}
|
||||
|
||||
static inline u32 asd_mem_offs_swb(void)
|
||||
{
|
||||
return asd_mem_offs_swc() + MBAR0_SWC_SIZE + 0x20;
|
||||
}
|
||||
|
||||
/* We know that the register wanted is in the range
|
||||
* of the sliding window.
|
||||
*/
|
||||
#define ASD_READ_SW(ww, type, ord) \
|
||||
static inline type asd_read_##ww##_##ord (struct asd_ha_struct *asd_ha,\
|
||||
u32 reg) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; \
|
||||
u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\
|
||||
return asd_read_##ord (asd_ha, (unsigned long) map_offs); \
|
||||
}
|
||||
|
||||
#define ASD_WRITE_SW(ww, type, ord) \
|
||||
static inline void asd_write_##ww##_##ord (struct asd_ha_struct *asd_ha,\
|
||||
u32 reg, type val) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; \
|
||||
u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\
|
||||
asd_write_##ord (asd_ha, (unsigned long) map_offs, val); \
|
||||
}
|
||||
|
||||
ASD_READ_SW(swa, u8, byte);
|
||||
ASD_READ_SW(swa, u16, word);
|
||||
ASD_READ_SW(swa, u32, dword);
|
||||
|
||||
ASD_READ_SW(swb, u8, byte);
|
||||
ASD_READ_SW(swb, u16, word);
|
||||
ASD_READ_SW(swb, u32, dword);
|
||||
|
||||
ASD_READ_SW(swc, u8, byte);
|
||||
ASD_READ_SW(swc, u16, word);
|
||||
ASD_READ_SW(swc, u32, dword);
|
||||
|
||||
ASD_WRITE_SW(swa, u8, byte);
|
||||
ASD_WRITE_SW(swa, u16, word);
|
||||
ASD_WRITE_SW(swa, u32, dword);
|
||||
|
||||
ASD_WRITE_SW(swb, u8, byte);
|
||||
ASD_WRITE_SW(swb, u16, word);
|
||||
ASD_WRITE_SW(swb, u32, dword);
|
||||
|
||||
ASD_WRITE_SW(swc, u8, byte);
|
||||
ASD_WRITE_SW(swc, u16, word);
|
||||
ASD_WRITE_SW(swc, u32, dword);
|
||||
|
||||
/*
|
||||
* A word about sliding windows:
|
||||
* MBAR0 is divided into sliding windows A, C and B, in that order.
|
||||
* SWA starts at offset 0 of MBAR0, up to 0x57, with size 0x58 bytes.
|
||||
* SWC starts at offset 0x58 of MBAR0, up to 0x60, with size 0x8 bytes.
|
||||
* From 0x60 to 0x7F, we have a copy of PCI config space 0x60-0x7F.
|
||||
* SWB starts at offset 0x80 of MBAR0 and extends to the end of MBAR0.
|
||||
* See asd_init_sw() in aic94xx_hwi.c
|
||||
*
|
||||
* We map the most common registers we'd access of the internal 4GB
|
||||
* host adapter memory space. If a register/internal memory location
|
||||
* is wanted which is not mapped, we slide SWB, by paging it,
|
||||
* see asd_move_swb() in aic94xx_reg.c.
|
||||
*/
|
||||
|
||||
/**
|
||||
* asd_move_swb -- move sliding window B
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @reg: register desired to be within range of the new window
|
||||
*/
|
||||
static inline void asd_move_swb(struct asd_ha_struct *asd_ha, u32 reg)
|
||||
{
|
||||
u32 base = reg & ~(MBAR0_SWB_SIZE-1);
|
||||
pci_write_config_dword(asd_ha->pcidev, PCI_CONF_MBAR0_SWB, base);
|
||||
asd_ha->io_handle[0].swb_base = base;
|
||||
}
|
||||
|
||||
static void __asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val)
|
||||
{
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
|
||||
if (io_handle->swa_base <= reg
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE)
|
||||
asd_write_swa_byte (asd_ha, reg,val);
|
||||
else if (io_handle->swb_base <= reg
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE)
|
||||
asd_write_swb_byte (asd_ha, reg, val);
|
||||
else if (io_handle->swc_base <= reg
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE)
|
||||
asd_write_swc_byte (asd_ha, reg, val);
|
||||
else {
|
||||
/* Ok, we have to move SWB */
|
||||
asd_move_swb(asd_ha, reg);
|
||||
asd_write_swb_byte (asd_ha, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
#define ASD_WRITE_REG(type, ord) \
|
||||
void asd_write_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg, type val)\
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
|
||||
unsigned long flags; \
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); \
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags); \
|
||||
if (io_handle->swa_base <= reg \
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE) \
|
||||
asd_write_swa_##ord (asd_ha, reg,val); \
|
||||
else if (io_handle->swb_base <= reg \
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE) \
|
||||
asd_write_swb_##ord (asd_ha, reg, val); \
|
||||
else if (io_handle->swc_base <= reg \
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE) \
|
||||
asd_write_swc_##ord (asd_ha, reg, val); \
|
||||
else { \
|
||||
/* Ok, we have to move SWB */ \
|
||||
asd_move_swb(asd_ha, reg); \
|
||||
asd_write_swb_##ord (asd_ha, reg, val); \
|
||||
} \
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags); \
|
||||
}
|
||||
|
||||
ASD_WRITE_REG(u8, byte);
|
||||
ASD_WRITE_REG(u16,word);
|
||||
ASD_WRITE_REG(u32,dword);
|
||||
|
||||
static u8 __asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg)
|
||||
{
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
|
||||
u8 val;
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
|
||||
if (io_handle->swa_base <= reg
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE)
|
||||
val = asd_read_swa_byte (asd_ha, reg);
|
||||
else if (io_handle->swb_base <= reg
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE)
|
||||
val = asd_read_swb_byte (asd_ha, reg);
|
||||
else if (io_handle->swc_base <= reg
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE)
|
||||
val = asd_read_swc_byte (asd_ha, reg);
|
||||
else {
|
||||
/* Ok, we have to move SWB */
|
||||
asd_move_swb(asd_ha, reg);
|
||||
val = asd_read_swb_byte (asd_ha, reg);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
#define ASD_READ_REG(type, ord) \
|
||||
type asd_read_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
|
||||
type val; \
|
||||
unsigned long flags; \
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); \
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags); \
|
||||
if (io_handle->swa_base <= reg \
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE) \
|
||||
val = asd_read_swa_##ord (asd_ha, reg); \
|
||||
else if (io_handle->swb_base <= reg \
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE) \
|
||||
val = asd_read_swb_##ord (asd_ha, reg); \
|
||||
else if (io_handle->swc_base <= reg \
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE) \
|
||||
val = asd_read_swc_##ord (asd_ha, reg); \
|
||||
else { \
|
||||
/* Ok, we have to move SWB */ \
|
||||
asd_move_swb(asd_ha, reg); \
|
||||
val = asd_read_swb_##ord (asd_ha, reg); \
|
||||
} \
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
ASD_READ_REG(u8, byte);
|
||||
ASD_READ_REG(u16,word);
|
||||
ASD_READ_REG(u32,dword);
|
||||
|
||||
/**
|
||||
* asd_read_reg_string -- read a string of bytes from io space memory
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @dst: pointer to a destination buffer where data will be written to
|
||||
* @offs: start offset (register) to read from
|
||||
* @count: number of bytes to read
|
||||
*/
|
||||
void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst,
|
||||
u32 offs, int count)
|
||||
{
|
||||
u8 *p = dst;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags);
|
||||
for ( ; count > 0; count--, offs++, p++)
|
||||
*p = __asd_read_reg_byte(asd_ha, offs);
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_write_reg_string -- write a string of bytes to io space memory
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @src: pointer to source buffer where data will be read from
|
||||
* @offs: start offset (register) to write to
|
||||
* @count: number of bytes to write
|
||||
*/
|
||||
void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src,
|
||||
u32 offs, int count)
|
||||
{
|
||||
u8 *p = src;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags);
|
||||
for ( ; count > 0; count--, offs++, p++)
|
||||
__asd_write_reg_byte(asd_ha, offs, *p);
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags);
|
||||
}
|
||||
302
drivers/scsi/aic94xx/aic94xx_reg.h
Normal file
302
drivers/scsi/aic94xx/aic94xx_reg.h
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver hardware registers definitions.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AIC94XX_REG_H_
|
||||
#define _AIC94XX_REG_H_
|
||||
|
||||
#include <asm/io.h>
|
||||
#include "aic94xx_hwi.h"
|
||||
|
||||
/* Values */
|
||||
#define AIC9410_DEV_REV_B0 0x8
|
||||
|
||||
/* MBAR0, SWA, SWB, SWC, internal memory space addresses */
|
||||
#define REG_BASE_ADDR 0xB8000000
|
||||
#define REG_BASE_ADDR_CSEQCIO 0xB8002000
|
||||
#define REG_BASE_ADDR_EXSI 0xB8042800
|
||||
|
||||
#define MBAR0_SWA_SIZE 0x58
|
||||
extern u32 MBAR0_SWB_SIZE;
|
||||
#define MBAR0_SWC_SIZE 0x8
|
||||
|
||||
/* MBAR1, points to On Chip Memory */
|
||||
#define OCM_BASE_ADDR 0xA0000000
|
||||
#define OCM_MAX_SIZE 0x20000
|
||||
|
||||
/* Smallest address possible to reference */
|
||||
#define ALL_BASE_ADDR OCM_BASE_ADDR
|
||||
|
||||
/* PCI configuration space registers */
|
||||
#define PCI_IOBAR_OFFSET 4
|
||||
|
||||
#define PCI_CONF_MBAR1 0x6C
|
||||
#define PCI_CONF_MBAR0_SWA 0x70
|
||||
#define PCI_CONF_MBAR0_SWB 0x74
|
||||
#define PCI_CONF_MBAR0_SWC 0x78
|
||||
#define PCI_CONF_MBAR_KEY 0x7C
|
||||
#define PCI_CONF_FLSH_BAR 0xB8
|
||||
|
||||
#include "aic94xx_reg_def.h"
|
||||
|
||||
u8 asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg);
|
||||
u16 asd_read_reg_word(struct asd_ha_struct *asd_ha, u32 reg);
|
||||
u32 asd_read_reg_dword(struct asd_ha_struct *asd_ha, u32 reg);
|
||||
|
||||
void asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val);
|
||||
void asd_write_reg_word(struct asd_ha_struct *asd_ha, u32 reg, u16 val);
|
||||
void asd_write_reg_dword(struct asd_ha_struct *asd_ha, u32 reg, u32 val);
|
||||
|
||||
void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst,
|
||||
u32 offs, int count);
|
||||
void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src,
|
||||
u32 offs, int count);
|
||||
|
||||
#define ASD_READ_OCM(type, ord, S) \
|
||||
static inline type asd_read_ocm_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u32 offs) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[1]; \
|
||||
type val = read##S (io_handle->addr + (unsigned long) offs); \
|
||||
rmb(); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
ASD_READ_OCM(u8, byte, b);
|
||||
ASD_READ_OCM(u16,word, w);
|
||||
ASD_READ_OCM(u32,dword,l);
|
||||
|
||||
#define ASD_WRITE_OCM(type, ord, S) \
|
||||
static inline void asd_write_ocm_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u32 offs, type val) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[1]; \
|
||||
write##S (val, io_handle->addr + (unsigned long) offs); \
|
||||
return; \
|
||||
}
|
||||
|
||||
ASD_WRITE_OCM(u8, byte, b);
|
||||
ASD_WRITE_OCM(u16,word, w);
|
||||
ASD_WRITE_OCM(u32,dword,l);
|
||||
|
||||
#define ASD_DDBSITE_READ(type, ord) \
|
||||
static inline type asd_ddbsite_read_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 ddb_site_no, \
|
||||
u16 offs) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnDDB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ADDBPTR, ddb_site_no); \
|
||||
return asd_read_reg_##ord (asd_ha, CTXACCESS); \
|
||||
}
|
||||
|
||||
ASD_DDBSITE_READ(u32, dword);
|
||||
ASD_DDBSITE_READ(u16, word);
|
||||
|
||||
static inline u8 asd_ddbsite_read_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no,
|
||||
u16 offs)
|
||||
{
|
||||
if (offs & 1)
|
||||
return asd_ddbsite_read_word(asd_ha, ddb_site_no,
|
||||
offs & ~1) >> 8;
|
||||
else
|
||||
return asd_ddbsite_read_word(asd_ha, ddb_site_no,
|
||||
offs) & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
#define ASD_DDBSITE_WRITE(type, ord) \
|
||||
static inline void asd_ddbsite_write_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 ddb_site_no, \
|
||||
u16 offs, type val) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnDDB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ADDBPTR, ddb_site_no); \
|
||||
asd_write_reg_##ord (asd_ha, CTXACCESS, val); \
|
||||
}
|
||||
|
||||
ASD_DDBSITE_WRITE(u32, dword);
|
||||
ASD_DDBSITE_WRITE(u16, word);
|
||||
|
||||
static inline void asd_ddbsite_write_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no,
|
||||
u16 offs, u8 val)
|
||||
{
|
||||
u16 base = offs & ~1;
|
||||
u16 rval = asd_ddbsite_read_word(asd_ha, ddb_site_no, base);
|
||||
if (offs & 1)
|
||||
rval = (val << 8) | (rval & 0xFF);
|
||||
else
|
||||
rval = (rval & 0xFF00) | val;
|
||||
asd_ddbsite_write_word(asd_ha, ddb_site_no, base, rval);
|
||||
}
|
||||
|
||||
|
||||
#define ASD_SCBSITE_READ(type, ord) \
|
||||
static inline type asd_scbsite_read_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 scb_site_no, \
|
||||
u16 offs) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnSCB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ASCBPTR, scb_site_no); \
|
||||
return asd_read_reg_##ord (asd_ha, CTXACCESS); \
|
||||
}
|
||||
|
||||
ASD_SCBSITE_READ(u32, dword);
|
||||
ASD_SCBSITE_READ(u16, word);
|
||||
|
||||
static inline u8 asd_scbsite_read_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 scb_site_no,
|
||||
u16 offs)
|
||||
{
|
||||
if (offs & 1)
|
||||
return asd_scbsite_read_word(asd_ha, scb_site_no,
|
||||
offs & ~1) >> 8;
|
||||
else
|
||||
return asd_scbsite_read_word(asd_ha, scb_site_no,
|
||||
offs) & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
#define ASD_SCBSITE_WRITE(type, ord) \
|
||||
static inline void asd_scbsite_write_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 scb_site_no, \
|
||||
u16 offs, type val) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnSCB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ASCBPTR, scb_site_no); \
|
||||
asd_write_reg_##ord (asd_ha, CTXACCESS, val); \
|
||||
}
|
||||
|
||||
ASD_SCBSITE_WRITE(u32, dword);
|
||||
ASD_SCBSITE_WRITE(u16, word);
|
||||
|
||||
static inline void asd_scbsite_write_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 scb_site_no,
|
||||
u16 offs, u8 val)
|
||||
{
|
||||
u16 base = offs & ~1;
|
||||
u16 rval = asd_scbsite_read_word(asd_ha, scb_site_no, base);
|
||||
if (offs & 1)
|
||||
rval = (val << 8) | (rval & 0xFF);
|
||||
else
|
||||
rval = (rval & 0xFF00) | val;
|
||||
asd_scbsite_write_word(asd_ha, scb_site_no, base, rval);
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_ddbsite_update_word -- atomically update a word in a ddb site
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @ddb_site_no: the DDB site number
|
||||
* @offs: the offset into the DDB
|
||||
* @oldval: old value found in that offset
|
||||
* @newval: the new value to replace it
|
||||
*
|
||||
* This function is used when the sequencers are running and we need to
|
||||
* update a DDB site atomically without expensive pausing and upausing
|
||||
* of the sequencers and accessing the DDB site through the CIO bus.
|
||||
*
|
||||
* Return 0 on success; -EFAULT on parity error; -EAGAIN if the old value
|
||||
* is different than the current value at that offset.
|
||||
*/
|
||||
static inline int asd_ddbsite_update_word(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no, u16 offs,
|
||||
u16 oldval, u16 newval)
|
||||
{
|
||||
u8 done;
|
||||
u16 oval = asd_ddbsite_read_word(asd_ha, ddb_site_no, offs);
|
||||
if (oval != oldval)
|
||||
return -EAGAIN;
|
||||
asd_write_reg_word(asd_ha, AOLDDATA, oldval);
|
||||
asd_write_reg_word(asd_ha, ANEWDATA, newval);
|
||||
do {
|
||||
done = asd_read_reg_byte(asd_ha, ATOMICSTATCTL);
|
||||
} while (!(done & ATOMICDONE));
|
||||
if (done & ATOMICERR)
|
||||
return -EFAULT; /* parity error */
|
||||
else if (done & ATOMICWIN)
|
||||
return 0; /* success */
|
||||
else
|
||||
return -EAGAIN; /* oldval different than current value */
|
||||
}
|
||||
|
||||
static inline int asd_ddbsite_update_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no, u16 offs,
|
||||
u8 _oldval, u8 _newval)
|
||||
{
|
||||
u16 base = offs & ~1;
|
||||
u16 oval;
|
||||
u16 nval = asd_ddbsite_read_word(asd_ha, ddb_site_no, base);
|
||||
if (offs & 1) {
|
||||
if ((nval >> 8) != _oldval)
|
||||
return -EAGAIN;
|
||||
nval = (_newval << 8) | (nval & 0xFF);
|
||||
oval = (_oldval << 8) | (nval & 0xFF);
|
||||
} else {
|
||||
if ((nval & 0xFF) != _oldval)
|
||||
return -EAGAIN;
|
||||
nval = (nval & 0xFF00) | _newval;
|
||||
oval = (nval & 0xFF00) | _oldval;
|
||||
}
|
||||
return asd_ddbsite_update_word(asd_ha, ddb_site_no, base, oval, nval);
|
||||
}
|
||||
|
||||
static inline void asd_write_reg_addr(struct asd_ha_struct *asd_ha, u32 reg,
|
||||
dma_addr_t dma_handle)
|
||||
{
|
||||
asd_write_reg_dword(asd_ha, reg, ASD_BUSADDR_LO(dma_handle));
|
||||
asd_write_reg_dword(asd_ha, reg+4, ASD_BUSADDR_HI(dma_handle));
|
||||
}
|
||||
|
||||
static inline u32 asd_get_cmdctx_size(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
/* DCHREVISION returns 0, possibly broken */
|
||||
u32 ctxmemsize = asd_read_reg_dword(asd_ha, LmMnINT(0,0)) & CTXMEMSIZE;
|
||||
return ctxmemsize ? 65536 : 32768;
|
||||
}
|
||||
|
||||
static inline u32 asd_get_devctx_size(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
u32 ctxmemsize = asd_read_reg_dword(asd_ha, LmMnINT(0,0)) & CTXMEMSIZE;
|
||||
return ctxmemsize ? 8192 : 4096;
|
||||
}
|
||||
|
||||
static inline void asd_disable_ints(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
asd_write_reg_dword(asd_ha, CHIMINTEN, RST_CHIMINTEN);
|
||||
}
|
||||
|
||||
static inline void asd_enable_ints(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
/* Enable COM SAS interrupt on errors, COMSTAT */
|
||||
asd_write_reg_dword(asd_ha, COMSTATEN,
|
||||
EN_CSBUFPERR | EN_CSERR | EN_OVLYERR);
|
||||
/* Enable DCH SAS CFIFTOERR */
|
||||
asd_write_reg_dword(asd_ha, DCHSTATUS, EN_CFIFTOERR);
|
||||
/* Enable Host Device interrupts */
|
||||
asd_write_reg_dword(asd_ha, CHIMINTEN, SET_CHIMINTEN);
|
||||
}
|
||||
|
||||
#endif
|
||||
2398
drivers/scsi/aic94xx/aic94xx_reg_def.h
Normal file
2398
drivers/scsi/aic94xx/aic94xx_reg_def.h
Normal file
File diff suppressed because it is too large
Load Diff
785
drivers/scsi/aic94xx/aic94xx_sas.h
Normal file
785
drivers/scsi/aic94xx/aic94xx_sas.h
Normal file
@@ -0,0 +1,785 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver SAS definitions and hardware interface header file.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AIC94XX_SAS_H_
|
||||
#define _AIC94XX_SAS_H_
|
||||
|
||||
#include <scsi/libsas.h>
|
||||
|
||||
/* ---------- DDBs ---------- */
|
||||
/* DDBs are device descriptor blocks which describe a device in the
|
||||
* domain that this sequencer can maintain low-level connections for
|
||||
* us. They are be 64 bytes.
|
||||
*/
|
||||
|
||||
struct asd_ddb_ssp_smp_target_port {
|
||||
u8 conn_type; /* byte 0 */
|
||||
#define DDB_TP_CONN_TYPE 0x81 /* Initiator port and addr frame type 0x01 */
|
||||
|
||||
u8 conn_rate;
|
||||
__be16 init_conn_tag;
|
||||
u8 dest_sas_addr[8]; /* bytes 4-11 */
|
||||
|
||||
__le16 send_queue_head;
|
||||
u8 sq_suspended;
|
||||
u8 ddb_type; /* DDB_TYPE_TARGET */
|
||||
#define DDB_TYPE_UNUSED 0xFF
|
||||
#define DDB_TYPE_TARGET 0xFE
|
||||
#define DDB_TYPE_INITIATOR 0xFD
|
||||
#define DDB_TYPE_PM_PORT 0xFC
|
||||
|
||||
__le16 _r_a;
|
||||
__be16 awt_def;
|
||||
|
||||
u8 compat_features; /* byte 20 */
|
||||
u8 pathway_blocked_count;
|
||||
__be16 arb_wait_time;
|
||||
__be32 more_compat_features; /* byte 24 */
|
||||
|
||||
u8 conn_mask;
|
||||
u8 flags; /* concurrent conn:2,2 and open:0(1) */
|
||||
#define CONCURRENT_CONN_SUPP 0x04
|
||||
#define OPEN_REQUIRED 0x01
|
||||
|
||||
u16 _r_b;
|
||||
__le16 exec_queue_tail;
|
||||
__le16 send_queue_tail;
|
||||
__le16 sister_ddb;
|
||||
|
||||
__le16 _r_c;
|
||||
|
||||
u8 max_concurrent_conn;
|
||||
u8 num_concurrent_conn;
|
||||
u8 num_contexts;
|
||||
|
||||
u8 _r_d;
|
||||
|
||||
__le16 active_task_count;
|
||||
|
||||
u8 _r_e[9];
|
||||
|
||||
u8 itnl_reason; /* I_T nexus loss reason */
|
||||
|
||||
__le16 _r_f;
|
||||
|
||||
__le16 itnl_timeout;
|
||||
#define ITNL_TIMEOUT_CONST 0x7D0 /* 2 seconds */
|
||||
|
||||
__le32 itnl_timestamp;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct asd_ddb_stp_sata_target_port {
|
||||
u8 conn_type; /* byte 0 */
|
||||
u8 conn_rate;
|
||||
__be16 init_conn_tag;
|
||||
u8 dest_sas_addr[8]; /* bytes 4-11 */
|
||||
|
||||
__le16 send_queue_head;
|
||||
u8 sq_suspended;
|
||||
u8 ddb_type; /* DDB_TYPE_TARGET */
|
||||
|
||||
__le16 _r_a;
|
||||
|
||||
__be16 awt_def;
|
||||
u8 compat_features; /* byte 20 */
|
||||
u8 pathway_blocked_count;
|
||||
__be16 arb_wait_time;
|
||||
__be32 more_compat_features; /* byte 24 */
|
||||
|
||||
u8 conn_mask;
|
||||
u8 flags; /* concurrent conn:2,2 and open:0(1) */
|
||||
#define SATA_MULTIPORT 0x80
|
||||
#define SUPPORTS_AFFIL 0x40
|
||||
#define STP_AFFIL_POL 0x20
|
||||
|
||||
u8 _r_b;
|
||||
u8 flags2; /* STP close policy:0 */
|
||||
#define STP_CL_POL_NO_TX 0x00
|
||||
#define STP_CL_POL_BTW_CMDS 0x01
|
||||
|
||||
__le16 exec_queue_tail;
|
||||
__le16 send_queue_tail;
|
||||
__le16 sister_ddb;
|
||||
__le16 ata_cmd_scbptr;
|
||||
__le32 sata_tag_alloc_mask;
|
||||
__le16 active_task_count;
|
||||
__le16 _r_c;
|
||||
__le32 sata_sactive;
|
||||
u8 num_sata_tags;
|
||||
u8 sata_status;
|
||||
u8 sata_ending_status;
|
||||
u8 itnl_reason; /* I_T nexus loss reason */
|
||||
__le16 ncq_data_scb_ptr;
|
||||
__le16 itnl_timeout;
|
||||
__le32 itnl_timestamp;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This struct asd_ddb_init_port, describes the device descriptor block
|
||||
* of an initiator port (when the sequencer is operating in target mode).
|
||||
* Bytes [0,11] and [20,27] are from the OPEN address frame.
|
||||
* The sequencer allocates an initiator port DDB entry.
|
||||
*/
|
||||
struct asd_ddb_init_port {
|
||||
u8 conn_type; /* byte 0 */
|
||||
u8 conn_rate;
|
||||
__be16 init_conn_tag; /* BE */
|
||||
u8 dest_sas_addr[8];
|
||||
__le16 send_queue_head; /* LE, byte 12 */
|
||||
u8 sq_suspended;
|
||||
u8 ddb_type; /* DDB_TYPE_INITIATOR */
|
||||
__le16 _r_a;
|
||||
__be16 awt_def; /* BE */
|
||||
u8 compat_features;
|
||||
u8 pathway_blocked_count;
|
||||
__be16 arb_wait_time; /* BE */
|
||||
__be32 more_compat_features; /* BE */
|
||||
u8 conn_mask;
|
||||
u8 flags; /* == 5 */
|
||||
u16 _r_b;
|
||||
__le16 exec_queue_tail; /* execution queue tail */
|
||||
__le16 send_queue_tail;
|
||||
__le16 sister_ddb;
|
||||
__le16 init_resp_timeout; /* initiator response timeout */
|
||||
__le32 _r_c;
|
||||
__le16 active_tasks; /* active task count */
|
||||
__le16 init_list; /* initiator list link pointer */
|
||||
__le32 _r_d;
|
||||
u8 max_conn_to[3]; /* from Conn-Disc mode page, in us, LE */
|
||||
u8 itnl_reason; /* I_T nexus loss reason */
|
||||
__le16 bus_inact_to; /* from Conn-Disc mode page, in 100 us, LE */
|
||||
__le16 itnl_to; /* from the Protocol Specific Port Ctrl MP */
|
||||
__le32 itnl_timestamp;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This struct asd_ddb_sata_tag, describes a look-up table to be used
|
||||
* by the sequencers. SATA II, IDENTIFY DEVICE data, word 76, bit 8:
|
||||
* NCQ support. This table is used by the sequencers to find the
|
||||
* corresponding SCB, given a SATA II tag value.
|
||||
*/
|
||||
struct asd_ddb_sata_tag {
|
||||
__le16 scb_pointer[32];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This struct asd_ddb_sata_pm_table, describes a port number to
|
||||
* connection handle look-up table. SATA targets attached to a port
|
||||
* multiplier require a 4-bit port number value. There is one DDB
|
||||
* entry of this type for each SATA port multiplier (sister DDB).
|
||||
* Given a SATA PM port number, this table gives us the SATA PM Port
|
||||
* DDB of the SATA port multiplier port (i.e. the SATA target
|
||||
* discovered on the port).
|
||||
*/
|
||||
struct asd_ddb_sata_pm_table {
|
||||
__le16 ddb_pointer[16];
|
||||
__le16 _r_a[16];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This struct asd_ddb_sata_pm_port, describes the SATA port multiplier
|
||||
* port format DDB.
|
||||
*/
|
||||
struct asd_ddb_sata_pm_port {
|
||||
u8 _r_a[15];
|
||||
u8 ddb_type;
|
||||
u8 _r_b[13];
|
||||
u8 pm_port_flags;
|
||||
#define PM_PORT_MASK 0xF0
|
||||
#define PM_PORT_SET 0x02
|
||||
u8 _r_c[6];
|
||||
__le16 sister_ddb;
|
||||
__le16 ata_cmd_scbptr;
|
||||
__le32 sata_tag_alloc_mask;
|
||||
__le16 active_task_count;
|
||||
__le16 parent_ddb;
|
||||
__le32 sata_sactive;
|
||||
u8 num_sata_tags;
|
||||
u8 sata_status;
|
||||
u8 sata_ending_status;
|
||||
u8 _r_d[9];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This struct asd_ddb_seq_shared, describes a DDB shared by the
|
||||
* central and link sequencers. port_map_by_links is indexed phy
|
||||
* number [0,7]; each byte is a bit mask of all the phys that are in
|
||||
* the same port as the indexed phy.
|
||||
*/
|
||||
struct asd_ddb_seq_shared {
|
||||
__le16 q_free_ddb_head;
|
||||
__le16 q_free_ddb_tail;
|
||||
__le16 q_free_ddb_cnt;
|
||||
__le16 q_used_ddb_head;
|
||||
__le16 q_used_ddb_tail;
|
||||
__le16 shared_mem_lock;
|
||||
__le16 smp_conn_tag;
|
||||
__le16 est_nexus_buf_cnt;
|
||||
__le16 est_nexus_buf_thresh;
|
||||
u32 _r_a;
|
||||
u8 settable_max_contexts;
|
||||
u8 _r_b[23];
|
||||
u8 conn_not_active;
|
||||
u8 phy_is_up;
|
||||
u8 _r_c[8];
|
||||
u8 port_map_by_links[8];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- SG Element ---------- */
|
||||
|
||||
/* This struct sg_el, describes the hardware scatter gather buffer
|
||||
* element. All entries are little endian. In an SCB, there are 2 of
|
||||
* this, plus one more, called a link element of this indicating a
|
||||
* sublist if needed.
|
||||
*
|
||||
* A link element has only the bus address set and the flags (DS) bit
|
||||
* valid. The bus address points to the start of the sublist.
|
||||
*
|
||||
* If a sublist is needed, then that sublist should also include the 2
|
||||
* sg_el embedded in the SCB, in which case next_sg_offset is 32,
|
||||
* since sizeof(sg_el) = 16; EOS should be 1 and EOL 0 in this case.
|
||||
*/
|
||||
struct sg_el {
|
||||
__le64 bus_addr;
|
||||
__le32 size;
|
||||
__le16 _r;
|
||||
u8 next_sg_offs;
|
||||
u8 flags;
|
||||
#define ASD_SG_EL_DS_MASK 0x30
|
||||
#define ASD_SG_EL_DS_OCM 0x10
|
||||
#define ASD_SG_EL_DS_HM 0x00
|
||||
#define ASD_SG_EL_LIST_MASK 0xC0
|
||||
#define ASD_SG_EL_LIST_EOL 0x40
|
||||
#define ASD_SG_EL_LIST_EOS 0x80
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- SCBs ---------- */
|
||||
|
||||
/* An SCB (sequencer control block) is comprised of a common header
|
||||
* and a task part, for a total of 128 bytes. All fields are in LE
|
||||
* order, unless otherwise noted.
|
||||
*/
|
||||
|
||||
/* This struct scb_header, defines the SCB header format.
|
||||
*/
|
||||
struct scb_header {
|
||||
__le64 next_scb;
|
||||
__le16 index; /* transaction context */
|
||||
u8 opcode;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* SCB opcodes: Execution queue
|
||||
*/
|
||||
#define INITIATE_SSP_TASK 0x00
|
||||
#define INITIATE_LONG_SSP_TASK 0x01
|
||||
#define INITIATE_BIDIR_SSP_TASK 0x02
|
||||
#define ABORT_TASK 0x03
|
||||
#define INITIATE_SSP_TMF 0x04
|
||||
#define SSP_TARG_GET_DATA 0x05
|
||||
#define SSP_TARG_GET_DATA_GOOD 0x06
|
||||
#define SSP_TARG_SEND_RESP 0x07
|
||||
#define QUERY_SSP_TASK 0x08
|
||||
#define INITIATE_ATA_TASK 0x09
|
||||
#define INITIATE_ATAPI_TASK 0x0a
|
||||
#define CONTROL_ATA_DEV 0x0b
|
||||
#define INITIATE_SMP_TASK 0x0c
|
||||
#define SMP_TARG_SEND_RESP 0x0f
|
||||
|
||||
/* SCB opcodes: Send Queue
|
||||
*/
|
||||
#define SSP_TARG_SEND_DATA 0x40
|
||||
#define SSP_TARG_SEND_DATA_GOOD 0x41
|
||||
|
||||
/* SCB opcodes: Link Queue
|
||||
*/
|
||||
#define CONTROL_PHY 0x80
|
||||
#define SEND_PRIMITIVE 0x81
|
||||
#define INITIATE_LINK_ADM_TASK 0x82
|
||||
|
||||
/* SCB opcodes: other
|
||||
*/
|
||||
#define EMPTY_SCB 0xc0
|
||||
#define INITIATE_SEQ_ADM_TASK 0xc1
|
||||
#define EST_ICL_TARG_WINDOW 0xc2
|
||||
#define COPY_MEM 0xc3
|
||||
#define CLEAR_NEXUS 0xc4
|
||||
#define INITIATE_DDB_ADM_TASK 0xc6
|
||||
#define ESTABLISH_NEXUS_ESCB 0xd0
|
||||
|
||||
#define LUN_SIZE 8
|
||||
|
||||
/* See SAS spec, task IU
|
||||
*/
|
||||
struct ssp_task_iu {
|
||||
u8 lun[LUN_SIZE]; /* BE */
|
||||
u16 _r_a;
|
||||
u8 tmf;
|
||||
u8 _r_b;
|
||||
__be16 tag; /* BE */
|
||||
u8 _r_c[14];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* See SAS spec, command IU
|
||||
*/
|
||||
struct ssp_command_iu {
|
||||
u8 lun[LUN_SIZE];
|
||||
u8 _r_a;
|
||||
u8 efb_prio_attr; /* enable first burst, task prio & attr */
|
||||
#define EFB_MASK 0x80
|
||||
#define TASK_PRIO_MASK 0x78
|
||||
#define TASK_ATTR_MASK 0x07
|
||||
|
||||
u8 _r_b;
|
||||
u8 add_cdb_len; /* in dwords, since bit 0,1 are reserved */
|
||||
union {
|
||||
u8 cdb[16];
|
||||
struct {
|
||||
__le64 long_cdb_addr; /* bus address, LE */
|
||||
__le32 long_cdb_size; /* LE */
|
||||
u8 _r_c[3];
|
||||
u8 eol_ds; /* eol:6,6, ds:5,4 */
|
||||
} long_cdb; /* sequencer extension */
|
||||
};
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct xfer_rdy_iu {
|
||||
__be32 requested_offset; /* BE */
|
||||
__be32 write_data_len; /* BE */
|
||||
__be32 _r_a;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- SCB tasks ---------- */
|
||||
|
||||
/* This is both ssp_task and long_ssp_task
|
||||
*/
|
||||
struct initiate_ssp_task {
|
||||
u8 proto_conn_rate; /* proto:6,4, conn_rate:3,0 */
|
||||
__le32 total_xfer_len;
|
||||
struct ssp_frame_hdr ssp_frame;
|
||||
struct ssp_command_iu ssp_cmd;
|
||||
__le16 sister_scb; /* 0xFFFF */
|
||||
__le16 conn_handle; /* index to DDB for the intended target */
|
||||
u8 data_dir; /* :1,0 */
|
||||
#define DATA_DIR_NONE 0x00
|
||||
#define DATA_DIR_IN 0x01
|
||||
#define DATA_DIR_OUT 0x02
|
||||
#define DATA_DIR_BYRECIPIENT 0x03
|
||||
|
||||
u8 _r_a;
|
||||
u8 retry_count;
|
||||
u8 _r_b[5];
|
||||
struct sg_el sg_element[3]; /* 2 real and 1 link */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This defines both ata_task and atapi_task.
|
||||
* ata: C bit of FIS should be 1,
|
||||
* atapi: C bit of FIS should be 1, and command register should be 0xA0,
|
||||
* to indicate a packet command.
|
||||
*/
|
||||
struct initiate_ata_task {
|
||||
u8 proto_conn_rate;
|
||||
__le32 total_xfer_len;
|
||||
struct host_to_dev_fis fis;
|
||||
__le32 data_offs;
|
||||
u8 atapi_packet[16];
|
||||
u8 _r_a[12];
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 ata_flags; /* CSMI:6,6, DTM:4,4, QT:3,3, data dir:1,0 */
|
||||
#define CSMI_TASK 0x40
|
||||
#define DATA_XFER_MODE_DMA 0x10
|
||||
#define ATA_Q_TYPE_MASK 0x08
|
||||
#define ATA_Q_TYPE_UNTAGGED 0x00
|
||||
#define ATA_Q_TYPE_NCQ 0x08
|
||||
|
||||
u8 _r_b;
|
||||
u8 retry_count;
|
||||
u8 _r_c;
|
||||
u8 flags;
|
||||
#define STP_AFFIL_POLICY 0x20
|
||||
#define SET_AFFIL_POLICY 0x10
|
||||
#define RET_PARTIAL_SGLIST 0x02
|
||||
|
||||
u8 _r_d[3];
|
||||
struct sg_el sg_element[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct initiate_smp_task {
|
||||
u8 proto_conn_rate;
|
||||
u8 _r_a[40];
|
||||
struct sg_el smp_req;
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 _r_c[8];
|
||||
struct sg_el smp_resp;
|
||||
u8 _r_d[32];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct control_phy {
|
||||
u8 phy_id;
|
||||
u8 sub_func;
|
||||
#define DISABLE_PHY 0x00
|
||||
#define ENABLE_PHY 0x01
|
||||
#define RELEASE_SPINUP_HOLD 0x02
|
||||
#define ENABLE_PHY_NO_SAS_OOB 0x03
|
||||
#define ENABLE_PHY_NO_SATA_OOB 0x04
|
||||
#define PHY_NO_OP 0x05
|
||||
#define EXECUTE_HARD_RESET 0x81
|
||||
|
||||
u8 func_mask;
|
||||
u8 speed_mask;
|
||||
u8 hot_plug_delay;
|
||||
u8 port_type;
|
||||
u8 flags;
|
||||
#define DEV_PRES_TIMER_OVERRIDE_ENABLE 0x01
|
||||
#define DISABLE_PHY_IF_OOB_FAILS 0x02
|
||||
|
||||
__le32 timeout_override;
|
||||
u8 link_reset_retries;
|
||||
u8 _r_a[47];
|
||||
__le16 conn_handle;
|
||||
u8 _r_b[56];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct control_ata_dev {
|
||||
u8 proto_conn_rate;
|
||||
__le32 _r_a;
|
||||
struct host_to_dev_fis fis;
|
||||
u8 _r_b[32];
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 ata_flags; /* 0 */
|
||||
u8 _r_c[55];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct empty_scb {
|
||||
u8 num_valid;
|
||||
__le32 _r_a;
|
||||
#define ASD_EDBS_PER_SCB 7
|
||||
/* header+data+CRC+DMA suffix data */
|
||||
#define ASD_EDB_SIZE (24+1024+4+16)
|
||||
struct sg_el eb[ASD_EDBS_PER_SCB];
|
||||
#define ELEMENT_NOT_VALID 0xC0
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct initiate_link_adm {
|
||||
u8 phy_id;
|
||||
u8 sub_func;
|
||||
#define GET_LINK_ERROR_COUNT 0x00
|
||||
#define RESET_LINK_ERROR_COUNT 0x01
|
||||
#define ENABLE_NOTIFY_SPINUP_INTS 0x02
|
||||
|
||||
u8 _r_a[57];
|
||||
__le16 conn_handle;
|
||||
u8 _r_b[56];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct copy_memory {
|
||||
u8 _r_a;
|
||||
__le16 xfer_len;
|
||||
__le16 _r_b;
|
||||
__le64 src_busaddr;
|
||||
u8 src_ds; /* See definition of sg_el */
|
||||
u8 _r_c[45];
|
||||
__le16 conn_handle;
|
||||
__le64 _r_d;
|
||||
__le64 dest_busaddr;
|
||||
u8 dest_ds; /* See definition of sg_el */
|
||||
u8 _r_e[39];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct abort_task {
|
||||
u8 proto_conn_rate;
|
||||
__le32 _r_a;
|
||||
struct ssp_frame_hdr ssp_frame;
|
||||
struct ssp_task_iu ssp_task;
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 flags; /* ovrd_itnl_timer:3,3, suspend_data_trans:2,2 */
|
||||
#define SUSPEND_DATA_TRANS 0x04
|
||||
|
||||
u8 _r_b;
|
||||
u8 retry_count;
|
||||
u8 _r_c[5];
|
||||
__le16 index; /* Transaction context of task to be queried */
|
||||
__le16 itnl_to;
|
||||
u8 _r_d[44];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct clear_nexus {
|
||||
u8 nexus;
|
||||
#define NEXUS_ADAPTER 0x00
|
||||
#define NEXUS_PORT 0x01
|
||||
#define NEXUS_I_T 0x02
|
||||
#define NEXUS_I_T_L 0x03
|
||||
#define NEXUS_TAG 0x04
|
||||
#define NEXUS_TRANS_CX 0x05
|
||||
#define NEXUS_SATA_TAG 0x06
|
||||
#define NEXUS_T_L 0x07
|
||||
#define NEXUS_L 0x08
|
||||
#define NEXUS_T_TAG 0x09
|
||||
|
||||
__le32 _r_a;
|
||||
u8 flags;
|
||||
#define SUSPEND_TX 0x80
|
||||
#define RESUME_TX 0x40
|
||||
#define SEND_Q 0x04
|
||||
#define EXEC_Q 0x02
|
||||
#define NOTINQ 0x01
|
||||
|
||||
u8 _r_b[3];
|
||||
u8 conn_mask;
|
||||
u8 _r_c[19];
|
||||
struct ssp_task_iu ssp_task; /* LUN and TAG */
|
||||
__le16 _r_d;
|
||||
__le16 conn_handle;
|
||||
__le64 _r_e;
|
||||
__le16 index; /* Transaction context of task to be cleared */
|
||||
__le16 context; /* Clear nexus context */
|
||||
u8 _r_f[44];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct initiate_ssp_tmf {
|
||||
u8 proto_conn_rate;
|
||||
__le32 _r_a;
|
||||
struct ssp_frame_hdr ssp_frame;
|
||||
struct ssp_task_iu ssp_task;
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 flags; /* itnl override and suspend data tx */
|
||||
#define OVERRIDE_ITNL_TIMER 8
|
||||
|
||||
u8 _r_b;
|
||||
u8 retry_count;
|
||||
u8 _r_c[5];
|
||||
__le16 index; /* Transaction context of task to be queried */
|
||||
__le16 itnl_to;
|
||||
u8 _r_d[44];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Transmits an arbitrary primitive on the link.
|
||||
* Used for NOTIFY and BROADCAST.
|
||||
*/
|
||||
struct send_prim {
|
||||
u8 phy_id;
|
||||
u8 wait_transmit; /* :0,0 */
|
||||
u8 xmit_flags;
|
||||
#define XMTPSIZE_MASK 0xF0
|
||||
#define XMTPSIZE_SINGLE 0x10
|
||||
#define XMTPSIZE_REPEATED 0x20
|
||||
#define XMTPSIZE_CONT 0x20
|
||||
#define XMTPSIZE_TRIPLE 0x30
|
||||
#define XMTPSIZE_REDUNDANT 0x60
|
||||
#define XMTPSIZE_INF 0
|
||||
|
||||
#define XMTCONTEN 0x04
|
||||
#define XMTPFRM 0x02 /* Transmit at the next frame boundary */
|
||||
#define XMTPIMM 0x01 /* Transmit immediately */
|
||||
|
||||
__le16 _r_a;
|
||||
u8 prim[4]; /* K, D0, D1, D2 */
|
||||
u8 _r_b[50];
|
||||
__le16 conn_handle;
|
||||
u8 _r_c[56];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This describes both SSP Target Get Data and SSP Target Get Data And
|
||||
* Send Good Response SCBs. Used when the sequencer is operating in
|
||||
* target mode...
|
||||
*/
|
||||
struct ssp_targ_get_data {
|
||||
u8 proto_conn_rate;
|
||||
__le32 total_xfer_len;
|
||||
struct ssp_frame_hdr ssp_frame;
|
||||
struct xfer_rdy_iu xfer_rdy;
|
||||
u8 lun[LUN_SIZE];
|
||||
__le64 _r_a;
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 data_dir; /* 01b */
|
||||
u8 _r_b;
|
||||
u8 retry_count;
|
||||
u8 _r_c[5];
|
||||
struct sg_el sg_element[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- The actual SCB struct ---------- */
|
||||
|
||||
struct scb {
|
||||
struct scb_header header;
|
||||
union {
|
||||
struct initiate_ssp_task ssp_task;
|
||||
struct initiate_ata_task ata_task;
|
||||
struct initiate_smp_task smp_task;
|
||||
struct control_phy control_phy;
|
||||
struct control_ata_dev control_ata_dev;
|
||||
struct empty_scb escb;
|
||||
struct initiate_link_adm link_adm;
|
||||
struct copy_memory cp_mem;
|
||||
struct abort_task abort_task;
|
||||
struct clear_nexus clear_nexus;
|
||||
struct initiate_ssp_tmf ssp_tmf;
|
||||
};
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- Done List ---------- */
|
||||
/* The done list entry opcode field is defined below.
|
||||
* The mnemonic encoding and meaning is as follows:
|
||||
* TC - Task Complete, status was received and acknowledged
|
||||
* TF - Task Failed, indicates an error prior to receiving acknowledgment
|
||||
* for the command:
|
||||
* - no conn,
|
||||
* - NACK or R_ERR received in response to this command,
|
||||
* - credit blocked or not available, or in the case of SMP request,
|
||||
* - no SMP response was received.
|
||||
* In these four cases it is known that the target didn't receive the
|
||||
* command.
|
||||
* TI - Task Interrupted, error after the command was acknowledged. It is
|
||||
* known that the command was received by the target.
|
||||
* TU - Task Unacked, command was transmitted but neither ACK (R_OK) nor NAK
|
||||
* (R_ERR) was received due to loss of signal, broken connection, loss of
|
||||
* dword sync or other reason. The application client should send the
|
||||
* appropriate task query.
|
||||
* TA - Task Aborted, see TF.
|
||||
* _RESP - The completion includes an empty buffer containing status.
|
||||
* TO - Timeout.
|
||||
*/
|
||||
#define TC_NO_ERROR 0x00
|
||||
#define TC_UNDERRUN 0x01
|
||||
#define TC_OVERRUN 0x02
|
||||
#define TF_OPEN_TO 0x03
|
||||
#define TF_OPEN_REJECT 0x04
|
||||
#define TI_BREAK 0x05
|
||||
#define TI_PROTO_ERR 0x06
|
||||
#define TC_SSP_RESP 0x07
|
||||
#define TI_PHY_DOWN 0x08
|
||||
#define TF_PHY_DOWN 0x09
|
||||
#define TC_LINK_ADM_RESP 0x0a
|
||||
#define TC_CSMI 0x0b
|
||||
#define TC_ATA_RESP 0x0c
|
||||
#define TU_PHY_DOWN 0x0d
|
||||
#define TU_BREAK 0x0e
|
||||
#define TI_SATA_TO 0x0f
|
||||
#define TI_NAK 0x10
|
||||
#define TC_CONTROL_PHY 0x11
|
||||
#define TF_BREAK 0x12
|
||||
#define TC_RESUME 0x13
|
||||
#define TI_ACK_NAK_TO 0x14
|
||||
#define TF_SMPRSP_TO 0x15
|
||||
#define TF_SMP_XMIT_RCV_ERR 0x16
|
||||
#define TC_PARTIAL_SG_LIST 0x17
|
||||
#define TU_ACK_NAK_TO 0x18
|
||||
#define TU_SATA_TO 0x19
|
||||
#define TF_NAK_RECV 0x1a
|
||||
#define TA_I_T_NEXUS_LOSS 0x1b
|
||||
#define TC_ATA_R_ERR_RECV 0x1c
|
||||
#define TF_TMF_NO_CTX 0x1d
|
||||
#define TA_ON_REQ 0x1e
|
||||
#define TF_TMF_NO_TAG 0x1f
|
||||
#define TF_TMF_TAG_FREE 0x20
|
||||
#define TF_TMF_TASK_DONE 0x21
|
||||
#define TF_TMF_NO_CONN_HANDLE 0x22
|
||||
#define TC_TASK_CLEARED 0x23
|
||||
#define TI_SYNCS_RECV 0x24
|
||||
#define TU_SYNCS_RECV 0x25
|
||||
#define TF_IRTT_TO 0x26
|
||||
#define TF_NO_SMP_CONN 0x27
|
||||
#define TF_IU_SHORT 0x28
|
||||
#define TF_DATA_OFFS_ERR 0x29
|
||||
#define TF_INV_CONN_HANDLE 0x2a
|
||||
#define TF_REQUESTED_N_PENDING 0x2b
|
||||
|
||||
/* 0xc1 - 0xc7: empty buffer received,
|
||||
0xd1 - 0xd7: establish nexus empty buffer received
|
||||
*/
|
||||
/* This is the ESCB mask */
|
||||
#define ESCB_RECVD 0xC0
|
||||
|
||||
|
||||
/* This struct done_list_struct defines the done list entry.
|
||||
* All fields are LE.
|
||||
*/
|
||||
struct done_list_struct {
|
||||
__le16 index; /* aka transaction context */
|
||||
u8 opcode;
|
||||
u8 status_block[4];
|
||||
u8 toggle; /* bit 0 */
|
||||
#define DL_TOGGLE_MASK 0x01
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- PHYS ---------- */
|
||||
|
||||
struct asd_phy {
|
||||
struct asd_sas_phy sas_phy;
|
||||
struct asd_phy_desc *phy_desc; /* hw profile */
|
||||
|
||||
struct sas_identify_frame *identify_frame;
|
||||
struct asd_dma_tok *id_frm_tok;
|
||||
|
||||
u8 frame_rcvd[ASD_EDB_SIZE];
|
||||
};
|
||||
|
||||
|
||||
#define ASD_SCB_SIZE sizeof(struct scb)
|
||||
#define ASD_DDB_SIZE sizeof(struct asd_ddb_ssp_smp_target_port)
|
||||
|
||||
/* Define this to 0 if you do not want NOTIFY (ENABLE SPINIP) sent.
|
||||
* Default: 0x10 (it's a mask)
|
||||
*/
|
||||
#define ASD_NOTIFY_ENABLE_SPINUP 0x10
|
||||
|
||||
/* If enabled, set this to the interval between transmission
|
||||
* of NOTIFY (ENABLE SPINUP). In units of 200 us.
|
||||
*/
|
||||
#define ASD_NOTIFY_TIMEOUT 2500
|
||||
|
||||
/* Initial delay after OOB, before we transmit NOTIFY (ENABLE SPINUP).
|
||||
* If 0, transmit immediately. In milliseconds.
|
||||
*/
|
||||
#define ASD_NOTIFY_DOWN_COUNT 0
|
||||
|
||||
/* Device present timer timeout constant, 10 ms. */
|
||||
#define ASD_DEV_PRESENT_TIMEOUT 0x2710
|
||||
|
||||
#define ASD_SATA_INTERLOCK_TIMEOUT 0
|
||||
|
||||
/* How long to wait before shutting down an STP connection, unless
|
||||
* an STP target sent frame(s). 50 usec.
|
||||
* IGNORED by the sequencer (i.e. value 0 always).
|
||||
*/
|
||||
#define ASD_STP_SHUTDOWN_TIMEOUT 0x0
|
||||
|
||||
/* ATA soft reset timer timeout. 5 usec. */
|
||||
#define ASD_SRST_ASSERT_TIMEOUT 0x05
|
||||
|
||||
/* 31 sec */
|
||||
#define ASD_RCV_FIS_TIMEOUT 0x01D905C0
|
||||
|
||||
#define ASD_ONE_MILLISEC_TIMEOUT 0x03e8
|
||||
|
||||
/* COMINIT timer */
|
||||
#define ASD_TEN_MILLISEC_TIMEOUT 0x2710
|
||||
#define ASD_COMINIT_TIMEOUT ASD_TEN_MILLISEC_TIMEOUT
|
||||
|
||||
/* 1 sec */
|
||||
#define ASD_SMP_RCV_TIMEOUT 0x000F4240
|
||||
|
||||
#endif
|
||||
732
drivers/scsi/aic94xx/aic94xx_scb.c
Normal file
732
drivers/scsi/aic94xx/aic94xx_scb.c
Normal file
@@ -0,0 +1,732 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver SCB management.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_reg.h"
|
||||
#include "aic94xx_hwi.h"
|
||||
#include "aic94xx_seq.h"
|
||||
|
||||
#include "aic94xx_dump.h"
|
||||
|
||||
/* ---------- EMPTY SCB ---------- */
|
||||
|
||||
#define DL_PHY_MASK 7
|
||||
#define BYTES_DMAED 0
|
||||
#define PRIMITIVE_RECVD 0x08
|
||||
#define PHY_EVENT 0x10
|
||||
#define LINK_RESET_ERROR 0x18
|
||||
#define TIMER_EVENT 0x20
|
||||
#define REQ_TASK_ABORT 0xF0
|
||||
#define REQ_DEVICE_RESET 0xF1
|
||||
#define SIGNAL_NCQ_ERROR 0xF2
|
||||
#define CLEAR_NCQ_ERROR 0xF3
|
||||
|
||||
#define PHY_EVENTS_STATUS (CURRENT_LOSS_OF_SIGNAL | CURRENT_OOB_DONE \
|
||||
| CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \
|
||||
| CURRENT_OOB_ERROR)
|
||||
|
||||
static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode)
|
||||
{
|
||||
switch (oob_mode & 7) {
|
||||
case PHY_SPEED_60:
|
||||
/* FIXME: sas transport class doesn't have this */
|
||||
phy->sas_phy.linkrate = PHY_LINKRATE_6;
|
||||
phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS;
|
||||
break;
|
||||
case PHY_SPEED_30:
|
||||
phy->sas_phy.linkrate = PHY_LINKRATE_3;
|
||||
phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
|
||||
break;
|
||||
case PHY_SPEED_15:
|
||||
phy->sas_phy.linkrate = PHY_LINKRATE_1_5;
|
||||
phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
|
||||
break;
|
||||
}
|
||||
if (oob_mode & SAS_MODE)
|
||||
phy->sas_phy.oob_mode = SAS_OOB_MODE;
|
||||
else if (oob_mode & SATA_MODE)
|
||||
phy->sas_phy.oob_mode = SATA_OOB_MODE;
|
||||
}
|
||||
|
||||
static inline void asd_phy_event_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
|
||||
int phy_id = dl->status_block[0] & DL_PHY_MASK;
|
||||
struct asd_phy *phy = &asd_ha->phys[phy_id];
|
||||
|
||||
u8 oob_status = dl->status_block[1] & PHY_EVENTS_STATUS;
|
||||
u8 oob_mode = dl->status_block[2];
|
||||
|
||||
switch (oob_status) {
|
||||
case CURRENT_LOSS_OF_SIGNAL:
|
||||
/* directly attached device was removed */
|
||||
ASD_DPRINTK("phy%d: device unplugged\n", phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
sas_phy_disconnected(&phy->sas_phy);
|
||||
sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL);
|
||||
break;
|
||||
case CURRENT_OOB_DONE:
|
||||
/* hot plugged device */
|
||||
asd_turn_led(asd_ha, phy_id, 1);
|
||||
get_lrate_mode(phy, oob_mode);
|
||||
ASD_DPRINTK("phy%d device plugged: lrate:0x%x, proto:0x%x\n",
|
||||
phy_id, phy->sas_phy.linkrate, phy->sas_phy.iproto);
|
||||
sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
|
||||
break;
|
||||
case CURRENT_SPINUP_HOLD:
|
||||
/* hot plug SATA, no COMWAKE sent */
|
||||
asd_turn_led(asd_ha, phy_id, 1);
|
||||
sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD);
|
||||
break;
|
||||
case CURRENT_GTO_TIMEOUT:
|
||||
case CURRENT_OOB_ERROR:
|
||||
ASD_DPRINTK("phy%d error while OOB: oob status:0x%x\n", phy_id,
|
||||
dl->status_block[1]);
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
sas_phy_disconnected(&phy->sas_phy);
|
||||
sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If phys are enabled sparsely, this will do the right thing. */
|
||||
static inline unsigned ord_phy(struct asd_ha_struct *asd_ha,
|
||||
struct asd_phy *phy)
|
||||
{
|
||||
u8 enabled_mask = asd_ha->hw_prof.enabled_phys;
|
||||
int i, k = 0;
|
||||
|
||||
for_each_phy(enabled_mask, enabled_mask, i) {
|
||||
if (&asd_ha->phys[i] == phy)
|
||||
return k;
|
||||
k++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_get_attached_sas_addr -- extract/generate attached SAS address
|
||||
* phy: pointer to asd_phy
|
||||
* sas_addr: pointer to buffer where the SAS address is to be written
|
||||
*
|
||||
* This function extracts the SAS address from an IDENTIFY frame
|
||||
* received. If OOB is SATA, then a SAS address is generated from the
|
||||
* HA tables.
|
||||
*
|
||||
* LOCKING: the frame_rcvd_lock needs to be held since this parses the frame
|
||||
* buffer.
|
||||
*/
|
||||
static inline void asd_get_attached_sas_addr(struct asd_phy *phy, u8 *sas_addr)
|
||||
{
|
||||
if (phy->sas_phy.frame_rcvd[0] == 0x34
|
||||
&& phy->sas_phy.oob_mode == SATA_OOB_MODE) {
|
||||
struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha;
|
||||
/* FIS device-to-host */
|
||||
u64 addr = be64_to_cpu(*(__be64 *)phy->phy_desc->sas_addr);
|
||||
|
||||
addr += asd_ha->hw_prof.sata_name_base + ord_phy(asd_ha, phy);
|
||||
*(__be64 *)sas_addr = cpu_to_be64(addr);
|
||||
} else {
|
||||
struct sas_identify_frame *idframe =
|
||||
(void *) phy->sas_phy.frame_rcvd;
|
||||
memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl,
|
||||
int edb_id, int phy_id)
|
||||
{
|
||||
unsigned long flags;
|
||||
int edb_el = edb_id + ascb->edb_index;
|
||||
struct asd_dma_tok *edb = ascb->ha->seq.edb_arr[edb_el];
|
||||
struct asd_phy *phy = &ascb->ha->phys[phy_id];
|
||||
struct sas_ha_struct *sas_ha = phy->sas_phy.ha;
|
||||
u16 size = ((dl->status_block[3] & 7) << 8) | dl->status_block[2];
|
||||
|
||||
size = min(size, (u16) sizeof(phy->frame_rcvd));
|
||||
|
||||
spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
|
||||
memcpy(phy->sas_phy.frame_rcvd, edb->vaddr, size);
|
||||
phy->sas_phy.frame_rcvd_size = size;
|
||||
asd_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
|
||||
spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
|
||||
asd_dump_frame_rcvd(phy, dl);
|
||||
sas_ha->notify_port_event(&phy->sas_phy, PORTE_BYTES_DMAED);
|
||||
}
|
||||
|
||||
static inline void asd_link_reset_err_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl,
|
||||
int phy_id)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
|
||||
struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
|
||||
u8 lr_error = dl->status_block[1];
|
||||
u8 retries_left = dl->status_block[2];
|
||||
|
||||
switch (lr_error) {
|
||||
case 0:
|
||||
ASD_DPRINTK("phy%d: Receive ID timer expired\n", phy_id);
|
||||
break;
|
||||
case 1:
|
||||
ASD_DPRINTK("phy%d: Loss of signal\n", phy_id);
|
||||
break;
|
||||
case 2:
|
||||
ASD_DPRINTK("phy%d: Loss of dword sync\n", phy_id);
|
||||
break;
|
||||
case 3:
|
||||
ASD_DPRINTK("phy%d: Receive FIS timeout\n", phy_id);
|
||||
break;
|
||||
default:
|
||||
ASD_DPRINTK("phy%d: unknown link reset error code: 0x%x\n",
|
||||
phy_id, lr_error);
|
||||
break;
|
||||
}
|
||||
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
sas_phy_disconnected(sas_phy);
|
||||
sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
|
||||
|
||||
if (retries_left == 0) {
|
||||
int num = 1;
|
||||
struct asd_ascb *cp = asd_ascb_alloc_list(ascb->ha, &num,
|
||||
GFP_ATOMIC);
|
||||
if (!cp) {
|
||||
asd_printk("%s: out of memory\n", __FUNCTION__);
|
||||
goto out;
|
||||
}
|
||||
ASD_DPRINTK("phy%d: retries:0 performing link reset seq\n",
|
||||
phy_id);
|
||||
asd_build_control_phy(cp, phy_id, ENABLE_PHY);
|
||||
if (asd_post_ascb_list(ascb->ha, cp, 1) != 0)
|
||||
asd_ascb_free(cp);
|
||||
}
|
||||
out:
|
||||
;
|
||||
}
|
||||
|
||||
static inline void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl,
|
||||
int phy_id)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct sas_ha_struct *sas_ha = &ascb->ha->sas_ha;
|
||||
struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
|
||||
u8 reg = dl->status_block[1];
|
||||
u32 cont = dl->status_block[2] << ((reg & 3)*8);
|
||||
|
||||
reg &= ~3;
|
||||
switch (reg) {
|
||||
case LmPRMSTAT0BYTE0:
|
||||
switch (cont) {
|
||||
case LmBROADCH:
|
||||
case LmBROADRVCH0:
|
||||
case LmBROADRVCH1:
|
||||
case LmBROADSES:
|
||||
ASD_DPRINTK("phy%d: BROADCAST change received:%d\n",
|
||||
phy_id, cont);
|
||||
spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
|
||||
sas_phy->sas_prim = ffs(cont);
|
||||
spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
|
||||
sas_ha->notify_port_event(sas_phy,PORTE_BROADCAST_RCVD);
|
||||
break;
|
||||
|
||||
case LmUNKNOWNP:
|
||||
ASD_DPRINTK("phy%d: unknown BREAK\n", phy_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n",
|
||||
phy_id, reg, cont);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case LmPRMSTAT1BYTE0:
|
||||
switch (cont) {
|
||||
case LmHARDRST:
|
||||
ASD_DPRINTK("phy%d: HARD_RESET primitive rcvd\n",
|
||||
phy_id);
|
||||
/* The sequencer disables all phys on that port.
|
||||
* We have to re-enable the phys ourselves. */
|
||||
sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET);
|
||||
break;
|
||||
|
||||
default:
|
||||
ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n",
|
||||
phy_id, reg, cont);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASD_DPRINTK("unknown primitive register:0x%x\n",
|
||||
dl->status_block[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_invalidate_edb -- invalidate an EDB and if necessary post the ESCB
|
||||
* @ascb: pointer to Empty SCB
|
||||
* @edb_id: index [0,6] to the empty data buffer which is to be invalidated
|
||||
*
|
||||
* After an EDB has been invalidated, if all EDBs in this ESCB have been
|
||||
* invalidated, the ESCB is posted back to the sequencer.
|
||||
* Context is tasklet/IRQ.
|
||||
*/
|
||||
void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id)
|
||||
{
|
||||
struct asd_seq_data *seq = &ascb->ha->seq;
|
||||
struct empty_scb *escb = &ascb->scb->escb;
|
||||
struct sg_el *eb = &escb->eb[edb_id];
|
||||
struct asd_dma_tok *edb = seq->edb_arr[ascb->edb_index + edb_id];
|
||||
|
||||
memset(edb->vaddr, 0, ASD_EDB_SIZE);
|
||||
eb->flags |= ELEMENT_NOT_VALID;
|
||||
escb->num_valid--;
|
||||
|
||||
if (escb->num_valid == 0) {
|
||||
int i;
|
||||
/* ASD_DPRINTK("reposting escb: vaddr: 0x%p, "
|
||||
"dma_handle: 0x%08llx, next: 0x%08llx, "
|
||||
"index:%d, opcode:0x%02x\n",
|
||||
ascb->dma_scb.vaddr,
|
||||
(u64)ascb->dma_scb.dma_handle,
|
||||
le64_to_cpu(ascb->scb->header.next_scb),
|
||||
le16_to_cpu(ascb->scb->header.index),
|
||||
ascb->scb->header.opcode);
|
||||
*/
|
||||
escb->num_valid = ASD_EDBS_PER_SCB;
|
||||
for (i = 0; i < ASD_EDBS_PER_SCB; i++)
|
||||
escb->eb[i].flags = 0;
|
||||
if (!list_empty(&ascb->list))
|
||||
list_del_init(&ascb->list);
|
||||
i = asd_post_escb_list(ascb->ha, ascb, 1);
|
||||
if (i)
|
||||
asd_printk("couldn't post escb, err:%d\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
static void escb_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
|
||||
int edb = (dl->opcode & DL_PHY_MASK) - 1; /* [0xc1,0xc7] -> [0,6] */
|
||||
u8 sb_opcode = dl->status_block[0];
|
||||
int phy_id = sb_opcode & DL_PHY_MASK;
|
||||
struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
|
||||
|
||||
if (edb > 6 || edb < 0) {
|
||||
ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n",
|
||||
edb, dl->opcode);
|
||||
ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n",
|
||||
sb_opcode, phy_id);
|
||||
ASD_DPRINTK("escb: vaddr: 0x%p, "
|
||||
"dma_handle: 0x%llx, next: 0x%llx, "
|
||||
"index:%d, opcode:0x%02x\n",
|
||||
ascb->dma_scb.vaddr,
|
||||
(unsigned long long)ascb->dma_scb.dma_handle,
|
||||
(unsigned long long)
|
||||
le64_to_cpu(ascb->scb->header.next_scb),
|
||||
le16_to_cpu(ascb->scb->header.index),
|
||||
ascb->scb->header.opcode);
|
||||
}
|
||||
|
||||
sb_opcode &= ~DL_PHY_MASK;
|
||||
|
||||
switch (sb_opcode) {
|
||||
case BYTES_DMAED:
|
||||
ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __FUNCTION__, phy_id);
|
||||
asd_bytes_dmaed_tasklet(ascb, dl, edb, phy_id);
|
||||
break;
|
||||
case PRIMITIVE_RECVD:
|
||||
ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __FUNCTION__,
|
||||
phy_id);
|
||||
asd_primitive_rcvd_tasklet(ascb, dl, phy_id);
|
||||
break;
|
||||
case PHY_EVENT:
|
||||
ASD_DPRINTK("%s: phy%d: PHY_EVENT\n", __FUNCTION__, phy_id);
|
||||
asd_phy_event_tasklet(ascb, dl);
|
||||
break;
|
||||
case LINK_RESET_ERROR:
|
||||
ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __FUNCTION__,
|
||||
phy_id);
|
||||
asd_link_reset_err_tasklet(ascb, dl, phy_id);
|
||||
break;
|
||||
case TIMER_EVENT:
|
||||
ASD_DPRINTK("%s: phy%d: TIMER_EVENT, lost dw sync\n",
|
||||
__FUNCTION__, phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
/* the device is gone */
|
||||
sas_phy_disconnected(sas_phy);
|
||||
sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT);
|
||||
break;
|
||||
case REQ_TASK_ABORT:
|
||||
ASD_DPRINTK("%s: phy%d: REQ_TASK_ABORT\n", __FUNCTION__,
|
||||
phy_id);
|
||||
break;
|
||||
case REQ_DEVICE_RESET:
|
||||
ASD_DPRINTK("%s: phy%d: REQ_DEVICE_RESET\n", __FUNCTION__,
|
||||
phy_id);
|
||||
break;
|
||||
case SIGNAL_NCQ_ERROR:
|
||||
ASD_DPRINTK("%s: phy%d: SIGNAL_NCQ_ERROR\n", __FUNCTION__,
|
||||
phy_id);
|
||||
break;
|
||||
case CLEAR_NCQ_ERROR:
|
||||
ASD_DPRINTK("%s: phy%d: CLEAR_NCQ_ERROR\n", __FUNCTION__,
|
||||
phy_id);
|
||||
break;
|
||||
default:
|
||||
ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__,
|
||||
phy_id, sb_opcode);
|
||||
ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n",
|
||||
edb, dl->opcode);
|
||||
ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n",
|
||||
sb_opcode, phy_id);
|
||||
ASD_DPRINTK("escb: vaddr: 0x%p, "
|
||||
"dma_handle: 0x%llx, next: 0x%llx, "
|
||||
"index:%d, opcode:0x%02x\n",
|
||||
ascb->dma_scb.vaddr,
|
||||
(unsigned long long)ascb->dma_scb.dma_handle,
|
||||
(unsigned long long)
|
||||
le64_to_cpu(ascb->scb->header.next_scb),
|
||||
le16_to_cpu(ascb->scb->header.index),
|
||||
ascb->scb->header.opcode);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
asd_invalidate_edb(ascb, edb);
|
||||
}
|
||||
|
||||
int asd_init_post_escbs(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
struct asd_seq_data *seq = &asd_ha->seq;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < seq->num_escbs; i++)
|
||||
seq->escb_arr[i]->tasklet_complete = escb_tasklet_complete;
|
||||
|
||||
ASD_DPRINTK("posting %d escbs\n", i);
|
||||
return asd_post_escb_list(asd_ha, seq->escb_arr[0], seq->num_escbs);
|
||||
}
|
||||
|
||||
/* ---------- CONTROL PHY ---------- */
|
||||
|
||||
#define CONTROL_PHY_STATUS (CURRENT_DEVICE_PRESENT | CURRENT_OOB_DONE \
|
||||
| CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \
|
||||
| CURRENT_OOB_ERROR)
|
||||
|
||||
/**
|
||||
* control_phy_tasklet_complete -- tasklet complete for CONTROL PHY ascb
|
||||
* @ascb: pointer to an ascb
|
||||
* @dl: pointer to the done list entry
|
||||
*
|
||||
* This function completes a CONTROL PHY scb and frees the ascb.
|
||||
* A note on LEDs:
|
||||
* - an LED blinks if there is IO though it,
|
||||
* - if a device is connected to the LED, it is lit,
|
||||
* - if no device is connected to the LED, is is dimmed (off).
|
||||
*/
|
||||
static void control_phy_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct scb *scb = ascb->scb;
|
||||
struct control_phy *control_phy = &scb->control_phy;
|
||||
u8 phy_id = control_phy->phy_id;
|
||||
struct asd_phy *phy = &ascb->ha->phys[phy_id];
|
||||
|
||||
u8 status = dl->status_block[0];
|
||||
u8 oob_status = dl->status_block[1];
|
||||
u8 oob_mode = dl->status_block[2];
|
||||
/* u8 oob_signals= dl->status_block[3]; */
|
||||
|
||||
if (status != 0) {
|
||||
ASD_DPRINTK("%s: phy%d status block opcode:0x%x\n",
|
||||
__FUNCTION__, phy_id, status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (control_phy->sub_func) {
|
||||
case DISABLE_PHY:
|
||||
asd_ha->hw_prof.enabled_phys &= ~(1 << phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
asd_control_led(asd_ha, phy_id, 0);
|
||||
ASD_DPRINTK("%s: disable phy%d\n", __FUNCTION__, phy_id);
|
||||
break;
|
||||
|
||||
case ENABLE_PHY:
|
||||
asd_control_led(asd_ha, phy_id, 1);
|
||||
if (oob_status & CURRENT_OOB_DONE) {
|
||||
asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
|
||||
get_lrate_mode(phy, oob_mode);
|
||||
asd_turn_led(asd_ha, phy_id, 1);
|
||||
ASD_DPRINTK("%s: phy%d, lrate:0x%x, proto:0x%x\n",
|
||||
__FUNCTION__, phy_id,phy->sas_phy.linkrate,
|
||||
phy->sas_phy.iproto);
|
||||
} else if (oob_status & CURRENT_SPINUP_HOLD) {
|
||||
asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 1);
|
||||
ASD_DPRINTK("%s: phy%d, spinup hold\n", __FUNCTION__,
|
||||
phy_id);
|
||||
} else if (oob_status & CURRENT_ERR_MASK) {
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
ASD_DPRINTK("%s: phy%d: error: oob status:0x%02x\n",
|
||||
__FUNCTION__, phy_id, oob_status);
|
||||
} else if (oob_status & (CURRENT_HOT_PLUG_CNCT
|
||||
| CURRENT_DEVICE_PRESENT)) {
|
||||
asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 1);
|
||||
ASD_DPRINTK("%s: phy%d: hot plug or device present\n",
|
||||
__FUNCTION__, phy_id);
|
||||
} else {
|
||||
asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
ASD_DPRINTK("%s: phy%d: no device present: "
|
||||
"oob_status:0x%x\n",
|
||||
__FUNCTION__, phy_id, oob_status);
|
||||
}
|
||||
break;
|
||||
case RELEASE_SPINUP_HOLD:
|
||||
case PHY_NO_OP:
|
||||
case EXECUTE_HARD_RESET:
|
||||
ASD_DPRINTK("%s: phy%d: sub_func:0x%x\n", __FUNCTION__,
|
||||
phy_id, control_phy->sub_func);
|
||||
/* XXX finish */
|
||||
break;
|
||||
default:
|
||||
ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __FUNCTION__,
|
||||
phy_id, control_phy->sub_func);
|
||||
break;
|
||||
}
|
||||
out:
|
||||
asd_ascb_free(ascb);
|
||||
}
|
||||
|
||||
static inline void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd)
|
||||
{
|
||||
/* disable all speeds, then enable defaults */
|
||||
*speed_mask = SAS_SPEED_60_DIS | SAS_SPEED_30_DIS | SAS_SPEED_15_DIS
|
||||
| SATA_SPEED_30_DIS | SATA_SPEED_15_DIS;
|
||||
|
||||
switch (pd->max_sas_lrate) {
|
||||
case PHY_LINKRATE_6:
|
||||
*speed_mask &= ~SAS_SPEED_60_DIS;
|
||||
default:
|
||||
case PHY_LINKRATE_3:
|
||||
*speed_mask &= ~SAS_SPEED_30_DIS;
|
||||
case PHY_LINKRATE_1_5:
|
||||
*speed_mask &= ~SAS_SPEED_15_DIS;
|
||||
}
|
||||
|
||||
switch (pd->min_sas_lrate) {
|
||||
case PHY_LINKRATE_6:
|
||||
*speed_mask |= SAS_SPEED_30_DIS;
|
||||
case PHY_LINKRATE_3:
|
||||
*speed_mask |= SAS_SPEED_15_DIS;
|
||||
default:
|
||||
case PHY_LINKRATE_1_5:
|
||||
/* nothing to do */
|
||||
;
|
||||
}
|
||||
|
||||
switch (pd->max_sata_lrate) {
|
||||
case PHY_LINKRATE_3:
|
||||
*speed_mask &= ~SATA_SPEED_30_DIS;
|
||||
default:
|
||||
case PHY_LINKRATE_1_5:
|
||||
*speed_mask &= ~SATA_SPEED_15_DIS;
|
||||
}
|
||||
|
||||
switch (pd->min_sata_lrate) {
|
||||
case PHY_LINKRATE_3:
|
||||
*speed_mask |= SATA_SPEED_15_DIS;
|
||||
default:
|
||||
case PHY_LINKRATE_1_5:
|
||||
/* nothing to do */
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_build_control_phy -- build a CONTROL PHY SCB
|
||||
* @ascb: pointer to an ascb
|
||||
* @phy_id: phy id to control, integer
|
||||
* @subfunc: subfunction, what to actually to do the phy
|
||||
*
|
||||
* This function builds a CONTROL PHY scb. No allocation of any kind
|
||||
* is performed. @ascb is allocated with the list function.
|
||||
* The caller can override the ascb->tasklet_complete to point
|
||||
* to its own callback function. It must call asd_ascb_free()
|
||||
* at its tasklet complete function.
|
||||
* See the default implementation.
|
||||
*/
|
||||
void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc)
|
||||
{
|
||||
struct asd_phy *phy = &ascb->ha->phys[phy_id];
|
||||
struct scb *scb = ascb->scb;
|
||||
struct control_phy *control_phy = &scb->control_phy;
|
||||
|
||||
scb->header.opcode = CONTROL_PHY;
|
||||
control_phy->phy_id = (u8) phy_id;
|
||||
control_phy->sub_func = subfunc;
|
||||
|
||||
switch (subfunc) {
|
||||
case EXECUTE_HARD_RESET: /* 0x81 */
|
||||
case ENABLE_PHY: /* 0x01 */
|
||||
/* decide hot plug delay */
|
||||
control_phy->hot_plug_delay = HOTPLUG_DELAY_TIMEOUT;
|
||||
|
||||
/* decide speed mask */
|
||||
set_speed_mask(&control_phy->speed_mask, phy->phy_desc);
|
||||
|
||||
/* initiator port settings are in the hi nibble */
|
||||
if (phy->sas_phy.role == PHY_ROLE_INITIATOR)
|
||||
control_phy->port_type = SAS_PROTO_ALL << 4;
|
||||
else if (phy->sas_phy.role == PHY_ROLE_TARGET)
|
||||
control_phy->port_type = SAS_PROTO_ALL;
|
||||
else
|
||||
control_phy->port_type =
|
||||
(SAS_PROTO_ALL << 4) | SAS_PROTO_ALL;
|
||||
|
||||
/* link reset retries, this should be nominal */
|
||||
control_phy->link_reset_retries = 10;
|
||||
|
||||
case RELEASE_SPINUP_HOLD: /* 0x02 */
|
||||
/* decide the func_mask */
|
||||
control_phy->func_mask = FUNCTION_MASK_DEFAULT;
|
||||
if (phy->phy_desc->flags & ASD_SATA_SPINUP_HOLD)
|
||||
control_phy->func_mask &= ~SPINUP_HOLD_DIS;
|
||||
else
|
||||
control_phy->func_mask |= SPINUP_HOLD_DIS;
|
||||
}
|
||||
|
||||
control_phy->conn_handle = cpu_to_le16(0xFFFF);
|
||||
|
||||
ascb->tasklet_complete = control_phy_tasklet_complete;
|
||||
}
|
||||
|
||||
/* ---------- INITIATE LINK ADM TASK ---------- */
|
||||
|
||||
static void link_adm_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
u8 opcode = dl->opcode;
|
||||
struct initiate_link_adm *link_adm = &ascb->scb->link_adm;
|
||||
u8 phy_id = link_adm->phy_id;
|
||||
|
||||
if (opcode != TC_NO_ERROR) {
|
||||
asd_printk("phy%d: link adm task 0x%x completed with error "
|
||||
"0x%x\n", phy_id, link_adm->sub_func, opcode);
|
||||
}
|
||||
ASD_DPRINTK("phy%d: link adm task 0x%x: 0x%x\n",
|
||||
phy_id, link_adm->sub_func, opcode);
|
||||
|
||||
asd_ascb_free(ascb);
|
||||
}
|
||||
|
||||
void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
|
||||
u8 subfunc)
|
||||
{
|
||||
struct scb *scb = ascb->scb;
|
||||
struct initiate_link_adm *link_adm = &scb->link_adm;
|
||||
|
||||
scb->header.opcode = INITIATE_LINK_ADM_TASK;
|
||||
|
||||
link_adm->phy_id = phy_id;
|
||||
link_adm->sub_func = subfunc;
|
||||
link_adm->conn_handle = cpu_to_le16(0xFFFF);
|
||||
|
||||
ascb->tasklet_complete = link_adm_tasklet_complete;
|
||||
}
|
||||
|
||||
/* ---------- SCB timer ---------- */
|
||||
|
||||
/**
|
||||
* asd_ascb_timedout -- called when a pending SCB's timer has expired
|
||||
* @data: unsigned long, a pointer to the ascb in question
|
||||
*
|
||||
* This is the default timeout function which does the most necessary.
|
||||
* Upper layers can implement their own timeout function, say to free
|
||||
* resources they have with this SCB, and then call this one at the
|
||||
* end of their timeout function. To do this, one should initialize
|
||||
* the ascb->timer.{function, data, expires} prior to calling the post
|
||||
* funcion. The timer is started by the post function.
|
||||
*/
|
||||
void asd_ascb_timedout(unsigned long data)
|
||||
{
|
||||
struct asd_ascb *ascb = (void *) data;
|
||||
struct asd_seq_data *seq = &ascb->ha->seq;
|
||||
unsigned long flags;
|
||||
|
||||
ASD_DPRINTK("scb:0x%x timed out\n", ascb->scb->header.opcode);
|
||||
|
||||
spin_lock_irqsave(&seq->pend_q_lock, flags);
|
||||
seq->pending--;
|
||||
list_del_init(&ascb->list);
|
||||
spin_unlock_irqrestore(&seq->pend_q_lock, flags);
|
||||
|
||||
asd_ascb_free(ascb);
|
||||
}
|
||||
|
||||
/* ---------- CONTROL PHY ---------- */
|
||||
|
||||
/* Given the spec value, return a driver value. */
|
||||
static const int phy_func_table[] = {
|
||||
[PHY_FUNC_NOP] = PHY_NO_OP,
|
||||
[PHY_FUNC_LINK_RESET] = ENABLE_PHY,
|
||||
[PHY_FUNC_HARD_RESET] = EXECUTE_HARD_RESET,
|
||||
[PHY_FUNC_DISABLE] = DISABLE_PHY,
|
||||
[PHY_FUNC_RELEASE_SPINUP_HOLD] = RELEASE_SPINUP_HOLD,
|
||||
};
|
||||
|
||||
int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = phy->ha->lldd_ha;
|
||||
struct asd_ascb *ascb;
|
||||
int res = 1;
|
||||
|
||||
if (func == PHY_FUNC_CLEAR_ERROR_LOG)
|
||||
return -ENOSYS;
|
||||
|
||||
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
|
||||
if (!ascb)
|
||||
return -ENOMEM;
|
||||
|
||||
asd_build_control_phy(ascb, phy->id, phy_func_table[func]);
|
||||
res = asd_post_ascb_list(asd_ha, ascb , 1);
|
||||
if (res)
|
||||
asd_ascb_free(ascb);
|
||||
|
||||
return res;
|
||||
}
|
||||
1136
drivers/scsi/aic94xx/aic94xx_sds.c
Normal file
1136
drivers/scsi/aic94xx/aic94xx_sds.c
Normal file
File diff suppressed because it is too large
Load Diff
1401
drivers/scsi/aic94xx/aic94xx_seq.c
Normal file
1401
drivers/scsi/aic94xx/aic94xx_seq.c
Normal file
File diff suppressed because it is too large
Load Diff
70
drivers/scsi/aic94xx/aic94xx_seq.h
Normal file
70
drivers/scsi/aic94xx/aic94xx_seq.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver sequencer interface header file.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AIC94XX_SEQ_H_
|
||||
#define _AIC94XX_SEQ_H_
|
||||
|
||||
#define CSEQ_NUM_VECS 3
|
||||
#define LSEQ_NUM_VECS 11
|
||||
|
||||
#define SAS_RAZOR_SEQUENCER_FW_FILE "aic94xx-seq.fw"
|
||||
|
||||
/* Note: All quantites in the sequencer file are little endian */
|
||||
struct sequencer_file_header {
|
||||
/* Checksum of the entire contents of the sequencer excluding
|
||||
* these four bytes */
|
||||
u32 csum;
|
||||
/* numeric major version */
|
||||
u32 major;
|
||||
/* numeric minor version */
|
||||
u32 minor;
|
||||
/* version string printed by driver */
|
||||
char version[16];
|
||||
u32 cseq_table_offset;
|
||||
u32 cseq_table_size;
|
||||
u32 lseq_table_offset;
|
||||
u32 lseq_table_size;
|
||||
u32 cseq_code_offset;
|
||||
u32 cseq_code_size;
|
||||
u32 lseq_code_offset;
|
||||
u32 lseq_code_size;
|
||||
u16 mode2_task;
|
||||
u16 cseq_idle_loop;
|
||||
u16 lseq_idle_loop;
|
||||
} __attribute__((packed));
|
||||
|
||||
#ifdef __KERNEL__
|
||||
int asd_pause_cseq(struct asd_ha_struct *asd_ha);
|
||||
int asd_unpause_cseq(struct asd_ha_struct *asd_ha);
|
||||
int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask);
|
||||
int asd_unpause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask);
|
||||
int asd_init_seqs(struct asd_ha_struct *asd_ha);
|
||||
int asd_start_seqs(struct asd_ha_struct *asd_ha);
|
||||
|
||||
void asd_update_port_links(struct asd_sas_phy *phy);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
642
drivers/scsi/aic94xx/aic94xx_task.c
Normal file
642
drivers/scsi/aic94xx/aic94xx_task.c
Normal file
@@ -0,0 +1,642 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA Tasks
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_sas.h"
|
||||
#include "aic94xx_hwi.h"
|
||||
|
||||
static void asd_unbuild_ata_ascb(struct asd_ascb *a);
|
||||
static void asd_unbuild_smp_ascb(struct asd_ascb *a);
|
||||
static void asd_unbuild_ssp_ascb(struct asd_ascb *a);
|
||||
|
||||
static inline void asd_can_dequeue(struct asd_ha_struct *asd_ha, int num)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
|
||||
asd_ha->seq.can_queue += num;
|
||||
spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
|
||||
}
|
||||
|
||||
/* PCI_DMA_... to our direction translation.
|
||||
*/
|
||||
static const u8 data_dir_flags[] = {
|
||||
[PCI_DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT, /* UNSPECIFIED */
|
||||
[PCI_DMA_TODEVICE] = DATA_DIR_OUT, /* OUTBOUND */
|
||||
[PCI_DMA_FROMDEVICE] = DATA_DIR_IN, /* INBOUND */
|
||||
[PCI_DMA_NONE] = DATA_DIR_NONE, /* NO TRANSFER */
|
||||
};
|
||||
|
||||
static inline int asd_map_scatterlist(struct sas_task *task,
|
||||
struct sg_el *sg_arr,
|
||||
unsigned long gfp_flags)
|
||||
{
|
||||
struct asd_ascb *ascb = task->lldd_task;
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct scatterlist *sc;
|
||||
int num_sg, res;
|
||||
|
||||
if (task->data_dir == PCI_DMA_NONE)
|
||||
return 0;
|
||||
|
||||
if (task->num_scatter == 0) {
|
||||
void *p = task->scatter;
|
||||
dma_addr_t dma = pci_map_single(asd_ha->pcidev, p,
|
||||
task->total_xfer_len,
|
||||
task->data_dir);
|
||||
sg_arr[0].bus_addr = cpu_to_le64((u64)dma);
|
||||
sg_arr[0].size = cpu_to_le32(task->total_xfer_len);
|
||||
sg_arr[0].flags |= ASD_SG_EL_LIST_EOL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
num_sg = pci_map_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
|
||||
task->data_dir);
|
||||
if (num_sg == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (num_sg > 3) {
|
||||
int i;
|
||||
|
||||
ascb->sg_arr = asd_alloc_coherent(asd_ha,
|
||||
num_sg*sizeof(struct sg_el),
|
||||
gfp_flags);
|
||||
if (!ascb->sg_arr) {
|
||||
res = -ENOMEM;
|
||||
goto err_unmap;
|
||||
}
|
||||
for (sc = task->scatter, i = 0; i < num_sg; i++, sc++) {
|
||||
struct sg_el *sg =
|
||||
&((struct sg_el *)ascb->sg_arr->vaddr)[i];
|
||||
sg->bus_addr = cpu_to_le64((u64)sg_dma_address(sc));
|
||||
sg->size = cpu_to_le32((u32)sg_dma_len(sc));
|
||||
if (i == num_sg-1)
|
||||
sg->flags |= ASD_SG_EL_LIST_EOL;
|
||||
}
|
||||
|
||||
for (sc = task->scatter, i = 0; i < 2; i++, sc++) {
|
||||
sg_arr[i].bus_addr =
|
||||
cpu_to_le64((u64)sg_dma_address(sc));
|
||||
sg_arr[i].size = cpu_to_le32((u32)sg_dma_len(sc));
|
||||
}
|
||||
sg_arr[1].next_sg_offs = 2 * sizeof(*sg_arr);
|
||||
sg_arr[1].flags |= ASD_SG_EL_LIST_EOS;
|
||||
|
||||
memset(&sg_arr[2], 0, sizeof(*sg_arr));
|
||||
sg_arr[2].bus_addr=cpu_to_le64((u64)ascb->sg_arr->dma_handle);
|
||||
} else {
|
||||
int i;
|
||||
for (sc = task->scatter, i = 0; i < num_sg; i++, sc++) {
|
||||
sg_arr[i].bus_addr =
|
||||
cpu_to_le64((u64)sg_dma_address(sc));
|
||||
sg_arr[i].size = cpu_to_le32((u32)sg_dma_len(sc));
|
||||
}
|
||||
sg_arr[i-1].flags |= ASD_SG_EL_LIST_EOL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_unmap:
|
||||
pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
|
||||
task->data_dir);
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void asd_unmap_scatterlist(struct asd_ascb *ascb)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct sas_task *task = ascb->uldd_task;
|
||||
|
||||
if (task->data_dir == PCI_DMA_NONE)
|
||||
return;
|
||||
|
||||
if (task->num_scatter == 0) {
|
||||
dma_addr_t dma = (dma_addr_t)
|
||||
le64_to_cpu(ascb->scb->ssp_task.sg_element[0].bus_addr);
|
||||
pci_unmap_single(ascb->ha->pcidev, dma, task->total_xfer_len,
|
||||
task->data_dir);
|
||||
return;
|
||||
}
|
||||
|
||||
asd_free_coherent(asd_ha, ascb->sg_arr);
|
||||
pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
|
||||
task->data_dir);
|
||||
}
|
||||
|
||||
/* ---------- Task complete tasklet ---------- */
|
||||
|
||||
static void asd_get_response_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct sas_task *task = ascb->uldd_task;
|
||||
struct task_status_struct *ts = &task->task_status;
|
||||
unsigned long flags;
|
||||
struct tc_resp_sb_struct {
|
||||
__le16 index_escb;
|
||||
u8 len_lsb;
|
||||
u8 flags;
|
||||
} __attribute__ ((packed)) *resp_sb = (void *) dl->status_block;
|
||||
|
||||
/* int size = ((resp_sb->flags & 7) << 8) | resp_sb->len_lsb; */
|
||||
int edb_id = ((resp_sb->flags & 0x70) >> 4)-1;
|
||||
struct asd_ascb *escb;
|
||||
struct asd_dma_tok *edb;
|
||||
void *r;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags);
|
||||
escb = asd_tc_index_find(&asd_ha->seq,
|
||||
(int)le16_to_cpu(resp_sb->index_escb));
|
||||
spin_unlock_irqrestore(&asd_ha->seq.tc_index_lock, flags);
|
||||
|
||||
if (!escb) {
|
||||
ASD_DPRINTK("Uh-oh! No escb for this dl?!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ts->buf_valid_size = 0;
|
||||
edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];
|
||||
r = edb->vaddr;
|
||||
if (task->task_proto == SAS_PROTO_SSP) {
|
||||
struct ssp_response_iu *iu =
|
||||
r + 16 + sizeof(struct ssp_frame_hdr);
|
||||
|
||||
ts->residual = le32_to_cpu(*(__le32 *)r);
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
if (iu->datapres == 0)
|
||||
ts->stat = iu->status;
|
||||
else if (iu->datapres == 1)
|
||||
ts->stat = iu->resp_data[3];
|
||||
else if (iu->datapres == 2) {
|
||||
ts->stat = SAM_CHECK_COND;
|
||||
ts->buf_valid_size = min((u32) SAS_STATUS_BUF_SIZE,
|
||||
be32_to_cpu(iu->sense_data_len));
|
||||
memcpy(ts->buf, iu->sense_data, ts->buf_valid_size);
|
||||
if (iu->status != SAM_CHECK_COND) {
|
||||
ASD_DPRINTK("device %llx sent sense data, but "
|
||||
"stat(0x%x) is not CHECK_CONDITION"
|
||||
"\n",
|
||||
SAS_ADDR(task->dev->sas_addr),
|
||||
ts->stat);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
struct ata_task_resp *resp = (void *) &ts->buf[0];
|
||||
|
||||
ts->residual = le32_to_cpu(*(__le32 *)r);
|
||||
|
||||
if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) {
|
||||
resp->frame_len = le16_to_cpu(*(__le16 *)(r+6));
|
||||
memcpy(&resp->ending_fis[0], r+16, 24);
|
||||
ts->buf_valid_size = sizeof(*resp);
|
||||
}
|
||||
}
|
||||
|
||||
asd_invalidate_edb(escb, edb_id);
|
||||
}
|
||||
|
||||
static void asd_task_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct sas_task *task = ascb->uldd_task;
|
||||
struct task_status_struct *ts = &task->task_status;
|
||||
unsigned long flags;
|
||||
u8 opcode = dl->opcode;
|
||||
|
||||
asd_can_dequeue(ascb->ha, 1);
|
||||
|
||||
Again:
|
||||
switch (opcode) {
|
||||
case TC_NO_ERROR:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAM_GOOD;
|
||||
break;
|
||||
case TC_UNDERRUN:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_DATA_UNDERRUN;
|
||||
ts->residual = le32_to_cpu(*(__le32 *)dl->status_block);
|
||||
break;
|
||||
case TC_OVERRUN:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_DATA_OVERRUN;
|
||||
ts->residual = 0;
|
||||
break;
|
||||
case TC_SSP_RESP:
|
||||
case TC_ATA_RESP:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_PROTO_RESPONSE;
|
||||
asd_get_response_tasklet(ascb, dl);
|
||||
break;
|
||||
case TF_OPEN_REJECT:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_OPEN_REJECT;
|
||||
if (dl->status_block[1] & 2)
|
||||
ts->open_rej_reason = 1 + dl->status_block[2];
|
||||
else if (dl->status_block[1] & 1)
|
||||
ts->open_rej_reason = (dl->status_block[2] >> 4)+10;
|
||||
else
|
||||
ts->open_rej_reason = SAS_OREJ_UNKNOWN;
|
||||
break;
|
||||
case TF_OPEN_TO:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_OPEN_TO;
|
||||
break;
|
||||
case TF_PHY_DOWN:
|
||||
case TU_PHY_DOWN:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_PHY_DOWN;
|
||||
break;
|
||||
case TI_PHY_DOWN:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_PHY_DOWN;
|
||||
break;
|
||||
case TI_BREAK:
|
||||
case TI_PROTO_ERR:
|
||||
case TI_NAK:
|
||||
case TI_ACK_NAK_TO:
|
||||
case TF_SMP_XMIT_RCV_ERR:
|
||||
case TC_ATA_R_ERR_RECV:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_INTERRUPTED;
|
||||
break;
|
||||
case TF_BREAK:
|
||||
case TU_BREAK:
|
||||
case TU_ACK_NAK_TO:
|
||||
case TF_SMPRSP_TO:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_DEV_NO_RESPONSE;
|
||||
break;
|
||||
case TF_NAK_RECV:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_NAK_R_ERR;
|
||||
break;
|
||||
case TA_I_T_NEXUS_LOSS:
|
||||
opcode = dl->status_block[0];
|
||||
goto Again;
|
||||
break;
|
||||
case TF_INV_CONN_HANDLE:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_DEVICE_UNKNOWN;
|
||||
break;
|
||||
case TF_REQUESTED_N_PENDING:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_PENDING;
|
||||
break;
|
||||
case TC_TASK_CLEARED:
|
||||
case TA_ON_REQ:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_ABORTED_TASK;
|
||||
break;
|
||||
|
||||
case TF_NO_SMP_CONN:
|
||||
case TF_TMF_NO_CTX:
|
||||
case TF_TMF_NO_TAG:
|
||||
case TF_TMF_TAG_FREE:
|
||||
case TF_TMF_TASK_DONE:
|
||||
case TF_TMF_NO_CONN_HANDLE:
|
||||
case TF_IRTT_TO:
|
||||
case TF_IU_SHORT:
|
||||
case TF_DATA_OFFS_ERR:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_DEV_NO_RESPONSE;
|
||||
break;
|
||||
|
||||
case TC_LINK_ADM_RESP:
|
||||
case TC_CONTROL_PHY:
|
||||
case TC_RESUME:
|
||||
case TC_PARTIAL_SG_LIST:
|
||||
default:
|
||||
ASD_DPRINTK("%s: dl opcode: 0x%x?\n", __FUNCTION__, opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (task->task_proto) {
|
||||
case SATA_PROTO:
|
||||
case SAS_PROTO_STP:
|
||||
asd_unbuild_ata_ascb(ascb);
|
||||
break;
|
||||
case SAS_PROTO_SMP:
|
||||
asd_unbuild_smp_ascb(ascb);
|
||||
break;
|
||||
case SAS_PROTO_SSP:
|
||||
asd_unbuild_ssp_ascb(ascb);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
|
||||
task->task_state_flags |= SAS_TASK_STATE_DONE;
|
||||
if (unlikely((task->task_state_flags & SAS_TASK_STATE_ABORTED))) {
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
ASD_DPRINTK("task 0x%p done with opcode 0x%x resp 0x%x "
|
||||
"stat 0x%x but aborted by upper layer!\n",
|
||||
task, opcode, ts->resp, ts->stat);
|
||||
complete(&ascb->completion);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
task->lldd_task = NULL;
|
||||
asd_ascb_free(ascb);
|
||||
mb();
|
||||
task->task_done(task);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- ATA ---------- */
|
||||
|
||||
static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task,
|
||||
unsigned long gfp_flags)
|
||||
{
|
||||
struct domain_device *dev = task->dev;
|
||||
struct scb *scb;
|
||||
u8 flags;
|
||||
int res = 0;
|
||||
|
||||
scb = ascb->scb;
|
||||
|
||||
if (unlikely(task->ata_task.device_control_reg_update))
|
||||
scb->header.opcode = CONTROL_ATA_DEV;
|
||||
else if (dev->sata_dev.command_set == ATA_COMMAND_SET)
|
||||
scb->header.opcode = INITIATE_ATA_TASK;
|
||||
else
|
||||
scb->header.opcode = INITIATE_ATAPI_TASK;
|
||||
|
||||
scb->ata_task.proto_conn_rate = (1 << 5); /* STP */
|
||||
if (dev->port->oob_mode == SAS_OOB_MODE)
|
||||
scb->ata_task.proto_conn_rate |= dev->linkrate;
|
||||
|
||||
scb->ata_task.total_xfer_len = cpu_to_le32(task->total_xfer_len);
|
||||
scb->ata_task.fis = task->ata_task.fis;
|
||||
scb->ata_task.fis.fis_type = 0x27;
|
||||
if (likely(!task->ata_task.device_control_reg_update))
|
||||
scb->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
|
||||
scb->ata_task.fis.flags &= 0xF0; /* PM_PORT field shall be 0 */
|
||||
if (dev->sata_dev.command_set == ATAPI_COMMAND_SET)
|
||||
memcpy(scb->ata_task.atapi_packet, task->ata_task.atapi_packet,
|
||||
16);
|
||||
scb->ata_task.sister_scb = cpu_to_le16(0xFFFF);
|
||||
scb->ata_task.conn_handle = cpu_to_le16(
|
||||
(u16)(unsigned long)dev->lldd_dev);
|
||||
|
||||
if (likely(!task->ata_task.device_control_reg_update)) {
|
||||
flags = 0;
|
||||
if (task->ata_task.dma_xfer)
|
||||
flags |= DATA_XFER_MODE_DMA;
|
||||
if (task->ata_task.use_ncq &&
|
||||
dev->sata_dev.command_set != ATAPI_COMMAND_SET)
|
||||
flags |= ATA_Q_TYPE_NCQ;
|
||||
flags |= data_dir_flags[task->data_dir];
|
||||
scb->ata_task.ata_flags = flags;
|
||||
|
||||
scb->ata_task.retry_count = task->ata_task.retry_count;
|
||||
|
||||
flags = 0;
|
||||
if (task->ata_task.set_affil_pol)
|
||||
flags |= SET_AFFIL_POLICY;
|
||||
if (task->ata_task.stp_affil_pol)
|
||||
flags |= STP_AFFIL_POLICY;
|
||||
scb->ata_task.flags = flags;
|
||||
}
|
||||
ascb->tasklet_complete = asd_task_tasklet_complete;
|
||||
|
||||
if (likely(!task->ata_task.device_control_reg_update))
|
||||
res = asd_map_scatterlist(task, scb->ata_task.sg_element,
|
||||
gfp_flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void asd_unbuild_ata_ascb(struct asd_ascb *a)
|
||||
{
|
||||
asd_unmap_scatterlist(a);
|
||||
}
|
||||
|
||||
/* ---------- SMP ---------- */
|
||||
|
||||
static int asd_build_smp_ascb(struct asd_ascb *ascb, struct sas_task *task,
|
||||
unsigned long gfp_flags)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct domain_device *dev = task->dev;
|
||||
struct scb *scb;
|
||||
|
||||
pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_req, 1,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_resp, 1,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
scb = ascb->scb;
|
||||
|
||||
scb->header.opcode = INITIATE_SMP_TASK;
|
||||
|
||||
scb->smp_task.proto_conn_rate = dev->linkrate;
|
||||
|
||||
scb->smp_task.smp_req.bus_addr =
|
||||
cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_req));
|
||||
scb->smp_task.smp_req.size =
|
||||
cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_req)-4);
|
||||
|
||||
scb->smp_task.smp_resp.bus_addr =
|
||||
cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_resp));
|
||||
scb->smp_task.smp_resp.size =
|
||||
cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_resp)-4);
|
||||
|
||||
scb->smp_task.sister_scb = cpu_to_le16(0xFFFF);
|
||||
scb->smp_task.conn_handle = cpu_to_le16((u16)
|
||||
(unsigned long)dev->lldd_dev);
|
||||
|
||||
ascb->tasklet_complete = asd_task_tasklet_complete;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void asd_unbuild_smp_ascb(struct asd_ascb *a)
|
||||
{
|
||||
struct sas_task *task = a->uldd_task;
|
||||
|
||||
BUG_ON(!task);
|
||||
pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_req, 1,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_resp, 1,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
}
|
||||
|
||||
/* ---------- SSP ---------- */
|
||||
|
||||
static int asd_build_ssp_ascb(struct asd_ascb *ascb, struct sas_task *task,
|
||||
unsigned long gfp_flags)
|
||||
{
|
||||
struct domain_device *dev = task->dev;
|
||||
struct scb *scb;
|
||||
int res = 0;
|
||||
|
||||
scb = ascb->scb;
|
||||
|
||||
scb->header.opcode = INITIATE_SSP_TASK;
|
||||
|
||||
scb->ssp_task.proto_conn_rate = (1 << 4); /* SSP */
|
||||
scb->ssp_task.proto_conn_rate |= dev->linkrate;
|
||||
scb->ssp_task.total_xfer_len = cpu_to_le32(task->total_xfer_len);
|
||||
scb->ssp_task.ssp_frame.frame_type = SSP_DATA;
|
||||
memcpy(scb->ssp_task.ssp_frame.hashed_dest_addr, dev->hashed_sas_addr,
|
||||
HASHED_SAS_ADDR_SIZE);
|
||||
memcpy(scb->ssp_task.ssp_frame.hashed_src_addr,
|
||||
dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
|
||||
scb->ssp_task.ssp_frame.tptt = cpu_to_be16(0xFFFF);
|
||||
|
||||
memcpy(scb->ssp_task.ssp_cmd.lun, task->ssp_task.LUN, 8);
|
||||
if (task->ssp_task.enable_first_burst)
|
||||
scb->ssp_task.ssp_cmd.efb_prio_attr |= EFB_MASK;
|
||||
scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_prio << 3);
|
||||
scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_attr & 7);
|
||||
memcpy(scb->ssp_task.ssp_cmd.cdb, task->ssp_task.cdb, 16);
|
||||
|
||||
scb->ssp_task.sister_scb = cpu_to_le16(0xFFFF);
|
||||
scb->ssp_task.conn_handle = cpu_to_le16(
|
||||
(u16)(unsigned long)dev->lldd_dev);
|
||||
scb->ssp_task.data_dir = data_dir_flags[task->data_dir];
|
||||
scb->ssp_task.retry_count = scb->ssp_task.retry_count;
|
||||
|
||||
ascb->tasklet_complete = asd_task_tasklet_complete;
|
||||
|
||||
res = asd_map_scatterlist(task, scb->ssp_task.sg_element, gfp_flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void asd_unbuild_ssp_ascb(struct asd_ascb *a)
|
||||
{
|
||||
asd_unmap_scatterlist(a);
|
||||
}
|
||||
|
||||
/* ---------- Execute Task ---------- */
|
||||
|
||||
static inline int asd_can_queue(struct asd_ha_struct *asd_ha, int num)
|
||||
{
|
||||
int res = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
|
||||
if ((asd_ha->seq.can_queue - num) < 0)
|
||||
res = -SAS_QUEUE_FULL;
|
||||
else
|
||||
asd_ha->seq.can_queue -= num;
|
||||
spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_execute_task(struct sas_task *task, const int num,
|
||||
unsigned long gfp_flags)
|
||||
{
|
||||
int res = 0;
|
||||
LIST_HEAD(alist);
|
||||
struct sas_task *t = task;
|
||||
struct asd_ascb *ascb = NULL, *a;
|
||||
struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
|
||||
|
||||
res = asd_can_queue(asd_ha, num);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
res = num;
|
||||
ascb = asd_ascb_alloc_list(asd_ha, &res, gfp_flags);
|
||||
if (res) {
|
||||
res = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
__list_add(&alist, ascb->list.prev, &ascb->list);
|
||||
list_for_each_entry(a, &alist, list) {
|
||||
a->uldd_task = t;
|
||||
t->lldd_task = a;
|
||||
t = list_entry(t->list.next, struct sas_task, list);
|
||||
}
|
||||
list_for_each_entry(a, &alist, list) {
|
||||
t = a->uldd_task;
|
||||
a->uldd_timer = 1;
|
||||
if (t->task_proto & SAS_PROTO_STP)
|
||||
t->task_proto = SAS_PROTO_STP;
|
||||
switch (t->task_proto) {
|
||||
case SATA_PROTO:
|
||||
case SAS_PROTO_STP:
|
||||
res = asd_build_ata_ascb(a, t, gfp_flags);
|
||||
break;
|
||||
case SAS_PROTO_SMP:
|
||||
res = asd_build_smp_ascb(a, t, gfp_flags);
|
||||
break;
|
||||
case SAS_PROTO_SSP:
|
||||
res = asd_build_ssp_ascb(a, t, gfp_flags);
|
||||
break;
|
||||
default:
|
||||
asd_printk("unknown sas_task proto: 0x%x\n",
|
||||
t->task_proto);
|
||||
res = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
if (res)
|
||||
goto out_err_unmap;
|
||||
}
|
||||
list_del_init(&alist);
|
||||
|
||||
res = asd_post_ascb_list(asd_ha, ascb, num);
|
||||
if (unlikely(res)) {
|
||||
a = NULL;
|
||||
__list_add(&alist, ascb->list.prev, &ascb->list);
|
||||
goto out_err_unmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out_err_unmap:
|
||||
{
|
||||
struct asd_ascb *b = a;
|
||||
list_for_each_entry(a, &alist, list) {
|
||||
if (a == b)
|
||||
break;
|
||||
t = a->uldd_task;
|
||||
switch (t->task_proto) {
|
||||
case SATA_PROTO:
|
||||
case SAS_PROTO_STP:
|
||||
asd_unbuild_ata_ascb(a);
|
||||
break;
|
||||
case SAS_PROTO_SMP:
|
||||
asd_unbuild_smp_ascb(a);
|
||||
break;
|
||||
case SAS_PROTO_SSP:
|
||||
asd_unbuild_ssp_ascb(a);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
t->lldd_task = NULL;
|
||||
}
|
||||
}
|
||||
list_del_init(&alist);
|
||||
out_err:
|
||||
if (ascb)
|
||||
asd_ascb_free_list(ascb);
|
||||
asd_can_dequeue(asd_ha, num);
|
||||
return res;
|
||||
}
|
||||
636
drivers/scsi/aic94xx/aic94xx_tmf.c
Normal file
636
drivers/scsi/aic94xx/aic94xx_tmf.c
Normal file
@@ -0,0 +1,636 @@
|
||||
/*
|
||||
* Aic94xx Task Management Functions
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This file is part of the aic94xx driver.
|
||||
*
|
||||
* The aic94xx driver is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; version 2 of the
|
||||
* License.
|
||||
*
|
||||
* The aic94xx driver is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with the aic94xx driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_sas.h"
|
||||
#include "aic94xx_hwi.h"
|
||||
|
||||
/* ---------- Internal enqueue ---------- */
|
||||
|
||||
static int asd_enqueue_internal(struct asd_ascb *ascb,
|
||||
void (*tasklet_complete)(struct asd_ascb *,
|
||||
struct done_list_struct *),
|
||||
void (*timed_out)(unsigned long))
|
||||
{
|
||||
int res;
|
||||
|
||||
ascb->tasklet_complete = tasklet_complete;
|
||||
ascb->uldd_timer = 1;
|
||||
|
||||
ascb->timer.data = (unsigned long) ascb;
|
||||
ascb->timer.function = timed_out;
|
||||
ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;
|
||||
|
||||
add_timer(&ascb->timer);
|
||||
|
||||
res = asd_post_ascb_list(ascb->ha, ascb, 1);
|
||||
if (unlikely(res))
|
||||
del_timer(&ascb->timer);
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void asd_timedout_common(unsigned long data)
|
||||
{
|
||||
struct asd_ascb *ascb = (void *) data;
|
||||
struct asd_seq_data *seq = &ascb->ha->seq;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&seq->pend_q_lock, flags);
|
||||
seq->pending--;
|
||||
list_del_init(&ascb->list);
|
||||
spin_unlock_irqrestore(&seq->pend_q_lock, flags);
|
||||
}
|
||||
|
||||
/* ---------- CLEAR NEXUS ---------- */
|
||||
|
||||
static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
ASD_DPRINTK("%s: here\n", __FUNCTION__);
|
||||
if (!del_timer(&ascb->timer)) {
|
||||
ASD_DPRINTK("%s: couldn't delete timer\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
ASD_DPRINTK("%s: opcode: 0x%x\n", __FUNCTION__, dl->opcode);
|
||||
ascb->uldd_task = (void *) (unsigned long) dl->opcode;
|
||||
complete(&ascb->completion);
|
||||
}
|
||||
|
||||
static void asd_clear_nexus_timedout(unsigned long data)
|
||||
{
|
||||
struct asd_ascb *ascb = (void *) data;
|
||||
|
||||
ASD_DPRINTK("%s: here\n", __FUNCTION__);
|
||||
asd_timedout_common(data);
|
||||
ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED;
|
||||
complete(&ascb->completion);
|
||||
}
|
||||
|
||||
#define CLEAR_NEXUS_PRE \
|
||||
ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \
|
||||
res = 1; \
|
||||
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \
|
||||
if (!ascb) \
|
||||
return -ENOMEM; \
|
||||
\
|
||||
scb = ascb->scb; \
|
||||
scb->header.opcode = CLEAR_NEXUS
|
||||
|
||||
#define CLEAR_NEXUS_POST \
|
||||
ASD_DPRINTK("%s: POST\n", __FUNCTION__); \
|
||||
res = asd_enqueue_internal(ascb, asd_clear_nexus_tasklet_complete, \
|
||||
asd_clear_nexus_timedout); \
|
||||
if (res) \
|
||||
goto out_err; \
|
||||
ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __FUNCTION__); \
|
||||
wait_for_completion(&ascb->completion); \
|
||||
res = (int) (unsigned long) ascb->uldd_task; \
|
||||
if (res == TC_NO_ERROR) \
|
||||
res = TMF_RESP_FUNC_COMPLETE; \
|
||||
out_err: \
|
||||
asd_ascb_free(ascb); \
|
||||
return res
|
||||
|
||||
int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = sas_ha->lldd_ha;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_ADAPTER;
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
|
||||
int asd_clear_nexus_port(struct asd_sas_port *port)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = port->ha->lldd_ha;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_PORT;
|
||||
scb->clear_nexus.conn_mask = port->phy_mask;
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int asd_clear_nexus_I_T(struct domain_device *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_I_T;
|
||||
scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ;
|
||||
if (dev->tproto)
|
||||
scb->clear_nexus.flags |= SUSPEND_TX;
|
||||
scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
|
||||
dev->lldd_dev);
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_I_T_L;
|
||||
scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ;
|
||||
if (dev->tproto)
|
||||
scb->clear_nexus.flags |= SUSPEND_TX;
|
||||
memcpy(scb->clear_nexus.ssp_task.lun, lun, 8);
|
||||
scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
|
||||
dev->lldd_dev);
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
|
||||
static int asd_clear_nexus_tag(struct sas_task *task)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
|
||||
struct asd_ascb *tascb = task->lldd_task;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_TAG;
|
||||
memcpy(scb->clear_nexus.ssp_task.lun, task->ssp_task.LUN, 8);
|
||||
scb->clear_nexus.ssp_task.tag = tascb->tag;
|
||||
if (task->dev->tproto)
|
||||
scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
|
||||
task->dev->lldd_dev);
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
|
||||
static int asd_clear_nexus_index(struct sas_task *task)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
|
||||
struct asd_ascb *tascb = task->lldd_task;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_TRANS_CX;
|
||||
if (task->dev->tproto)
|
||||
scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
|
||||
task->dev->lldd_dev);
|
||||
scb->clear_nexus.index = cpu_to_le16(tascb->tc_index);
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
|
||||
/* ---------- TMFs ---------- */
|
||||
|
||||
static void asd_tmf_timedout(unsigned long data)
|
||||
{
|
||||
struct asd_ascb *ascb = (void *) data;
|
||||
|
||||
ASD_DPRINTK("tmf timed out\n");
|
||||
asd_timedout_common(data);
|
||||
ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED;
|
||||
complete(&ascb->completion);
|
||||
}
|
||||
|
||||
static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
unsigned long flags;
|
||||
struct tc_resp_sb_struct {
|
||||
__le16 index_escb;
|
||||
u8 len_lsb;
|
||||
u8 flags;
|
||||
} __attribute__ ((packed)) *resp_sb = (void *) dl->status_block;
|
||||
|
||||
int edb_id = ((resp_sb->flags & 0x70) >> 4)-1;
|
||||
struct asd_ascb *escb;
|
||||
struct asd_dma_tok *edb;
|
||||
struct ssp_frame_hdr *fh;
|
||||
struct ssp_response_iu *ru;
|
||||
int res = TMF_RESP_FUNC_FAILED;
|
||||
|
||||
ASD_DPRINTK("tmf resp tasklet\n");
|
||||
|
||||
spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags);
|
||||
escb = asd_tc_index_find(&asd_ha->seq,
|
||||
(int)le16_to_cpu(resp_sb->index_escb));
|
||||
spin_unlock_irqrestore(&asd_ha->seq.tc_index_lock, flags);
|
||||
|
||||
if (!escb) {
|
||||
ASD_DPRINTK("Uh-oh! No escb for this dl?!\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];
|
||||
ascb->tag = *(__be16 *)(edb->vaddr+4);
|
||||
fh = edb->vaddr + 16;
|
||||
ru = edb->vaddr + 16 + sizeof(*fh);
|
||||
res = ru->status;
|
||||
if (ru->datapres == 1) /* Response data present */
|
||||
res = ru->resp_data[3];
|
||||
#if 0
|
||||
ascb->tag = fh->tag;
|
||||
#endif
|
||||
ascb->tag_valid = 1;
|
||||
|
||||
asd_invalidate_edb(escb, edb_id);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void asd_tmf_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
if (!del_timer(&ascb->timer))
|
||||
return;
|
||||
|
||||
ASD_DPRINTK("tmf tasklet complete\n");
|
||||
|
||||
if (dl->opcode == TC_SSP_RESP)
|
||||
ascb->uldd_task = (void *) (unsigned long)
|
||||
asd_get_tmf_resp_tasklet(ascb, dl);
|
||||
else
|
||||
ascb->uldd_task = (void *) 0xFF00 + (unsigned long) dl->opcode;
|
||||
|
||||
complete(&ascb->completion);
|
||||
}
|
||||
|
||||
static inline int asd_clear_nexus(struct sas_task *task)
|
||||
{
|
||||
int res = TMF_RESP_FUNC_FAILED;
|
||||
struct asd_ascb *tascb = task->lldd_task;
|
||||
unsigned long flags;
|
||||
|
||||
ASD_DPRINTK("task not done, clearing nexus\n");
|
||||
if (tascb->tag_valid)
|
||||
res = asd_clear_nexus_tag(task);
|
||||
else
|
||||
res = asd_clear_nexus_index(task);
|
||||
wait_for_completion_timeout(&tascb->completion,
|
||||
AIC94XX_SCB_TIMEOUT);
|
||||
ASD_DPRINTK("came back from clear nexus\n");
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (task->task_state_flags & SAS_TASK_STATE_DONE)
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_abort_task -- ABORT TASK TMF
|
||||
* @task: the task to be aborted
|
||||
*
|
||||
* Before calling ABORT TASK the task state flags should be ORed with
|
||||
* SAS_TASK_STATE_ABORTED (unless SAS_TASK_STATE_DONE is set) under
|
||||
* the task_state_lock IRQ spinlock, then ABORT TASK *must* be called.
|
||||
*
|
||||
* Implements the ABORT TASK TMF, I_T_L_Q nexus.
|
||||
* Returns: SAS TMF responses (see sas_task.h),
|
||||
* -ENOMEM,
|
||||
* -SAS_QUEUE_FULL.
|
||||
*
|
||||
* When ABORT TASK returns, the caller of ABORT TASK checks first the
|
||||
* task->task_state_flags, and then the return value of ABORT TASK.
|
||||
*
|
||||
* If the task has task state bit SAS_TASK_STATE_DONE set, then the
|
||||
* task was completed successfully prior to it being aborted. The
|
||||
* caller of ABORT TASK has responsibility to call task->task_done()
|
||||
* xor free the task, depending on their framework. The return code
|
||||
* is TMF_RESP_FUNC_FAILED in this case.
|
||||
*
|
||||
* Else the SAS_TASK_STATE_DONE bit is not set,
|
||||
* If the return code is TMF_RESP_FUNC_COMPLETE, then
|
||||
* the task was aborted successfully. The caller of
|
||||
* ABORT TASK has responsibility to call task->task_done()
|
||||
* to finish the task, xor free the task depending on their
|
||||
* framework.
|
||||
* else
|
||||
* the ABORT TASK returned some kind of error. The task
|
||||
* was _not_ cancelled. Nothing can be assumed.
|
||||
* The caller of ABORT TASK may wish to retry.
|
||||
*/
|
||||
int asd_abort_task(struct sas_task *task)
|
||||
{
|
||||
struct asd_ascb *tascb = task->lldd_task;
|
||||
struct asd_ha_struct *asd_ha = tascb->ha;
|
||||
int res = 1;
|
||||
unsigned long flags;
|
||||
struct asd_ascb *ascb = NULL;
|
||||
struct scb *scb;
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task);
|
||||
goto out_done;
|
||||
}
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
|
||||
if (!ascb)
|
||||
return -ENOMEM;
|
||||
scb = ascb->scb;
|
||||
|
||||
scb->header.opcode = ABORT_TASK;
|
||||
|
||||
switch (task->task_proto) {
|
||||
case SATA_PROTO:
|
||||
case SAS_PROTO_STP:
|
||||
scb->abort_task.proto_conn_rate = (1 << 5); /* STP */
|
||||
break;
|
||||
case SAS_PROTO_SSP:
|
||||
scb->abort_task.proto_conn_rate = (1 << 4); /* SSP */
|
||||
scb->abort_task.proto_conn_rate |= task->dev->linkrate;
|
||||
break;
|
||||
case SAS_PROTO_SMP:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (task->task_proto == SAS_PROTO_SSP) {
|
||||
scb->abort_task.ssp_frame.frame_type = SSP_TASK;
|
||||
memcpy(scb->abort_task.ssp_frame.hashed_dest_addr,
|
||||
task->dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
|
||||
memcpy(scb->abort_task.ssp_frame.hashed_src_addr,
|
||||
task->dev->port->ha->hashed_sas_addr,
|
||||
HASHED_SAS_ADDR_SIZE);
|
||||
scb->abort_task.ssp_frame.tptt = cpu_to_be16(0xFFFF);
|
||||
|
||||
memcpy(scb->abort_task.ssp_task.lun, task->ssp_task.LUN, 8);
|
||||
scb->abort_task.ssp_task.tmf = TMF_ABORT_TASK;
|
||||
scb->abort_task.ssp_task.tag = cpu_to_be16(0xFFFF);
|
||||
}
|
||||
|
||||
scb->abort_task.sister_scb = cpu_to_le16(0xFFFF);
|
||||
scb->abort_task.conn_handle = cpu_to_le16(
|
||||
(u16)(unsigned long)task->dev->lldd_dev);
|
||||
scb->abort_task.retry_count = 1;
|
||||
scb->abort_task.index = cpu_to_le16((u16)tascb->tc_index);
|
||||
scb->abort_task.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST);
|
||||
|
||||
res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
|
||||
asd_tmf_timedout);
|
||||
if (res)
|
||||
goto out;
|
||||
wait_for_completion(&ascb->completion);
|
||||
ASD_DPRINTK("tmf came back\n");
|
||||
|
||||
res = (int) (unsigned long) ascb->uldd_task;
|
||||
tascb->tag = ascb->tag;
|
||||
tascb->tag_valid = ascb->tag_valid;
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task);
|
||||
goto out_done;
|
||||
}
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
switch (res) {
|
||||
/* The task to be aborted has been sent to the device.
|
||||
* We got a Response IU for the ABORT TASK TMF. */
|
||||
case TC_NO_ERROR + 0xFF00:
|
||||
case TMF_RESP_FUNC_COMPLETE:
|
||||
case TMF_RESP_FUNC_FAILED:
|
||||
res = asd_clear_nexus(task);
|
||||
break;
|
||||
case TMF_RESP_INVALID_FRAME:
|
||||
case TMF_RESP_OVERLAPPED_TAG:
|
||||
case TMF_RESP_FUNC_ESUPP:
|
||||
case TMF_RESP_NO_LUN:
|
||||
goto out_done; break;
|
||||
}
|
||||
/* In the following we assume that the managing layer
|
||||
* will _never_ make a mistake, when issuing ABORT TASK.
|
||||
*/
|
||||
switch (res) {
|
||||
default:
|
||||
res = asd_clear_nexus(task);
|
||||
/* fallthrough */
|
||||
case TC_NO_ERROR + 0xFF00:
|
||||
case TMF_RESP_FUNC_COMPLETE:
|
||||
break;
|
||||
/* The task hasn't been sent to the device xor we never got
|
||||
* a (sane) Response IU for the ABORT TASK TMF.
|
||||
*/
|
||||
case TF_NAK_RECV + 0xFF00:
|
||||
res = TMF_RESP_INVALID_FRAME;
|
||||
break;
|
||||
case TF_TMF_TASK_DONE + 0xFF00: /* done but not reported yet */
|
||||
res = TMF_RESP_FUNC_FAILED;
|
||||
wait_for_completion_timeout(&tascb->completion,
|
||||
AIC94XX_SCB_TIMEOUT);
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (task->task_state_flags & SAS_TASK_STATE_DONE)
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
goto out_done;
|
||||
case TF_TMF_NO_TAG + 0xFF00:
|
||||
case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */
|
||||
case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
goto out_done;
|
||||
case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */
|
||||
res = TMF_RESP_FUNC_ESUPP;
|
||||
goto out;
|
||||
}
|
||||
out_done:
|
||||
if (res == TMF_RESP_FUNC_COMPLETE) {
|
||||
task->lldd_task = NULL;
|
||||
mb();
|
||||
asd_ascb_free(tascb);
|
||||
}
|
||||
out:
|
||||
asd_ascb_free(ascb);
|
||||
ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_initiate_ssp_tmf -- send a TMF to an I_T_L or I_T_L_Q nexus
|
||||
* @dev: pointer to struct domain_device of interest
|
||||
* @lun: pointer to u8[8] which is the LUN
|
||||
* @tmf: the TMF to be performed (see sas_task.h or the SAS spec)
|
||||
* @index: the transaction context of the task to be queried if QT TMF
|
||||
*
|
||||
* This function is used to send ABORT TASK SET, CLEAR ACA,
|
||||
* CLEAR TASK SET, LU RESET and QUERY TASK TMFs.
|
||||
*
|
||||
* No SCBs should be queued to the I_T_L nexus when this SCB is
|
||||
* pending.
|
||||
*
|
||||
* Returns: TMF response code (see sas_task.h or the SAS spec)
|
||||
*/
|
||||
static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun,
|
||||
int tmf, int index)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
struct asd_ascb *ascb;
|
||||
int res = 1;
|
||||
struct scb *scb;
|
||||
|
||||
if (!(dev->tproto & SAS_PROTO_SSP))
|
||||
return TMF_RESP_FUNC_ESUPP;
|
||||
|
||||
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
|
||||
if (!ascb)
|
||||
return -ENOMEM;
|
||||
scb = ascb->scb;
|
||||
|
||||
if (tmf == TMF_QUERY_TASK)
|
||||
scb->header.opcode = QUERY_SSP_TASK;
|
||||
else
|
||||
scb->header.opcode = INITIATE_SSP_TMF;
|
||||
|
||||
scb->ssp_tmf.proto_conn_rate = (1 << 4); /* SSP */
|
||||
scb->ssp_tmf.proto_conn_rate |= dev->linkrate;
|
||||
/* SSP frame header */
|
||||
scb->ssp_tmf.ssp_frame.frame_type = SSP_TASK;
|
||||
memcpy(scb->ssp_tmf.ssp_frame.hashed_dest_addr,
|
||||
dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
|
||||
memcpy(scb->ssp_tmf.ssp_frame.hashed_src_addr,
|
||||
dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
|
||||
scb->ssp_tmf.ssp_frame.tptt = cpu_to_be16(0xFFFF);
|
||||
/* SSP Task IU */
|
||||
memcpy(scb->ssp_tmf.ssp_task.lun, lun, 8);
|
||||
scb->ssp_tmf.ssp_task.tmf = tmf;
|
||||
|
||||
scb->ssp_tmf.sister_scb = cpu_to_le16(0xFFFF);
|
||||
scb->ssp_tmf.conn_handle= cpu_to_le16((u16)(unsigned long)
|
||||
dev->lldd_dev);
|
||||
scb->ssp_tmf.retry_count = 1;
|
||||
scb->ssp_tmf.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST);
|
||||
if (tmf == TMF_QUERY_TASK)
|
||||
scb->ssp_tmf.index = cpu_to_le16(index);
|
||||
|
||||
res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
|
||||
asd_tmf_timedout);
|
||||
if (res)
|
||||
goto out_err;
|
||||
wait_for_completion(&ascb->completion);
|
||||
res = (int) (unsigned long) ascb->uldd_task;
|
||||
|
||||
switch (res) {
|
||||
case TC_NO_ERROR + 0xFF00:
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
break;
|
||||
case TF_NAK_RECV + 0xFF00:
|
||||
res = TMF_RESP_INVALID_FRAME;
|
||||
break;
|
||||
case TF_TMF_TASK_DONE + 0xFF00:
|
||||
res = TMF_RESP_FUNC_FAILED;
|
||||
break;
|
||||
case TF_TMF_NO_TAG + 0xFF00:
|
||||
case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */
|
||||
case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
break;
|
||||
case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */
|
||||
res = TMF_RESP_FUNC_ESUPP;
|
||||
break;
|
||||
default:
|
||||
ASD_DPRINTK("%s: converting result 0x%x to TMF_RESP_FUNC_FAILED\n",
|
||||
__FUNCTION__, res);
|
||||
res = TMF_RESP_FUNC_FAILED;
|
||||
break;
|
||||
}
|
||||
out_err:
|
||||
asd_ascb_free(ascb);
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_abort_task_set(struct domain_device *dev, u8 *lun)
|
||||
{
|
||||
int res = asd_initiate_ssp_tmf(dev, lun, TMF_ABORT_TASK_SET, 0);
|
||||
|
||||
if (res == TMF_RESP_FUNC_COMPLETE)
|
||||
asd_clear_nexus_I_T_L(dev, lun);
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_clear_aca(struct domain_device *dev, u8 *lun)
|
||||
{
|
||||
int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_ACA, 0);
|
||||
|
||||
if (res == TMF_RESP_FUNC_COMPLETE)
|
||||
asd_clear_nexus_I_T_L(dev, lun);
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_clear_task_set(struct domain_device *dev, u8 *lun)
|
||||
{
|
||||
int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_TASK_SET, 0);
|
||||
|
||||
if (res == TMF_RESP_FUNC_COMPLETE)
|
||||
asd_clear_nexus_I_T_L(dev, lun);
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_lu_reset(struct domain_device *dev, u8 *lun)
|
||||
{
|
||||
int res = asd_initiate_ssp_tmf(dev, lun, TMF_LU_RESET, 0);
|
||||
|
||||
if (res == TMF_RESP_FUNC_COMPLETE)
|
||||
asd_clear_nexus_I_T_L(dev, lun);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_query_task -- send a QUERY TASK TMF to an I_T_L_Q nexus
|
||||
* task: pointer to sas_task struct of interest
|
||||
*
|
||||
* Returns: TMF_RESP_FUNC_COMPLETE if the task is not in the task set,
|
||||
* or TMF_RESP_FUNC_SUCC if the task is in the task set.
|
||||
*
|
||||
* Normally the management layer sets the task to aborted state,
|
||||
* and then calls query task and then abort task.
|
||||
*/
|
||||
int asd_query_task(struct sas_task *task)
|
||||
{
|
||||
struct asd_ascb *ascb = task->lldd_task;
|
||||
int index;
|
||||
|
||||
if (ascb) {
|
||||
index = ascb->tc_index;
|
||||
return asd_initiate_ssp_tmf(task->dev, task->ssp_task.LUN,
|
||||
TMF_QUERY_TASK, index);
|
||||
}
|
||||
return TMF_RESP_FUNC_COMPLETE;
|
||||
}
|
||||
Reference in New Issue
Block a user