aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/aic94xx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/aic94xx')
-rw-r--r--drivers/scsi/aic94xx/Kconfig1
-rw-r--r--drivers/scsi/aic94xx/Makefile6
-rw-r--r--drivers/scsi/aic94xx/aic94xx.h27
-rw-r--r--drivers/scsi/aic94xx/aic94xx_dev.c88
-rw-r--r--drivers/scsi/aic94xx/aic94xx_dump.c18
-rw-r--r--drivers/scsi/aic94xx/aic94xx_dump.h9
-rw-r--r--drivers/scsi/aic94xx/aic94xx_hwi.c77
-rw-r--r--drivers/scsi/aic94xx/aic94xx_hwi.h33
-rw-r--r--drivers/scsi/aic94xx/aic94xx_init.c410
-rw-r--r--drivers/scsi/aic94xx/aic94xx_reg.c53
-rw-r--r--drivers/scsi/aic94xx/aic94xx_reg_def.h15
-rw-r--r--drivers/scsi/aic94xx/aic94xx_sas.h4
-rw-r--r--drivers/scsi/aic94xx/aic94xx_scb.c285
-rw-r--r--drivers/scsi/aic94xx/aic94xx_sds.c416
-rw-r--r--drivers/scsi/aic94xx/aic94xx_sds.h121
-rw-r--r--drivers/scsi/aic94xx/aic94xx_seq.c111
-rw-r--r--drivers/scsi/aic94xx/aic94xx_seq.h8
-rw-r--r--drivers/scsi/aic94xx/aic94xx_task.c114
-rw-r--r--drivers/scsi/aic94xx/aic94xx_tmf.c356
19 files changed, 1582 insertions, 570 deletions
diff --git a/drivers/scsi/aic94xx/Kconfig b/drivers/scsi/aic94xx/Kconfig
index 0ed391d8ee8..c83fe751d0b 100644
--- a/drivers/scsi/aic94xx/Kconfig
+++ b/drivers/scsi/aic94xx/Kconfig
@@ -28,6 +28,7 @@ config SCSI_AIC94XX
tristate "Adaptec AIC94xx SAS/SATA support"
depends on PCI
select SCSI_SAS_LIBSAS
+ select FW_LOADER
help
This driver supports Adaptec's SAS/SATA 3Gb/s 64 bit PCI-X
AIC94xx chip based host adapters.
diff --git a/drivers/scsi/aic94xx/Makefile b/drivers/scsi/aic94xx/Makefile
index e6b70123940..c0a15c75458 100644
--- a/drivers/scsi/aic94xx/Makefile
+++ b/drivers/scsi/aic94xx/Makefile
@@ -6,7 +6,7 @@
#
# This file is licensed under GPLv2.
#
-# This file is part of the the aic94xx driver.
+# 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
@@ -22,9 +22,7 @@
# 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
+ccflags-$(CONFIG_AIC94XX_DEBUG) := -DASD_DEBUG -DASD_ENTER_EXIT
obj-$(CONFIG_SCSI_AIC94XX) += aic94xx.o
aic94xx-y += aic94xx_init.o \
diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h
index 71a031df7a3..66cda669b41 100644
--- a/drivers/scsi/aic94xx/aic94xx.h
+++ b/drivers/scsi/aic94xx/aic94xx.h
@@ -39,9 +39,9 @@
#ifdef ASD_ENTER_EXIT
#define ENTER printk(KERN_NOTICE "%s: ENTER %s\n", ASD_DRIVER_NAME, \
- __FUNCTION__)
+ __func__)
#define EXIT printk(KERN_NOTICE "%s: --EXIT %s\n", ASD_DRIVER_NAME, \
- __FUNCTION__)
+ __func__)
#else
#define ENTER
#define EXIT
@@ -56,9 +56,8 @@
/* 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];
+extern struct kmem_cache *asd_dma_token_cache;
+extern struct kmem_cache *asd_ascb_cache;
static inline void asd_stringify_sas_addr(char *p, const u8 *sas_addr)
{
@@ -68,21 +67,6 @@ static inline void asd_stringify_sas_addr(char *p, const u8 *sas_addr)
*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;
@@ -96,12 +80,15 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id);
int asd_execute_task(struct sas_task *, int num, gfp_t gfp_flags);
+void asd_set_dmamode(struct domain_device *dev);
+
/* ---------- 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_I_T_nexus_reset(struct domain_device *dev);
int asd_query_task(struct sas_task *);
/* ---------- Adapter and Port management ---------- */
diff --git a/drivers/scsi/aic94xx/aic94xx_dev.c b/drivers/scsi/aic94xx/aic94xx_dev.c
index 6f8901b748f..33072388ea1 100644
--- a/drivers/scsi/aic94xx/aic94xx_dev.c
+++ b/drivers/scsi/aic94xx/aic94xx_dev.c
@@ -35,20 +35,16 @@
#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)
+static 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);
@@ -75,24 +71,20 @@ out:
#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)
+static 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)
+static 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)
+ if (dev->dev_type == SAS_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);
@@ -117,36 +109,46 @@ static int asd_init_sata_tag_ddb(struct domain_device *dev)
return 0;
}
-static inline int asd_init_sata(struct domain_device *dev)
+void asd_set_dmamode(struct domain_device *dev)
{
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+ struct ata_device *ata_dev = sas_to_ata_dev(dev);
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;
+ if (dev->dev_type == SAS_SATA_DEV || dev->dev_type == SAS_SATA_PM_PORT) {
+ if (ata_id_has_ncq(ata_dev->id))
+ qdepth = ata_id_queue_depth(ata_dev->id);
asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK,
- (1<<qdepth)-1);
+ (1ULL<<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) {
+
+ if (qdepth > 0)
+ if (asd_init_sata_tag_ddb(dev) != 0) {
+ unsigned long flags;
+
+ spin_lock_irqsave(dev->sata_dev.ap->lock, flags);
+ ata_dev->flags |= ATA_DFLAG_NCQ_OFF;
+ spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags);
+ }
+}
+
+static 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;
+
+ asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
+ if (dev->dev_type == SAS_SATA_DEV || dev->dev_type == SAS_SATA_PM ||
+ dev->dev_type == SAS_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;
+
+ return 0;
}
static int asd_init_target_ddb(struct domain_device *dev)
@@ -172,8 +174,8 @@ static int asd_init_target_ddb(struct domain_device *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)) {
+ if ((dev->dev_type == SAS_SATA_DEV) ||
+ (dev->tproto & SAS_PROTOCOL_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 &&
@@ -186,8 +188,8 @@ static int asd_init_target_ddb(struct domain_device *dev)
} else {
flags |= CONCURRENT_CONN_SUPP;
if (!dev->parent &&
- (dev->dev_type == EDGE_DEV ||
- dev->dev_type == FANOUT_DEV))
+ (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
+ dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE))
asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN,
4);
else
@@ -196,12 +198,12 @@ static int asd_init_target_ddb(struct domain_device *dev)
asd_ddbsite_write_byte(asd_ha, ddb, NUM_CTX, 1);
}
}
- if (dev->dev_type == SATA_PM)
+ if (dev->dev_type == SAS_SATA_PM)
flags |= SATA_MULTIPORT;
asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS, flags);
flags = 0;
- if (dev->tproto & SAS_PROTO_STP)
+ if (dev->tproto & SAS_PROTOCOL_STP)
flags |= STP_CL_POL_NO_TX;
asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS2, flags);
@@ -209,7 +211,7 @@ static int asd_init_target_ddb(struct domain_device *dev)
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)) {
+ if (dev->dev_type == SAS_SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
i = asd_init_sata(dev);
if (i < 0) {
asd_free_ddb(asd_ha, ddb);
@@ -217,7 +219,7 @@ static int asd_init_target_ddb(struct domain_device *dev)
}
}
- if (dev->dev_type == SAS_END_DEV) {
+ if (dev->dev_type == SAS_END_DEVICE) {
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,
@@ -320,13 +322,16 @@ out:
int asd_dev_found(struct domain_device *dev)
{
+ unsigned long flags;
int res = 0;
+ struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+ spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
switch (dev->dev_type) {
- case SATA_PM:
+ case SAS_SATA_PM:
res = asd_init_sata_pm_ddb(dev);
break;
- case SATA_PM_PORT:
+ case SAS_SATA_PM_PORT:
res = asd_init_sata_pm_port_ddb(dev);
break;
default:
@@ -335,14 +340,18 @@ int asd_dev_found(struct domain_device *dev)
else
res = asd_init_initiator_ddb(dev);
}
+ spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
+
return res;
}
void asd_dev_gone(struct domain_device *dev)
{
int ddb, sister_ddb;
+ unsigned long flags;
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+ spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
ddb = (int) (unsigned long) dev->lldd_dev;
sister_ddb = asd_ddbsite_read_word(asd_ha, ddb, SISTER_DDB);
@@ -350,4 +359,5 @@ void asd_dev_gone(struct domain_device *dev)
asd_free_ddb(asd_ha, sister_ddb);
asd_free_ddb(asd_ha, ddb);
dev->lldd_dev = NULL;
+ spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
}
diff --git a/drivers/scsi/aic94xx/aic94xx_dump.c b/drivers/scsi/aic94xx/aic94xx_dump.c
index e6ade5996d9..a16a77c8b9c 100644
--- a/drivers/scsi/aic94xx/aic94xx_dump.c
+++ b/drivers/scsi/aic94xx/aic94xx_dump.c
@@ -29,7 +29,7 @@
*
*/
-#include "linux/pci.h"
+#include <linux/pci.h>
#include "aic94xx.h"
#include "aic94xx_reg.h"
#include "aic94xx_reg_def.h"
@@ -556,7 +556,7 @@ static void asd_dump_lseq_state(struct asd_ha_struct *asd_ha, int lseq)
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_dword(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);
@@ -738,6 +738,8 @@ static void asd_dump_lseq_state(struct asd_ha_struct *asd_ha, int lseq)
PRINT_LMIP_dword(asd_ha, lseq, DEV_PRES_TIMER_TERM_TS);
}
+#if 0
+
/**
* asd_dump_ddb_site -- dump a CSEQ DDB site
* @asd_ha: pointer to host adapter structure
@@ -880,6 +882,8 @@ void asd_dump_scb_sites(struct asd_ha_struct *asd_ha)
}
}
+#endif /* 0 */
+
/**
* ads_dump_seq_state -- dump CSEQ and LSEQ states
* @asd_ha: pointer to host adapter structure
@@ -903,11 +907,11 @@ void asd_dump_frame_rcvd(struct asd_phy *phy,
int i;
switch ((dl->status_block[1] & 0x70) >> 3) {
- case SAS_PROTO_STP:
+ case SAS_PROTOCOL_STP:
ASD_DPRINTK("STP proto device-to-host FIS:\n");
break;
default:
- case SAS_PROTO_SSP:
+ case SAS_PROTOCOL_SSP:
ASD_DPRINTK("SAS proto IDENTIFY:\n");
break;
}
@@ -922,7 +926,9 @@ void asd_dump_frame_rcvd(struct asd_phy *phy,
spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
}
-static inline void asd_dump_scb(struct asd_ascb *ascb, int ind)
+#if 0
+
+static 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",
@@ -956,4 +962,6 @@ void asd_dump_scb_list(struct asd_ascb *ascb, int num)
}
}
+#endif /* 0 */
+
#endif /* ASD_DEBUG */
diff --git a/drivers/scsi/aic94xx/aic94xx_dump.h b/drivers/scsi/aic94xx/aic94xx_dump.h
index 0c388e7da6b..191a753d42a 100644
--- a/drivers/scsi/aic94xx/aic94xx_dump.h
+++ b/drivers/scsi/aic94xx/aic94xx_dump.h
@@ -29,24 +29,15 @@
#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_ */
diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c
index 1d8c5e5f442..4df867e07b2 100644
--- a/drivers/scsi/aic94xx/aic94xx_hwi.c
+++ b/drivers/scsi/aic94xx/aic94xx_hwi.c
@@ -25,8 +25,10 @@
*/
#include <linux/pci.h>
+#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/firmware.h>
#include "aic94xx.h"
#include "aic94xx_reg.h"
@@ -38,16 +40,14 @@ u32 MBAR0_SWB_SIZE;
/* ---------- Initialization ---------- */
-static void asd_get_user_sas_addr(struct asd_ha_struct *asd_ha)
+static int asd_get_user_sas_addr(struct asd_ha_struct *asd_ha)
{
- extern char sas_addr_str[];
- /* If the user has specified a WWN it overrides other settings
- */
- if (sas_addr_str[0] != '\0')
- asd_destringify_sas_addr(asd_ha->hw_prof.sas_addr,
- sas_addr_str);
- else if (asd_ha->hw_prof.sas_addr[0] != 0)
- asd_stringify_sas_addr(sas_addr_str, asd_ha->hw_prof.sas_addr);
+ /* adapter came with a sas address */
+ if (asd_ha->hw_prof.sas_addr[0])
+ return 0;
+
+ return sas_request_addr(asd_ha->sas_ha.core.shost,
+ asd_ha->hw_prof.sas_addr);
}
static void asd_propagate_sas_addr(struct asd_ha_struct *asd_ha)
@@ -74,7 +74,7 @@ static void asd_init_phy_identify(struct asd_phy *phy)
memset(phy->identify_frame, 0, sizeof(*phy->identify_frame));
- phy->identify_frame->dev_type = SAS_END_DEV;
+ phy->identify_frame->dev_type = SAS_END_DEVICE;
if (phy->sas_phy.role & PHY_ROLE_INITIATOR)
phy->identify_frame->initiator_bits = phy->sas_phy.iproto;
if (phy->sas_phy.role & PHY_ROLE_TARGET)
@@ -91,7 +91,7 @@ static int asd_init_phy(struct asd_phy *phy)
sas_phy->enabled = 1;
sas_phy->class = SAS;
- sas_phy->iproto = SAS_PROTO_ALL;
+ sas_phy->iproto = SAS_PROTOCOL_ALL;
sas_phy->tproto = 0;
sas_phy->type = PHY_TYPE_PHYSICAL;
sas_phy->role = PHY_ROLE_INITIATOR;
@@ -112,6 +112,21 @@ static int asd_init_phy(struct asd_phy *phy)
return 0;
}
+static void asd_init_ports(struct asd_ha_struct *asd_ha)
+{
+ int i;
+
+ spin_lock_init(&asd_ha->asd_ports_lock);
+ for (i = 0; i < ASD_MAX_PHYS; i++) {
+ struct asd_port *asd_port = &asd_ha->asd_ports[i];
+
+ memset(asd_port->sas_addr, 0, SAS_ADDR_SIZE);
+ memset(asd_port->attached_sas_addr, 0, SAS_ADDR_SIZE);
+ asd_port->phy_mask = 0;
+ asd_port->num_phys = 0;
+ }
+}
+
static int asd_init_phys(struct asd_ha_struct *asd_ha)
{
u8 i;
@@ -121,6 +136,7 @@ static int asd_init_phys(struct asd_ha_struct *asd_ha)
struct asd_phy *phy = &asd_ha->phys[i];
phy->phy_desc = &asd_ha->hw_prof.phy_desc[i];
+ phy->asd_port = NULL;
phy->sas_phy.enabled = 0;
phy->sas_phy.id = i;
@@ -235,7 +251,7 @@ static int asd_init_scbs(struct asd_ha_struct *asd_ha)
return 0;
}
-static inline void asd_get_max_scb_ddb(struct asd_ha_struct *asd_ha)
+static void asd_get_max_scb_ddb(struct asd_ha_struct *asd_ha)
{
asd_ha->hw_prof.max_scbs = asd_get_cmdctx_size(asd_ha)/ASD_SCB_SIZE;
asd_ha->hw_prof.max_ddbs = asd_get_devctx_size(asd_ha)/ASD_DDB_SIZE;
@@ -641,8 +657,7 @@ int asd_init_hw(struct asd_ha_struct *asd_ha)
asd_init_ctxmem(asd_ha);
- asd_get_user_sas_addr(asd_ha);
- if (!asd_ha->hw_prof.sas_addr[0]) {
+ if (asd_get_user_sas_addr(asd_ha)) {
asd_printk("No SAS Address provided for %s\n",
pci_name(asd_ha->pcidev));
err = -ENODEV;
@@ -658,6 +673,8 @@ int asd_init_hw(struct asd_ha_struct *asd_ha)
goto Out;
}
+ asd_init_ports(asd_ha);
+
err = asd_init_scbs(asd_ha);
if (err) {
asd_printk("couldn't initialize scbs for %s\n",
@@ -755,7 +772,7 @@ static void asd_dl_tasklet_handler(unsigned long data)
* asd_process_donelist_isr -- schedule processing of done list entries
* @asd_ha: pointer to host adapter structure
*/
-static inline void asd_process_donelist_isr(struct asd_ha_struct *asd_ha)
+static void asd_process_donelist_isr(struct asd_ha_struct *asd_ha)
{
tasklet_schedule(&asd_ha->seq.dl_tasklet);
}
@@ -764,7 +781,7 @@ static inline void asd_process_donelist_isr(struct asd_ha_struct *asd_ha)
* asd_com_sas_isr -- process device communication interrupt (COMINT)
* @asd_ha: pointer to host adapter structure
*/
-static inline void asd_com_sas_isr(struct asd_ha_struct *asd_ha)
+static void asd_com_sas_isr(struct asd_ha_struct *asd_ha)
{
u32 comstat = asd_read_reg_dword(asd_ha, COMSTAT);
@@ -803,7 +820,7 @@ static inline void asd_com_sas_isr(struct asd_ha_struct *asd_ha)
asd_chip_reset(asd_ha);
}
-static inline void asd_arp2_err(struct asd_ha_struct *asd_ha, u32 dchstatus)
+static void asd_arp2_err(struct asd_ha_struct *asd_ha, u32 dchstatus)
{
static const char *halt_code[256] = {
"UNEXPECTED_INTERRUPT0",
@@ -890,7 +907,7 @@ static inline void asd_arp2_err(struct asd_ha_struct *asd_ha, u32 dchstatus)
* asd_dch_sas_isr -- process device channel interrupt (DEVINT)
* @asd_ha: pointer to host adapter structure
*/
-static inline void asd_dch_sas_isr(struct asd_ha_struct *asd_ha)
+static void asd_dch_sas_isr(struct asd_ha_struct *asd_ha)
{
u32 dchstatus = asd_read_reg_dword(asd_ha, DCHSTATUS);
@@ -905,7 +922,7 @@ static inline void asd_dch_sas_isr(struct asd_ha_struct *asd_ha)
* ads_rbi_exsi_isr -- process external system interface interrupt (INITERR)
* @asd_ha: pointer to host adapter structure
*/
-static inline void asd_rbi_exsi_isr(struct asd_ha_struct *asd_ha)
+static void asd_rbi_exsi_isr(struct asd_ha_struct *asd_ha)
{
u32 stat0r = asd_read_reg_dword(asd_ha, ASISTAT0R);
@@ -953,7 +970,7 @@ static inline void asd_rbi_exsi_isr(struct asd_ha_struct *asd_ha)
*
* Asserted on PCIX errors: target abort, etc.
*/
-static inline void asd_hst_pcix_isr(struct asd_ha_struct *asd_ha)
+static void asd_hst_pcix_isr(struct asd_ha_struct *asd_ha)
{
u16 status;
u32 pcix_status;
@@ -996,11 +1013,10 @@ static inline void asd_hst_pcix_isr(struct asd_ha_struct *asd_ha)
* asd_hw_isr -- host adapter interrupt service routine
* @irq: ignored
* @dev_id: pointer to host adapter structure
- * @regs: ignored
*
* The ISR processes done list entries and level 3 error handling.
*/
-irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs)
+irqreturn_t asd_hw_isr(int irq, void *dev_id)
{
struct asd_ha_struct *asd_ha = dev_id;
u32 chimint = asd_read_reg_dword(asd_ha, CHIMINT);
@@ -1027,18 +1043,17 @@ irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs)
/* ---------- SCB handling ---------- */
-static inline struct asd_ascb *asd_ascb_alloc(struct asd_ha_struct *asd_ha,
- gfp_t gfp_flags)
+static struct asd_ascb *asd_ascb_alloc(struct asd_ha_struct *asd_ha,
+ gfp_t gfp_flags)
{
- extern kmem_cache_t *asd_ascb_cache;
+ extern struct kmem_cache *asd_ascb_cache;
struct asd_seq_data *seq = &asd_ha->seq;
struct asd_ascb *ascb;
unsigned long flags;
- ascb = kmem_cache_alloc(asd_ascb_cache, gfp_flags);
+ ascb = kmem_cache_zalloc(asd_ascb_cache, gfp_flags);
if (ascb) {
- memset(ascb, 0, sizeof(*ascb));
ascb->dma_scb.size = sizeof(struct scb);
ascb->dma_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool,
gfp_flags,
@@ -1128,8 +1143,8 @@ struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct
*
* LOCKING: called with the pending list lock held.
*/
-static inline void asd_swap_head_scb(struct asd_ha_struct *asd_ha,
- struct asd_ascb *ascb)
+static void asd_swap_head_scb(struct asd_ha_struct *asd_ha,
+ struct asd_ascb *ascb)
{
struct asd_seq_data *seq = &asd_ha->seq;
struct asd_ascb *last = list_entry(ascb->list.prev,
@@ -1155,7 +1170,7 @@ static inline void asd_swap_head_scb(struct asd_ha_struct *asd_ha,
* intended to be called from asd_post_ascb_list(), just prior to
* posting the SCBs to the sequencer.
*/
-static inline void asd_start_scb_timers(struct list_head *list)
+static void asd_start_scb_timers(struct list_head *list)
{
struct asd_ascb *ascb;
list_for_each_entry(ascb, list, list) {
@@ -1345,7 +1360,7 @@ int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask)
struct asd_ascb *ascb_list;
if (!phy_mask) {
- asd_printk("%s called with phy_mask of 0!?\n", __FUNCTION__);
+ asd_printk("%s called with phy_mask of 0!?\n", __func__);
return 0;
}
diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.h b/drivers/scsi/aic94xx/aic94xx_hwi.h
index 8498144aa5e..8c1c28239e9 100644
--- a/drivers/scsi/aic94xx/aic94xx_hwi.h
+++ b/drivers/scsi/aic94xx/aic94xx_hwi.h
@@ -40,17 +40,6 @@
#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 */
@@ -83,6 +72,7 @@ struct flash_struct {
u8 manuf;
u8 dev_id;
u8 sec_prot;
+ u8 method;
u32 dir_offs;
};
@@ -150,7 +140,7 @@ struct asd_ascb {
/* internally generated command */
struct timer_list timer;
- struct completion completion;
+ struct completion *completion;
u8 tag_valid:1;
__be16 tag; /* error recovery only */
@@ -192,6 +182,16 @@ struct asd_seq_data {
struct asd_ascb **escb_arr; /* array of pointers to escbs */
};
+/* This is an internal port structure. These are used to get accurate
+ * phy_mask for updating DDB 0.
+ */
+struct asd_port {
+ u8 sas_addr[SAS_ADDR_SIZE];
+ u8 attached_sas_addr[SAS_ADDR_SIZE];
+ u32 phy_mask;
+ int num_phys;
+};
+
/* This is the Host Adapter structure. It describes the hardware
* SAS adapter.
*/
@@ -210,11 +210,15 @@ struct asd_ha_struct {
struct hw_profile hw_prof;
struct asd_phy phys[ASD_MAX_PHYS];
+ spinlock_t asd_ports_lock;
+ struct asd_port asd_ports[ASD_MAX_PHYS];
struct asd_sas_port ports[ASD_MAX_PHYS];
struct dma_pool *scb_pool;
struct asd_seq_data seq; /* sequencer related */
+ u32 bios_status;
+ const struct firmware *bios_image;
};
/* ---------- Common macros ---------- */
@@ -290,7 +294,6 @@ static inline void asd_init_ascb(struct asd_ha_struct *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!
@@ -371,7 +374,7 @@ static inline void asd_ascb_free_list(struct asd_ascb *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);
+irqreturn_t asd_hw_isr(int irq, void *dev_id);
struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct
@@ -388,8 +391,6 @@ 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);
diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c
index ee2ccad7048..c56741fc4b9 100644
--- a/drivers/scsi/aic94xx/aic94xx_init.c
+++ b/drivers/scsi/aic94xx/aic94xx_init.c
@@ -24,12 +24,13 @@
*
*/
-#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 <linux/firmware.h>
+#include <linux/slab.h>
#include <scsi/scsi_host.h>
@@ -37,9 +38,10 @@
#include "aic94xx_reg.h"
#include "aic94xx_hwi.h"
#include "aic94xx_seq.h"
+#include "aic94xx_sds.h"
/* The format is "version.release.patchlevel" */
-#define ASD_DRIVER_VERSION "1.0.2"
+#define ASD_DRIVER_VERSION "1.0.3"
static int use_msi = 0;
module_param_named(use_msi, use_msi, int, S_IRUGO);
@@ -55,9 +57,9 @@ MODULE_PARM_DESC(collector, "\n"
"\tThe aic94xx SAS LLDD supports both modes.\n"
"\tDefault: 0 (Direct Mode).\n");
-char sas_addr_str[2*SAS_ADDR_SIZE + 1] = "";
-
static struct scsi_transport_template *aic94xx_transport_template;
+static int asd_scan_finished(struct Scsi_Host *, unsigned long);
+static void asd_scan_start(struct Scsi_Host *);
static struct scsi_host_template aic94xx_sht = {
.module = THIS_MODULE,
@@ -66,7 +68,8 @@ static struct scsi_host_template aic94xx_sht = {
.queuecommand = sas_queuecommand,
.target_alloc = sas_target_alloc,
.slave_configure = sas_slave_configure,
- .slave_destroy = sas_slave_destroy,
+ .scan_finished = asd_scan_finished,
+ .scan_start = asd_scan_start,
.change_queue_depth = sas_change_queue_depth,
.change_queue_type = sas_change_queue_type,
.bios_param = sas_bios_param,
@@ -76,9 +79,13 @@ static struct scsi_host_template aic94xx_sht = {
.sg_tablesize = SG_ALL,
.max_sectors = SCSI_DEFAULT_MAX_SECTORS,
.use_clustering = ENABLE_CLUSTERING,
+ .eh_device_reset_handler = sas_eh_device_reset_handler,
+ .eh_bus_reset_handler = sas_eh_bus_reset_handler,
+ .target_destroy = sas_target_destroy,
+ .ioctl = sas_ioctl,
};
-static int __devinit asd_map_memio(struct asd_ha_struct *asd_ha)
+static int asd_map_memio(struct asd_ha_struct *asd_ha)
{
int err, i;
struct asd_ha_addrspace *io_handle;
@@ -126,7 +133,7 @@ Err:
return err;
}
-static void __devexit asd_unmap_memio(struct asd_ha_struct *asd_ha)
+static void asd_unmap_memio(struct asd_ha_struct *asd_ha)
{
struct asd_ha_addrspace *io_handle;
@@ -139,7 +146,7 @@ static void __devexit asd_unmap_memio(struct asd_ha_struct *asd_ha)
pci_release_region(asd_ha->pcidev, 0);
}
-static int __devinit asd_map_ioport(struct asd_ha_struct *asd_ha)
+static int 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];
@@ -163,12 +170,12 @@ static int __devinit asd_map_ioport(struct asd_ha_struct *asd_ha)
return err;
}
-static void __devexit asd_unmap_ioport(struct asd_ha_struct *asd_ha)
+static void 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)
+static int asd_map_ha(struct asd_ha_struct *asd_ha)
{
int err;
u16 cmd_reg;
@@ -200,7 +207,7 @@ Err:
return err;
}
-static void __devexit asd_unmap_ha(struct asd_ha_struct *asd_ha)
+static void asd_unmap_ha(struct asd_ha_struct *asd_ha)
{
if (asd_ha->iospace)
asd_unmap_ioport(asd_ha);
@@ -214,17 +221,12 @@ static const char *asd_dev_rev[30] = {
[8] = "B0",
};
-static int __devinit asd_common_setup(struct asd_ha_struct *asd_ha)
+static int 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;
- }
+ asd_ha->revision_id = asd_ha->pcidev->revision;
+
err = -ENODEV;
if (asd_ha->revision_id < AIC9410_DEV_REV_B0) {
asd_printk("%s is revision %s (%X), which is not supported\n",
@@ -235,7 +237,7 @@ static int __devinit asd_common_setup(struct asd_ha_struct *asd_ha)
}
/* Provide some sane default values. */
asd_ha->hw_prof.max_scbs = 512;
- asd_ha->hw_prof.max_ddbs = 128;
+ asd_ha->hw_prof.max_ddbs = ASD_MAX_DDBS;
asd_ha->hw_prof.num_phys = ASD_MAX_PHYS;
/* All phys are enabled, by default. */
asd_ha->hw_prof.enabled_phys = 0xFF;
@@ -255,7 +257,7 @@ Err:
return err;
}
-static int __devinit asd_aic9410_setup(struct asd_ha_struct *asd_ha)
+static int asd_aic9410_setup(struct asd_ha_struct *asd_ha)
{
int err = asd_common_setup(asd_ha);
@@ -270,7 +272,7 @@ static int __devinit asd_aic9410_setup(struct asd_ha_struct *asd_ha)
return 0;
}
-static int __devinit asd_aic9405_setup(struct asd_ha_struct *asd_ha)
+static int asd_aic9405_setup(struct asd_ha_struct *asd_ha)
{
int err = asd_common_setup(asd_ha);
@@ -310,11 +312,209 @@ static ssize_t asd_show_dev_pcba_sn(struct device *dev,
}
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)
+#define FLASH_CMD_NONE 0x00
+#define FLASH_CMD_UPDATE 0x01
+#define FLASH_CMD_VERIFY 0x02
+
+struct flash_command {
+ u8 command[8];
+ int code;
+};
+
+static struct flash_command flash_command_table[] =
+{
+ {"verify", FLASH_CMD_VERIFY},
+ {"update", FLASH_CMD_UPDATE},
+ {"", FLASH_CMD_NONE} /* Last entry should be NULL. */
+};
+
+struct error_bios {
+ char *reason;
+ int err_code;
+};
+
+static struct error_bios flash_error_table[] =
{
- 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);
+ {"Failed to open bios image file", FAIL_OPEN_BIOS_FILE},
+ {"PCI ID mismatch", FAIL_CHECK_PCI_ID},
+ {"Checksum mismatch", FAIL_CHECK_SUM},
+ {"Unknown Error", FAIL_UNKNOWN},
+ {"Failed to verify.", FAIL_VERIFY},
+ {"Failed to reset flash chip.", FAIL_RESET_FLASH},
+ {"Failed to find flash chip type.", FAIL_FIND_FLASH_ID},
+ {"Failed to erash flash chip.", FAIL_ERASE_FLASH},
+ {"Failed to program flash chip.", FAIL_WRITE_FLASH},
+ {"Flash in progress", FLASH_IN_PROGRESS},
+ {"Image file size Error", FAIL_FILE_SIZE},
+ {"Input parameter error", FAIL_PARAMETERS},
+ {"Out of memory", FAIL_OUT_MEMORY},
+ {"OK", 0} /* Last entry err_code = 0. */
+};
+
+static ssize_t asd_store_update_bios(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
+ char *cmd_ptr, *filename_ptr;
+ struct bios_file_header header, *hdr_ptr;
+ int res, i;
+ u32 csum = 0;
+ int flash_command = FLASH_CMD_NONE;
+ int err = 0;
+
+ cmd_ptr = kzalloc(count*2, GFP_KERNEL);
+
+ if (!cmd_ptr) {
+ err = FAIL_OUT_MEMORY;
+ goto out;
+ }
+
+ filename_ptr = cmd_ptr + count;
+ res = sscanf(buf, "%s %s", cmd_ptr, filename_ptr);
+ if (res != 2) {
+ err = FAIL_PARAMETERS;
+ goto out1;
+ }
+
+ for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) {
+ if (!memcmp(flash_command_table[i].command,
+ cmd_ptr, strlen(cmd_ptr))) {
+ flash_command = flash_command_table[i].code;
+ break;
+ }
+ }
+ if (flash_command == FLASH_CMD_NONE) {
+ err = FAIL_PARAMETERS;
+ goto out1;
+ }
+
+ if (asd_ha->bios_status == FLASH_IN_PROGRESS) {
+ err = FLASH_IN_PROGRESS;
+ goto out1;
+ }
+ err = request_firmware(&asd_ha->bios_image,
+ filename_ptr,
+ &asd_ha->pcidev->dev);
+ if (err) {
+ asd_printk("Failed to load bios image file %s, error %d\n",
+ filename_ptr, err);
+ err = FAIL_OPEN_BIOS_FILE;
+ goto out1;
+ }
+
+ hdr_ptr = (struct bios_file_header *)asd_ha->bios_image->data;
+
+ if ((hdr_ptr->contrl_id.vendor != asd_ha->pcidev->vendor ||
+ hdr_ptr->contrl_id.device != asd_ha->pcidev->device) &&
+ (hdr_ptr->contrl_id.sub_vendor != asd_ha->pcidev->vendor ||
+ hdr_ptr->contrl_id.sub_device != asd_ha->pcidev->device)) {
+
+ ASD_DPRINTK("The PCI vendor or device id does not match\n");
+ ASD_DPRINTK("vendor=%x dev=%x sub_vendor=%x sub_dev=%x"
+ " pci vendor=%x pci dev=%x\n",
+ hdr_ptr->contrl_id.vendor,
+ hdr_ptr->contrl_id.device,
+ hdr_ptr->contrl_id.sub_vendor,
+ hdr_ptr->contrl_id.sub_device,
+ asd_ha->pcidev->vendor,
+ asd_ha->pcidev->device);
+ err = FAIL_CHECK_PCI_ID;
+ goto out2;
+ }
+
+ if (hdr_ptr->filelen != asd_ha->bios_image->size) {
+ err = FAIL_FILE_SIZE;
+ goto out2;
+ }
+
+ /* calculate checksum */
+ for (i = 0; i < hdr_ptr->filelen; i++)
+ csum += asd_ha->bios_image->data[i];
+
+ if ((csum & 0x0000ffff) != hdr_ptr->checksum) {
+ ASD_DPRINTK("BIOS file checksum mismatch\n");
+ err = FAIL_CHECK_SUM;
+ goto out2;
+ }
+ if (flash_command == FLASH_CMD_UPDATE) {
+ asd_ha->bios_status = FLASH_IN_PROGRESS;
+ err = asd_write_flash_seg(asd_ha,
+ &asd_ha->bios_image->data[sizeof(*hdr_ptr)],
+ 0, hdr_ptr->filelen-sizeof(*hdr_ptr));
+ if (!err)
+ err = asd_verify_flash_seg(asd_ha,
+ &asd_ha->bios_image->data[sizeof(*hdr_ptr)],
+ 0, hdr_ptr->filelen-sizeof(*hdr_ptr));
+ } else {
+ asd_ha->bios_status = FLASH_IN_PROGRESS;
+ err = asd_verify_flash_seg(asd_ha,
+ &asd_ha->bios_image->data[sizeof(header)],
+ 0, hdr_ptr->filelen-sizeof(header));
+ }
+
+out2:
+ release_firmware(asd_ha->bios_image);
+out1:
+ kfree(cmd_ptr);
+out:
+ asd_ha->bios_status = err;
+
+ if (!err)
+ return count;
+ else
+ return -err;
+}
+
+static ssize_t asd_show_update_bios(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i;
+ struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
+
+ for (i = 0; flash_error_table[i].err_code != 0; i++) {
+ if (flash_error_table[i].err_code == asd_ha->bios_status)
+ break;
+ }
+ if (asd_ha->bios_status != FLASH_IN_PROGRESS)
+ asd_ha->bios_status = FLASH_OK;
+
+ return snprintf(buf, PAGE_SIZE, "status=%x %s\n",
+ flash_error_table[i].err_code,
+ flash_error_table[i].reason);
+}
+
+static DEVICE_ATTR(update_bios, S_IRUGO|S_IWUSR,
+ asd_show_update_bios, asd_store_update_bios);
+
+static int asd_create_dev_attrs(struct asd_ha_struct *asd_ha)
+{
+ int err;
+
+ err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+ if (err)
+ return err;
+
+ err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
+ if (err)
+ goto err_rev;
+
+ err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
+ if (err)
+ goto err_biosb;
+ err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_update_bios);
+ if (err)
+ goto err_update_bios;
+
+ return 0;
+
+err_update_bios:
+ device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
+err_biosb:
+ device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
+err_rev:
+ device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+ return err;
}
static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha)
@@ -322,12 +522,13 @@ 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);
+ device_remove_file(&asd_ha->pcidev->dev, &dev_attr_update_bios);
}
/* The first entry, 0, is used for dynamic ids, the rest for devices
* we know about.
*/
-static struct asd_pcidev_struct {
+static const struct asd_pcidev_struct {
const char * name;
int (*setup)(struct asd_ha_struct *asd_ha);
} asd_pcidev_data[] = {
@@ -343,7 +544,7 @@ static struct asd_pcidev_struct {
},
};
-static inline int asd_create_ha_caches(struct asd_ha_struct *asd_ha)
+static 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,
@@ -361,7 +562,7 @@ static inline int asd_create_ha_caches(struct asd_ha_struct *asd_ha)
* 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)
+static void asd_free_edbs(struct asd_ha_struct *asd_ha)
{
struct asd_seq_data *seq = &asd_ha->seq;
int i;
@@ -372,7 +573,7 @@ static inline void asd_free_edbs(struct asd_ha_struct *asd_ha)
seq->edb_arr = NULL;
}
-static inline void asd_free_escbs(struct asd_ha_struct *asd_ha)
+static void asd_free_escbs(struct asd_ha_struct *asd_ha)
{
struct asd_seq_data *seq = &asd_ha->seq;
int i;
@@ -387,7 +588,7 @@ static inline void asd_free_escbs(struct asd_ha_struct *asd_ha)
seq->escb_arr = NULL;
}
-static inline void asd_destroy_ha_caches(struct asd_ha_struct *asd_ha)
+static void asd_destroy_ha_caches(struct asd_ha_struct *asd_ha)
{
int i;
@@ -433,8 +634,8 @@ static inline void asd_destroy_ha_caches(struct asd_ha_struct *asd_ha)
asd_ha->scb_pool = NULL;
}
-kmem_cache_t *asd_dma_token_cache;
-kmem_cache_t *asd_ascb_cache;
+struct kmem_cache *asd_dma_token_cache;
+struct kmem_cache *asd_ascb_cache;
static int asd_create_global_caches(void)
{
@@ -444,7 +645,7 @@ static int asd_create_global_caches(void)
sizeof(struct asd_dma_tok),
0,
SLAB_HWCACHE_ALIGN,
- NULL, NULL);
+ NULL);
if (!asd_dma_token_cache) {
asd_printk("couldn't create dma token cache\n");
return -ENOMEM;
@@ -456,7 +657,7 @@ static int asd_create_global_caches(void)
sizeof(struct asd_ascb),
0,
SLAB_HWCACHE_ALIGN,
- NULL, NULL);
+ NULL);
if (!asd_ascb_cache) {
asd_printk("couldn't create ascb cache\n");
goto Err;
@@ -485,9 +686,9 @@ 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);
+ kcalloc(ASD_MAX_PHYS, sizeof(*sas_phys), GFP_KERNEL);
struct asd_sas_port **sas_ports =
- kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_port), GFP_KERNEL);
+ kcalloc(ASD_MAX_PHYS, sizeof(*sas_ports), GFP_KERNEL);
if (!sas_phys || !sas_ports) {
kfree(sas_phys);
@@ -509,6 +710,7 @@ static int asd_register_sas_ha(struct asd_ha_struct *asd_ha)
asd_ha->sas_ha.num_phys= ASD_MAX_PHYS;
asd_ha->sas_ha.lldd_queue_size = asd_ha->seq.can_queue;
+ asd_ha->sas_ha.lldd_max_execute_num = lldd_max_execute_num;
return sas_register_ha(&asd_ha->sas_ha);
}
@@ -529,10 +731,9 @@ static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha)
return err;
}
-static int __devinit asd_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
+static int asd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
- struct asd_pcidev_struct *asd_dev;
+ const struct asd_pcidev_struct *asd_dev;
unsigned asd_id = (unsigned) id->driver_data;
struct asd_ha_struct *asd_ha;
struct Scsi_Host *shost;
@@ -561,12 +762,13 @@ static int __devinit asd_pci_probe(struct pci_dev *dev,
asd_ha = kzalloc(sizeof(*asd_ha), GFP_KERNEL);
if (!asd_ha) {
asd_printk("out of memory\n");
- goto Err;
+ goto Err_put;
}
asd_ha->pcidev = dev;
- asd_ha->sas_ha.pcidev = asd_ha->pcidev;
+ asd_ha->sas_ha.dev = &asd_ha->pcidev->dev;
asd_ha->sas_ha.lldd_ha = asd_ha;
+ asd_ha->bios_status = FLASH_OK;
asd_ha->name = asd_dev->name;
asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev));
@@ -578,34 +780,30 @@ static int __devinit asd_pci_probe(struct pci_dev *dev,
shost->max_cmd_len = 16;
err = scsi_add_host(shost, &dev->dev);
- if (err) {
- scsi_host_put(shost);
+ if (err)
goto Err_free;
- }
-
-
err = asd_dev->setup(asd_ha);
if (err)
- goto Err_free;
+ goto Err_remove;
err = -ENODEV;
- if (!pci_set_dma_mask(dev, DMA_64BIT_MASK)
- && !pci_set_consistent_dma_mask(dev, DMA_64BIT_MASK))
+ if (!pci_set_dma_mask(dev, DMA_BIT_MASK(64))
+ && !pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(64)))
;
- else if (!pci_set_dma_mask(dev, DMA_32BIT_MASK)
- && !pci_set_consistent_dma_mask(dev, DMA_32BIT_MASK))
+ else if (!pci_set_dma_mask(dev, DMA_BIT_MASK(32))
+ && !pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(32)))
;
else {
asd_printk("no suitable DMA mask for %s\n", pci_name(dev));
- goto Err_free;
+ goto Err_remove;
}
pci_set_drvdata(dev, asd_ha);
err = asd_map_ha(asd_ha);
if (err)
- goto Err_free;
+ goto Err_remove;
err = asd_create_ha_caches(asd_ha);
if (err)
@@ -629,7 +827,7 @@ static int __devinit asd_pci_probe(struct pci_dev *dev,
if (use_msi)
pci_enable_msi(asd_ha->pcidev);
- err = request_irq(asd_ha->pcidev->irq, asd_hw_isr, SA_SHIRQ,
+ err = request_irq(asd_ha->pcidev->irq, asd_hw_isr, IRQF_SHARED,
ASD_DRIVER_NAME, asd_ha);
if (err) {
asd_printk("couldn't get irq %d for %s\n",
@@ -646,29 +844,21 @@ static int __devinit asd_pci_probe(struct pci_dev *dev,
}
ASD_DPRINTK("escbs posted\n");
- asd_create_dev_attrs(asd_ha);
+ err = asd_create_dev_attrs(asd_ha);
+ if (err)
+ goto Err_dev_attrs;
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);
+ scsi_scan_host(shost);
return 0;
-Err_en_phys:
- asd_unregister_sas_ha(asd_ha);
+
Err_reg_sas:
asd_remove_dev_attrs(asd_ha);
+Err_dev_attrs:
Err_escbs:
asd_disable_ints(asd_ha);
free_irq(dev->irq, asd_ha);
@@ -680,9 +870,12 @@ Err_free_cache:
asd_destroy_ha_caches(asd_ha);
Err_unmap:
asd_unmap_ha(asd_ha);
+Err_remove:
+ scsi_remove_host(shost);
Err_free:
kfree(asd_ha);
- scsi_remove_host(shost);
+Err_put:
+ scsi_host_put(shost);
Err:
pci_disable_device(dev);
return err;
@@ -704,6 +897,15 @@ static void asd_free_queues(struct asd_ha_struct *asd_ha)
list_for_each_safe(pos, n, &pending) {
struct asd_ascb *ascb = list_entry(pos, struct asd_ascb, list);
+ /*
+ * Delete unexpired ascb timers. This may happen if we issue
+ * a CONTROL PHY scb to an adapter and rmmod before the scb
+ * times out. Apparently we don't wait for the CONTROL PHY
+ * to complete, so it doesn't matter if we kill the timer.
+ */
+ del_timer_sync(&ascb->timer);
+ WARN_ON(ascb->scb->header.opcode != CONTROL_PHY);
+
list_del_init(pos);
ASD_DPRINTK("freeing from pending\n");
asd_ascb_free(ascb);
@@ -721,7 +923,7 @@ static void asd_turn_off_leds(struct asd_ha_struct *asd_ha)
}
}
-static void __devexit asd_pci_remove(struct pci_dev *dev)
+static void asd_pci_remove(struct pci_dev *dev)
{
struct asd_ha_struct *asd_ha = pci_get_drvdata(dev);
@@ -749,15 +951,37 @@ static void __devexit asd_pci_remove(struct pci_dev *dev)
return;
}
+static void asd_scan_start(struct Scsi_Host *shost)
+{
+ struct asd_ha_struct *asd_ha;
+ int err;
+
+ asd_ha = SHOST_TO_SAS_HA(shost)->lldd_ha;
+ err = asd_enable_phys(asd_ha, asd_ha->hw_prof.enabled_phys);
+ if (err)
+ asd_printk("Couldn't enable phys, err:%d\n", err);
+}
+
+static int asd_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+ /* give the phy enabling interrupt event time to come in (1s
+ * is empirically about all it takes) */
+ if (time < HZ)
+ return 0;
+ /* Wait for discovery to finish */
+ sas_drain_work(SHOST_TO_SAS_HA(shost));
+ return 1;
+}
+
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)
+static int asd_create_driver_attrs(struct device_driver *driver)
{
- driver_create_file(driver, &driver_attr_version);
+ return driver_create_file(driver, &driver_attr_version);
}
static void asd_remove_driver_attrs(struct device_driver *driver)
@@ -766,8 +990,6 @@ static void asd_remove_driver_attrs(struct device_driver *driver)
}
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,
@@ -777,7 +999,7 @@ static struct sas_domain_function_template aic94xx_transport_functions = {
.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_I_T_nexus_reset = asd_I_T_nexus_reset,
.lldd_lu_reset = asd_lu_reset,
.lldd_query_task = asd_query_task,
@@ -785,23 +1007,20 @@ static struct sas_domain_function_template aic94xx_transport_functions = {
.lldd_clear_nexus_ha = asd_clear_nexus_ha,
.lldd_control_phy = asd_control_phy,
+
+ .lldd_ata_set_dmamode = asd_set_dmamode,
};
-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},
+static const struct pci_device_id aic94xx_pci_table[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x410),0, 0, 1},
+ {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x412),0, 0, 1},
+ {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x416),0, 0, 1},
+ {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x41E),0, 0, 1},
+ {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x41F),0, 0, 1},
+ {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x430),0, 0, 2},
+ {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x432),0, 0, 2},
+ {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x43E),0, 0, 2},
+ {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x43F),0, 0, 2},
{}
};
@@ -811,7 +1030,7 @@ 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),
+ .remove = asd_pci_remove,
};
static int __init aic94xx_init(void)
@@ -835,10 +1054,14 @@ static int __init aic94xx_init(void)
if (err)
goto out_release_transport;
- asd_create_driver_attrs(&aic94xx_pci_driver.driver);
+ err = asd_create_driver_attrs(&aic94xx_pci_driver.driver);
+ if (err)
+ goto out_unregister_pcidrv;
return err;
+ out_unregister_pcidrv:
+ pci_unregister_driver(&aic94xx_pci_driver);
out_release_transport:
sas_release_transport(aic94xx_transport_template);
out_destroy_caches:
@@ -852,6 +1075,7 @@ 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_release_firmware();
asd_destroy_global_caches();
asd_printk("%s version %s unloaded\n", ASD_DRIVER_DESCRIPTION,
ASD_DRIVER_VERSION);
diff --git a/drivers/scsi/aic94xx/aic94xx_reg.c b/drivers/scsi/aic94xx/aic94xx_reg.c
index f210dac3203..56b17c22526 100644
--- a/drivers/scsi/aic94xx/aic94xx_reg.c
+++ b/drivers/scsi/aic94xx/aic94xx_reg.c
@@ -32,8 +32,8 @@
* 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)
+static void asd_write_byte(struct asd_ha_struct *asd_ha,
+ unsigned long offs, u8 val)
{
if (unlikely(asd_ha->iospace))
outb(val,
@@ -43,8 +43,8 @@ static inline void asd_write_byte(struct asd_ha_struct *asd_ha,
wmb();
}
-static inline void asd_write_word(struct asd_ha_struct *asd_ha,
- unsigned long offs, u16 val)
+static void asd_write_word(struct asd_ha_struct *asd_ha,
+ unsigned long offs, u16 val)
{
if (unlikely(asd_ha->iospace))
outw(val,
@@ -54,8 +54,8 @@ static inline void asd_write_word(struct asd_ha_struct *asd_ha,
wmb();
}
-static inline void asd_write_dword(struct asd_ha_struct *asd_ha,
- unsigned long offs, u32 val)
+static void asd_write_dword(struct asd_ha_struct *asd_ha,
+ unsigned long offs, u32 val)
{
if (unlikely(asd_ha->iospace))
outl(val,
@@ -67,8 +67,7 @@ static inline void asd_write_dword(struct asd_ha_struct *asd_ha,
/* Reading from device address space.
*/
-static inline u8 asd_read_byte(struct asd_ha_struct *asd_ha,
- unsigned long offs)
+static u8 asd_read_byte(struct asd_ha_struct *asd_ha, unsigned long offs)
{
u8 val;
if (unlikely(asd_ha->iospace))
@@ -80,8 +79,8 @@ static inline u8 asd_read_byte(struct asd_ha_struct *asd_ha,
return val;
}
-static inline u16 asd_read_word(struct asd_ha_struct *asd_ha,
- unsigned long offs)
+static u16 asd_read_word(struct asd_ha_struct *asd_ha,
+ unsigned long offs)
{
u16 val;
if (unlikely(asd_ha->iospace))
@@ -93,8 +92,8 @@ static inline u16 asd_read_word(struct asd_ha_struct *asd_ha,
return val;
}
-static inline u32 asd_read_dword(struct asd_ha_struct *asd_ha,
- unsigned long offs)
+static u32 asd_read_dword(struct asd_ha_struct *asd_ha,
+ unsigned long offs)
{
u32 val;
if (unlikely(asd_ha->iospace))
@@ -124,22 +123,22 @@ static inline u32 asd_mem_offs_swb(void)
/* 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_READ_SW(ww, type, ord) \
+static 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); \
+#define ASD_WRITE_SW(ww, type, ord) \
+static 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);
@@ -186,7 +185,7 @@ ASD_WRITE_SW(swc, u32, dword);
* @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)
+static 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);
diff --git a/drivers/scsi/aic94xx/aic94xx_reg_def.h b/drivers/scsi/aic94xx/aic94xx_reg_def.h
index b79f45f3ad4..dd6cc8008b1 100644
--- a/drivers/scsi/aic94xx/aic94xx_reg_def.h
+++ b/drivers/scsi/aic94xx/aic94xx_reg_def.h
@@ -1,5 +1,5 @@
/*
- * Aic94xx SAS/SATA driver hardware registers defintions.
+ * Aic94xx SAS/SATA driver hardware registers definitions.
*
* Copyright (C) 2004 Adaptec, Inc. All rights reserved.
* Copyright (C) 2004 David Chaw <david_chaw@adaptec.com>
@@ -1689,7 +1689,7 @@
#define PHY_START_CAL 0x01
/*
- * HST_PCIX2 Registers, Addresss Range: (0x00-0xFC)
+ * HST_PCIX2 Registers, Address Range: (0x00-0xFC)
*/
#define PCIX_REG_BASE_ADR 0xB8040000
@@ -1802,7 +1802,7 @@
#define PCIC_TP_CTRL 0xFC
/*
- * EXSI Registers, Addresss Range: (0x00-0xFC)
+ * EXSI Registers, Address Range: (0x00-0xFC)
*/
#define EXSI_REG_BASE_ADR REG_BASE_ADDR_EXSI
@@ -2000,7 +2000,7 @@
* The host accesses this scratch in a different manner from the
* central sequencer. The sequencer has to use CSEQ registers CSCRPAGE
* and CMnSCRPAGE to access the scratch memory. A flat mapping of the
- * scratch memory is avaliable for software convenience and to prevent
+ * scratch memory is available for software convenience and to prevent
* corruption while the sequencer is running. This memory is mapped
* onto addresses 800h - BFFh, total of 400h bytes.
*
@@ -2134,7 +2134,7 @@
* The host accesses this scratch in a different manner from the
* link sequencer. The sequencer has to use LSEQ registers
* LmSCRPAGE and LmMnSCRPAGE to access the scratch memory. A flat
-* mapping of the scratch memory is avaliable for software
+* mapping of the scratch memory is available for software
* convenience and to prevent corruption while the sequencer is
* running. This memory is mapped onto addresses 800h - 9FFh.
*
@@ -2226,9 +2226,10 @@
#define LmSEQ_SAS_RESET_MODE(LinkNum) (LmSCRATCH(LinkNum) + 0x0074)
#define LmSEQ_LINK_RESET_RETRY_COUNT(LinkNum) (LmSCRATCH(LinkNum) + 0x0075)
#define LmSEQ_NUM_LINK_RESET_RETRIES(LinkNum) (LmSCRATCH(LinkNum) + 0x0076)
-#define LmSEQ_OOB_INT_ENABLES(LinkNum) (LmSCRATCH(LinkNum) + 0x007A)
+#define LmSEQ_OOB_INT_ENABLES(LinkNum) (LmSCRATCH(LinkNum) + 0x0078)
+#define LmSEQ_NOTIFY_TIMER_DOWN_COUNT(LinkNum) (LmSCRATCH(LinkNum) + 0x007A)
#define LmSEQ_NOTIFY_TIMER_TIMEOUT(LinkNum) (LmSCRATCH(LinkNum) + 0x007C)
-#define LmSEQ_NOTIFY_TIMER_DOWN_COUNT(LinkNum) (LmSCRATCH(LinkNum) + 0x007E)
+#define LmSEQ_NOTIFY_TIMER_INITIAL_COUNT(LinkNum) (LmSCRATCH(LinkNum) + 0x007E)
/* Mode dependent scratch page 1, mode 0 and mode 1 */
#define LmSEQ_SG_LIST_PTR_ADDR0(LinkNum) (LmSCRATCH(LinkNum) + 0x0020)
diff --git a/drivers/scsi/aic94xx/aic94xx_sas.h b/drivers/scsi/aic94xx/aic94xx_sas.h
index 64d23171234..912e6b755f7 100644
--- a/drivers/scsi/aic94xx/aic94xx_sas.h
+++ b/drivers/scsi/aic94xx/aic94xx_sas.h
@@ -34,6 +34,7 @@
* domain that this sequencer can maintain low-level connections for
* us. They are be 64 bytes.
*/
+#define ASD_MAX_DDBS 128
struct asd_ddb_ssp_smp_target_port {
u8 conn_type; /* byte 0 */
@@ -291,7 +292,7 @@ struct scb_header {
#define INITIATE_SSP_TASK 0x00
#define INITIATE_LONG_SSP_TASK 0x01
#define INITIATE_BIDIR_SSP_TASK 0x02
-#define ABORT_TASK 0x03
+#define SCB_ABORT_TASK 0x03
#define INITIATE_SSP_TMF 0x04
#define SSP_TARG_GET_DATA 0x05
#define SSP_TARG_GET_DATA_GOOD 0x06
@@ -733,6 +734,7 @@ struct asd_phy {
struct sas_identify_frame *identify_frame;
struct asd_dma_tok *id_frm_tok;
+ struct asd_port *asd_port;
u8 frame_rcvd[ASD_EDB_SIZE];
};
diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c
index 7ee49b51b72..fdac7c2fef3 100644
--- a/drivers/scsi/aic94xx/aic94xx_scb.c
+++ b/drivers/scsi/aic94xx/aic94xx_scb.c
@@ -24,7 +24,8 @@
*
*/
-#include <linux/pci.h>
+#include <linux/gfp.h>
+#include <scsi/scsi_host.h>
#include "aic94xx.h"
#include "aic94xx_reg.h"
@@ -50,7 +51,7 @@
| CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \
| CURRENT_OOB_ERROR)
-static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode)
+static void get_lrate_mode(struct asd_phy *phy, u8 oob_mode)
{
struct sas_phy *sas_phy = phy->sas_phy.phy;
@@ -81,7 +82,7 @@ static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode)
phy->sas_phy.oob_mode = SATA_OOB_MODE;
}
-static inline void asd_phy_event_tasklet(struct asd_ascb *ascb,
+static void asd_phy_event_tasklet(struct asd_ascb *ascb,
struct done_list_struct *dl)
{
struct asd_ha_struct *asd_ha = ascb->ha;
@@ -125,8 +126,7 @@ static inline void asd_phy_event_tasklet(struct asd_ascb *ascb,
}
/* 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)
+static 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;
@@ -151,7 +151,7 @@ static inline unsigned ord_phy(struct asd_ha_struct *asd_ha,
* 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)
+static 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) {
@@ -168,9 +168,73 @@ static inline void asd_get_attached_sas_addr(struct asd_phy *phy, u8 *sas_addr)
}
}
-static inline void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb,
- struct done_list_struct *dl,
- int edb_id, int phy_id)
+static void asd_form_port(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
+{
+ int i;
+ struct asd_port *free_port = NULL;
+ struct asd_port *port;
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ unsigned long flags;
+
+ spin_lock_irqsave(&asd_ha->asd_ports_lock, flags);
+ if (!phy->asd_port) {
+ for (i = 0; i < ASD_MAX_PHYS; i++) {
+ port = &asd_ha->asd_ports[i];
+
+ /* Check for wide port */
+ if (port->num_phys > 0 &&
+ memcmp(port->sas_addr, sas_phy->sas_addr,
+ SAS_ADDR_SIZE) == 0 &&
+ memcmp(port->attached_sas_addr,
+ sas_phy->attached_sas_addr,
+ SAS_ADDR_SIZE) == 0) {
+ break;
+ }
+
+ /* Find a free port */
+ if (port->num_phys == 0 && free_port == NULL) {
+ free_port = port;
+ }
+ }
+
+ /* Use a free port if this doesn't form a wide port */
+ if (i >= ASD_MAX_PHYS) {
+ port = free_port;
+ BUG_ON(!port);
+ memcpy(port->sas_addr, sas_phy->sas_addr,
+ SAS_ADDR_SIZE);
+ memcpy(port->attached_sas_addr,
+ sas_phy->attached_sas_addr,
+ SAS_ADDR_SIZE);
+ }
+ port->num_phys++;
+ port->phy_mask |= (1U << sas_phy->id);
+ phy->asd_port = port;
+ }
+ ASD_DPRINTK("%s: updating phy_mask 0x%x for phy%d\n",
+ __func__, phy->asd_port->phy_mask, sas_phy->id);
+ asd_update_port_links(asd_ha, phy);
+ spin_unlock_irqrestore(&asd_ha->asd_ports_lock, flags);
+}
+
+static void asd_deform_port(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
+{
+ struct asd_port *port = phy->asd_port;
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ unsigned long flags;
+
+ spin_lock_irqsave(&asd_ha->asd_ports_lock, flags);
+ if (port) {
+ port->num_phys--;
+ port->phy_mask &= ~(1U << sas_phy->id);
+ phy->asd_port = NULL;
+ }
+ spin_unlock_irqrestore(&asd_ha->asd_ports_lock, flags);
+}
+
+static 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;
@@ -187,16 +251,18 @@ static inline void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb,
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);
+ asd_form_port(ascb->ha, phy);
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)
+static 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];
+ struct asd_phy *phy = &asd_ha->phys[phy_id];
u8 lr_error = dl->status_block[1];
u8 retries_left = dl->status_block[2];
@@ -221,6 +287,7 @@ static inline void asd_link_reset_err_tasklet(struct asd_ascb *ascb,
asd_turn_led(asd_ha, phy_id, 0);
sas_phy_disconnected(sas_phy);
+ asd_deform_port(asd_ha, phy);
sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
if (retries_left == 0) {
@@ -228,7 +295,7 @@ static inline void asd_link_reset_err_tasklet(struct asd_ascb *ascb,
struct asd_ascb *cp = asd_ascb_alloc_list(ascb->ha, &num,
GFP_ATOMIC);
if (!cp) {
- asd_printk("%s: out of memory\n", __FUNCTION__);
+ asd_printk("%s: out of memory\n", __func__);
goto out;
}
ASD_DPRINTK("phy%d: retries:0 performing link reset seq\n",
@@ -241,13 +308,15 @@ out:
;
}
-static inline void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb,
- struct done_list_struct *dl,
- int phy_id)
+static 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];
+ struct asd_ha_struct *asd_ha = ascb->ha;
+ struct asd_phy *phy = &asd_ha->phys[phy_id];
u8 reg = dl->status_block[1];
u32 cont = dl->status_block[2] << ((reg & 3)*8);
@@ -284,6 +353,7 @@ static inline void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb,
phy_id);
/* The sequencer disables all phys on that port.
* We have to re-enable the phys ourselves. */
+ asd_deform_port(asd_ha, phy);
sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET);
break;
@@ -351,6 +421,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
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];
+ struct asd_phy *phy = &asd_ha->phys[phy_id];
if (edb > 6 || edb < 0) {
ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n",
@@ -368,53 +439,156 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
ascb->scb->header.opcode);
}
+ /* Catch these before we mask off the sb_opcode bits */
+ switch (sb_opcode) {
+ case REQ_TASK_ABORT: {
+ struct asd_ascb *a, *b;
+ u16 tc_abort;
+ struct domain_device *failed_dev = NULL;
+
+ ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n",
+ __func__, dl->status_block[3]);
+
+ /*
+ * Find the task that caused the abort and abort it first.
+ * The sequencer won't put anything on the done list until
+ * that happens.
+ */
+ tc_abort = *((u16*)(&dl->status_block[1]));
+ tc_abort = le16_to_cpu(tc_abort);
+
+ list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) {
+ struct sas_task *task = a->uldd_task;
+
+ if (a->tc_index != tc_abort)
+ continue;
+
+ if (task) {
+ failed_dev = task->dev;
+ sas_task_abort(task);
+ } else {
+ ASD_DPRINTK("R_T_A for non TASK scb 0x%x\n",
+ a->scb->header.opcode);
+ }
+ break;
+ }
+
+ if (!failed_dev) {
+ ASD_DPRINTK("%s: Can't find task (tc=%d) to abort!\n",
+ __func__, tc_abort);
+ goto out;
+ }
+
+ /*
+ * Now abort everything else for that device (hba?) so
+ * that the EH will wake up and do something.
+ */
+ list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) {
+ struct sas_task *task = a->uldd_task;
+
+ if (task &&
+ task->dev == failed_dev &&
+ a->tc_index != tc_abort)
+ sas_task_abort(task);
+ }
+
+ goto out;
+ }
+ case REQ_DEVICE_RESET: {
+ struct asd_ascb *a;
+ u16 conn_handle;
+ unsigned long flags;
+ struct sas_task *last_dev_task = NULL;
+
+ conn_handle = *((u16*)(&dl->status_block[1]));
+ conn_handle = le16_to_cpu(conn_handle);
+
+ ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __func__,
+ dl->status_block[3]);
+
+ /* Find the last pending task for the device... */
+ list_for_each_entry(a, &asd_ha->seq.pend_q, list) {
+ u16 x;
+ struct domain_device *dev;
+ struct sas_task *task = a->uldd_task;
+
+ if (!task)
+ continue;
+ dev = task->dev;
+
+ x = (unsigned long)dev->lldd_dev;
+ if (x == conn_handle)
+ last_dev_task = task;
+ }
+
+ if (!last_dev_task) {
+ ASD_DPRINTK("%s: Device reset for idle device %d?\n",
+ __func__, conn_handle);
+ goto out;
+ }
+
+ /* ...and set the reset flag */
+ spin_lock_irqsave(&last_dev_task->task_state_lock, flags);
+ last_dev_task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
+ spin_unlock_irqrestore(&last_dev_task->task_state_lock, flags);
+
+ /* Kill all pending tasks for the device */
+ list_for_each_entry(a, &asd_ha->seq.pend_q, list) {
+ u16 x;
+ struct domain_device *dev;
+ struct sas_task *task = a->uldd_task;
+
+ if (!task)
+ continue;
+ dev = task->dev;
+
+ x = (unsigned long)dev->lldd_dev;
+ if (x == conn_handle)
+ sas_task_abort(task);
+ }
+
+ goto out;
+ }
+ case SIGNAL_NCQ_ERROR:
+ ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __func__);
+ goto out;
+ case CLEAR_NCQ_ERROR:
+ ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __func__);
+ goto out;
+ }
+
sb_opcode &= ~DL_PHY_MASK;
switch (sb_opcode) {
case BYTES_DMAED:
- ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __FUNCTION__, phy_id);
+ ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __func__, phy_id);
asd_bytes_dmaed_tasklet(ascb, dl, edb, phy_id);
break;
case PRIMITIVE_RECVD:
- ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __FUNCTION__,
+ ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __func__,
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_DPRINTK("%s: phy%d: PHY_EVENT\n", __func__, phy_id);
asd_phy_event_tasklet(ascb, dl);
break;
case LINK_RESET_ERROR:
- ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __FUNCTION__,
+ ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __func__,
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);
+ __func__, phy_id);
asd_turn_led(asd_ha, phy_id, 0);
/* the device is gone */
sas_phy_disconnected(sas_phy);
+ asd_deform_port(asd_ha, 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__,
+ ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __func__,
phy_id, sb_opcode);
ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n",
edb, dl->opcode);
@@ -432,7 +606,7 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
break;
}
-
+out:
asd_invalidate_edb(ascb, edb);
}
@@ -481,7 +655,7 @@ static void control_phy_tasklet_complete(struct asd_ascb *ascb,
if (status != 0) {
ASD_DPRINTK("%s: phy%d status block opcode:0x%x\n",
- __FUNCTION__, phy_id, status);
+ __func__, phy_id, status);
goto out;
}
@@ -490,7 +664,7 @@ static void control_phy_tasklet_complete(struct asd_ascb *ascb,
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);
+ ASD_DPRINTK("%s: disable phy%d\n", __func__, phy_id);
break;
case ENABLE_PHY:
@@ -500,40 +674,40 @@ static void control_phy_tasklet_complete(struct asd_ascb *ascb,
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,
+ __func__, 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__,
+ ASD_DPRINTK("%s: phy%d, spinup hold\n", __func__,
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);
+ __func__, 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);
+ __func__, 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);
+ __func__, 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__,
+ ASD_DPRINTK("%s: phy%d: sub_func:0x%x\n", __func__,
phy_id, control_phy->sub_func);
/* XXX finish */
break;
default:
- ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __FUNCTION__,
+ ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __func__,
phy_id, control_phy->sub_func);
break;
}
@@ -541,7 +715,7 @@ out:
asd_ascb_free(ascb);
}
-static inline void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd)
+static 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
@@ -620,12 +794,12 @@ void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc)
/* initiator port settings are in the hi nibble */
if (phy->sas_phy.role == PHY_ROLE_INITIATOR)
- control_phy->port_type = SAS_PROTO_ALL << 4;
+ control_phy->port_type = SAS_PROTOCOL_ALL << 4;
else if (phy->sas_phy.role == PHY_ROLE_TARGET)
- control_phy->port_type = SAS_PROTO_ALL;
+ control_phy->port_type = SAS_PROTOCOL_ALL;
else
control_phy->port_type =
- (SAS_PROTO_ALL << 4) | SAS_PROTO_ALL;
+ (SAS_PROTOCOL_ALL << 4) | SAS_PROTOCOL_ALL;
/* link reset retries, this should be nominal */
control_phy->link_reset_retries = 10;
@@ -646,6 +820,8 @@ void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc)
/* ---------- INITIATE LINK ADM TASK ---------- */
+#if 0
+
static void link_adm_tasklet_complete(struct asd_ascb *ascb,
struct done_list_struct *dl)
{
@@ -678,6 +854,8 @@ void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
ascb->tasklet_complete = link_adm_tasklet_complete;
}
+#endif /* 0 */
+
/* ---------- SCB timer ---------- */
/**
@@ -689,7 +867,7 @@ void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
* 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.
+ * function. The timer is started by the post function.
*/
void asd_ascb_timedout(unsigned long data)
{
@@ -728,6 +906,7 @@ int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg)
switch (func) {
case PHY_FUNC_CLEAR_ERROR_LOG:
+ case PHY_FUNC_GET_EVENTS:
return -ENOSYS;
case PHY_FUNC_SET_LINK_RATE:
rates = arg;
diff --git a/drivers/scsi/aic94xx/aic94xx_sds.c b/drivers/scsi/aic94xx/aic94xx_sds.c
index 83574b5b4e6..edb43fda9f3 100644
--- a/drivers/scsi/aic94xx/aic94xx_sds.c
+++ b/drivers/scsi/aic94xx/aic94xx_sds.c
@@ -26,10 +26,12 @@
*/
#include <linux/pci.h>
+#include <linux/slab.h>
#include <linux/delay.h>
#include "aic94xx.h"
#include "aic94xx_reg.h"
+#include "aic94xx_sds.h"
/* ---------- OCM stuff ---------- */
@@ -64,7 +66,7 @@ struct asd_ocm_dir {
#define OCM_INIT_DIR_ENTRIES 5
/***************************************************************************
-* OCM dircetory default
+* OCM directory default
***************************************************************************/
static struct asd_ocm_dir OCMDirInit =
{
@@ -73,7 +75,7 @@ static struct asd_ocm_dir OCMDirInit =
};
/***************************************************************************
-* OCM dircetory Entries default
+* OCM directory Entries default
***************************************************************************/
static struct asd_ocm_dir_ent OCMDirEntriesInit[OCM_INIT_DIR_ENTRIES] =
{
@@ -377,7 +379,7 @@ out:
#define FLASH_RESET 0xF0
-#define FLASH_SIZE 0x200000
+#define ASD_FLASH_SIZE 0x200000
#define FLASH_DIR_COOKIE "*** ADAPTEC FLASH DIRECTORY *** "
#define FLASH_NEXT_ENTRY_OFFS 0x2000
#define FLASH_MAX_DIR_ENTRIES 32
@@ -427,7 +429,7 @@ struct asd_manuf_sec {
struct asd_manuf_phy_desc {
u8 state; /* low 4 bits */
-#define MS_PHY_STATE_ENABLEABLE 0
+#define MS_PHY_STATE_ENABLED 0
#define MS_PHY_STATE_REPORTED 1
#define MS_PHY_STATE_HIDDEN 2
u8 phy_id;
@@ -589,8 +591,8 @@ static int asd_reset_flash(struct asd_ha_struct *asd_ha)
return err;
}
-static inline int asd_read_flash_seg(struct asd_ha_struct *asd_ha,
- void *buffer, u32 offs, int size)
+static int asd_read_flash_seg(struct asd_ha_struct *asd_ha,
+ void *buffer, u32 offs, int size)
{
asd_read_reg_string(asd_ha, buffer, asd_ha->hw_prof.flash.bar+offs,
size);
@@ -609,7 +611,7 @@ static int asd_find_flash_dir(struct asd_ha_struct *asd_ha,
struct asd_flash_dir *flash_dir)
{
u32 v;
- for (v = 0; v < FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) {
+ for (v = 0; v < ASD_FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) {
asd_read_flash_seg(asd_ha, flash_dir, v,
sizeof(FLASH_DIR_COOKIE)-1);
if (memcmp(flash_dir->cookie, FLASH_DIR_COOKIE,
@@ -630,10 +632,6 @@ static int asd_flash_getid(struct asd_ha_struct *asd_ha)
reg = asd_read_reg_dword(asd_ha, EXSICNFGR);
- if (!(reg & FLASHEX)) {
- ASD_DPRINTK("flash doesn't exist\n");
- return -ENOENT;
- }
if (pci_read_config_dword(asd_ha->pcidev, PCI_CONF_FLSH_BAR,
&asd_ha->hw_prof.flash.bar)) {
asd_printk("couldn't read PCI_CONF_FLSH_BAR of %s\n",
@@ -760,11 +758,11 @@ static void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1)
*
* HIDDEN phys do not count in the total count. REPORTED phys cannot
* be enabled but are reported and counted towards the total.
- * ENEBLEABLE phys are enabled by default and count towards the total.
+ * ENABLED phys are enabled by default and count towards the total.
* The absolute total phy number is ASD_MAX_PHYS. hw_prof->num_phys
* merely specifies the number of phys the host adapter decided to
* report. E.g., it is possible for phys 0, 1 and 2 to be HIDDEN,
- * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENEBLEABLE.
+ * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENABLED.
* In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2
* are actually enabled (enabled by default, max number of phys
* enableable in this case).
@@ -820,8 +818,8 @@ static int asd_ms_get_phy_params(struct asd_ha_struct *asd_ha,
asd_ha->hw_prof.enabled_phys &= ~(1 << i);
rep_phys++;
continue;
- case MS_PHY_STATE_ENABLEABLE:
- ASD_DPRINTK("ms: phy%d: ENEBLEABLE\n", i);
+ case MS_PHY_STATE_ENABLED:
+ ASD_DPRINTK("ms: phy%d: ENABLED\n", i);
asd_ha->hw_prof.enabled_phys |= (1 << i);
en_phys++;
break;
@@ -1087,3 +1085,391 @@ out:
kfree(flash_dir);
return err;
}
+
+/**
+ * asd_verify_flash_seg - verify data with flash memory
+ * @asd_ha: pointer to the host adapter structure
+ * @src: pointer to the source data to be verified
+ * @dest_offset: offset from flash memory
+ * @bytes_to_verify: total bytes to verify
+ */
+int asd_verify_flash_seg(struct asd_ha_struct *asd_ha,
+ const void *src, u32 dest_offset, u32 bytes_to_verify)
+{
+ const u8 *src_buf;
+ u8 flash_char;
+ int err;
+ u32 nv_offset, reg, i;
+
+ reg = asd_ha->hw_prof.flash.bar;
+ src_buf = NULL;
+
+ err = FLASH_OK;
+ nv_offset = dest_offset;
+ src_buf = (const u8 *)src;
+ for (i = 0; i < bytes_to_verify; i++) {
+ flash_char = asd_read_reg_byte(asd_ha, reg + nv_offset + i);
+ if (flash_char != src_buf[i]) {
+ err = FAIL_VERIFY;
+ break;
+ }
+ }
+ return err;
+}
+
+/**
+ * asd_write_flash_seg - write data into flash memory
+ * @asd_ha: pointer to the host adapter structure
+ * @src: pointer to the source data to be written
+ * @dest_offset: offset from flash memory
+ * @bytes_to_write: total bytes to write
+ */
+int asd_write_flash_seg(struct asd_ha_struct *asd_ha,
+ const void *src, u32 dest_offset, u32 bytes_to_write)
+{
+ const u8 *src_buf;
+ u32 nv_offset, reg, i;
+ int err;
+
+ reg = asd_ha->hw_prof.flash.bar;
+ src_buf = NULL;
+
+ err = asd_check_flash_type(asd_ha);
+ if (err) {
+ ASD_DPRINTK("couldn't find the type of flash. err=%d\n", err);
+ return err;
+ }
+
+ nv_offset = dest_offset;
+ err = asd_erase_nv_sector(asd_ha, nv_offset, bytes_to_write);
+ if (err) {
+ ASD_DPRINTK("Erase failed at offset:0x%x\n",
+ nv_offset);
+ return err;
+ }
+
+ err = asd_reset_flash(asd_ha);
+ if (err) {
+ ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+ return err;
+ }
+
+ src_buf = (const u8 *)src;
+ for (i = 0; i < bytes_to_write; i++) {
+ /* Setup program command sequence */
+ switch (asd_ha->hw_prof.flash.method) {
+ case FLASH_METHOD_A:
+ {
+ asd_write_reg_byte(asd_ha,
+ (reg + 0xAAA), 0xAA);
+ asd_write_reg_byte(asd_ha,
+ (reg + 0x555), 0x55);
+ asd_write_reg_byte(asd_ha,
+ (reg + 0xAAA), 0xA0);
+ asd_write_reg_byte(asd_ha,
+ (reg + nv_offset + i),
+ (*(src_buf + i)));
+ break;
+ }
+ case FLASH_METHOD_B:
+ {
+ asd_write_reg_byte(asd_ha,
+ (reg + 0x555), 0xAA);
+ asd_write_reg_byte(asd_ha,
+ (reg + 0x2AA), 0x55);
+ asd_write_reg_byte(asd_ha,
+ (reg + 0x555), 0xA0);
+ asd_write_reg_byte(asd_ha,
+ (reg + nv_offset + i),
+ (*(src_buf + i)));
+ break;
+ }
+ default:
+ break;
+ }
+ if (asd_chk_write_status(asd_ha,
+ (nv_offset + i), 0) != 0) {
+ ASD_DPRINTK("aicx: Write failed at offset:0x%x\n",
+ reg + nv_offset + i);
+ return FAIL_WRITE_FLASH;
+ }
+ }
+
+ err = asd_reset_flash(asd_ha);
+ if (err) {
+ ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+ return err;
+ }
+ return 0;
+}
+
+int asd_chk_write_status(struct asd_ha_struct *asd_ha,
+ u32 sector_addr, u8 erase_flag)
+{
+ u32 reg;
+ u32 loop_cnt;
+ u8 nv_data1, nv_data2;
+ u8 toggle_bit1;
+
+ /*
+ * Read from DQ2 requires sector address
+ * while it's dont care for DQ6
+ */
+ reg = asd_ha->hw_prof.flash.bar;
+
+ for (loop_cnt = 0; loop_cnt < 50000; loop_cnt++) {
+ nv_data1 = asd_read_reg_byte(asd_ha, reg);
+ nv_data2 = asd_read_reg_byte(asd_ha, reg);
+
+ toggle_bit1 = ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6)
+ ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6));
+
+ if (toggle_bit1 == 0) {
+ return 0;
+ } else {
+ if (nv_data2 & FLASH_STATUS_BIT_MASK_DQ5) {
+ nv_data1 = asd_read_reg_byte(asd_ha,
+ reg);
+ nv_data2 = asd_read_reg_byte(asd_ha,
+ reg);
+ toggle_bit1 =
+ ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6)
+ ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6));
+
+ if (toggle_bit1 == 0)
+ return 0;
+ }
+ }
+
+ /*
+ * ERASE is a sector-by-sector operation and requires
+ * more time to finish while WRITE is byte-byte-byte
+ * operation and takes lesser time to finish.
+ *
+ * For some strange reason a reduced ERASE delay gives different
+ * behaviour across different spirit boards. Hence we set
+ * a optimum balance of 50mus for ERASE which works well
+ * across all boards.
+ */
+ if (erase_flag) {
+ udelay(FLASH_STATUS_ERASE_DELAY_COUNT);
+ } else {
+ udelay(FLASH_STATUS_WRITE_DELAY_COUNT);
+ }
+ }
+ return -1;
+}
+
+/**
+ * asd_hwi_erase_nv_sector - Erase the flash memory sectors.
+ * @asd_ha: pointer to the host adapter structure
+ * @flash_addr: pointer to offset from flash memory
+ * @size: total bytes to erase.
+ */
+int asd_erase_nv_sector(struct asd_ha_struct *asd_ha, u32 flash_addr, u32 size)
+{
+ u32 reg;
+ u32 sector_addr;
+
+ reg = asd_ha->hw_prof.flash.bar;
+
+ /* sector staring address */
+ sector_addr = flash_addr & FLASH_SECTOR_SIZE_MASK;
+
+ /*
+ * Erasing an flash sector needs to be done in six consecutive
+ * write cyles.
+ */
+ while (sector_addr < flash_addr+size) {
+ switch (asd_ha->hw_prof.flash.method) {
+ case FLASH_METHOD_A:
+ asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA);
+ asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55);
+ asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0x80);
+ asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA);
+ asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55);
+ asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30);
+ break;
+ case FLASH_METHOD_B:
+ asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
+ asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
+ asd_write_reg_byte(asd_ha, (reg + 0x555), 0x80);
+ asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
+ asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
+ asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30);
+ break;
+ default:
+ break;
+ }
+
+ if (asd_chk_write_status(asd_ha, sector_addr, 1) != 0)
+ return FAIL_ERASE_FLASH;
+
+ sector_addr += FLASH_SECTOR_SIZE;
+ }
+
+ return 0;
+}
+
+int asd_check_flash_type(struct asd_ha_struct *asd_ha)
+{
+ u8 manuf_id;
+ u8 dev_id;
+ u8 sec_prot;
+ u32 inc;
+ u32 reg;
+ int err;
+
+ /* get Flash memory base address */
+ reg = asd_ha->hw_prof.flash.bar;
+
+ /* Determine flash info */
+ err = asd_reset_flash(asd_ha);
+ if (err) {
+ ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+ return err;
+ }
+
+ asd_ha->hw_prof.flash.method = FLASH_METHOD_UNKNOWN;
+ asd_ha->hw_prof.flash.manuf = FLASH_MANUF_ID_UNKNOWN;
+ asd_ha->hw_prof.flash.dev_id = FLASH_DEV_ID_UNKNOWN;
+
+ /* Get flash info. This would most likely be AMD Am29LV family flash.
+ * First try the sequence for word mode. It is the same as for
+ * 008B (byte mode only), 160B (word mode) and 800D (word mode).
+ */
+ inc = asd_ha->hw_prof.flash.wide ? 2 : 1;
+ asd_write_reg_byte(asd_ha, reg + 0xAAA, 0xAA);
+ asd_write_reg_byte(asd_ha, reg + 0x555, 0x55);
+ asd_write_reg_byte(asd_ha, reg + 0xAAA, 0x90);
+ manuf_id = asd_read_reg_byte(asd_ha, reg);
+ dev_id = asd_read_reg_byte(asd_ha, reg + inc);
+ sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc);
+ /* Get out of autoselect mode. */
+ err = asd_reset_flash(asd_ha);
+ if (err) {
+ ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+ return err;
+ }
+ ASD_DPRINTK("Flash MethodA manuf_id(0x%x) dev_id(0x%x) "
+ "sec_prot(0x%x)\n", manuf_id, dev_id, sec_prot);
+ err = asd_reset_flash(asd_ha);
+ if (err != 0)
+ return err;
+
+ switch (manuf_id) {
+ case FLASH_MANUF_ID_AMD:
+ switch (sec_prot) {
+ case FLASH_DEV_ID_AM29LV800DT:
+ case FLASH_DEV_ID_AM29LV640MT:
+ case FLASH_DEV_ID_AM29F800B:
+ asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
+ break;
+ default:
+ break;
+ }
+ break;
+ case FLASH_MANUF_ID_ST:
+ switch (sec_prot) {
+ case FLASH_DEV_ID_STM29W800DT:
+ case FLASH_DEV_ID_STM29LV640:
+ asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
+ break;
+ default:
+ break;
+ }
+ break;
+ case FLASH_MANUF_ID_FUJITSU:
+ switch (sec_prot) {
+ case FLASH_DEV_ID_MBM29LV800TE:
+ case FLASH_DEV_ID_MBM29DL800TA:
+ asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
+ break;
+ }
+ break;
+ case FLASH_MANUF_ID_MACRONIX:
+ switch (sec_prot) {
+ case FLASH_DEV_ID_MX29LV800BT:
+ asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
+ break;
+ }
+ break;
+ }
+
+ if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN) {
+ err = asd_reset_flash(asd_ha);
+ if (err) {
+ ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+ return err;
+ }
+
+ /* Issue Unlock sequence for AM29LV008BT */
+ asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
+ asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
+ asd_write_reg_byte(asd_ha, (reg + 0x555), 0x90);
+ manuf_id = asd_read_reg_byte(asd_ha, reg);
+ dev_id = asd_read_reg_byte(asd_ha, reg + inc);
+ sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc);
+
+ ASD_DPRINTK("Flash MethodB manuf_id(0x%x) dev_id(0x%x) sec_prot"
+ "(0x%x)\n", manuf_id, dev_id, sec_prot);
+
+ err = asd_reset_flash(asd_ha);
+ if (err != 0) {
+ ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+ return err;
+ }
+
+ switch (manuf_id) {
+ case FLASH_MANUF_ID_AMD:
+ switch (dev_id) {
+ case FLASH_DEV_ID_AM29LV008BT:
+ asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
+ break;
+ default:
+ break;
+ }
+ break;
+ case FLASH_MANUF_ID_ST:
+ switch (dev_id) {
+ case FLASH_DEV_ID_STM29008:
+ asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
+ break;
+ default:
+ break;
+ }
+ break;
+ case FLASH_MANUF_ID_FUJITSU:
+ switch (dev_id) {
+ case FLASH_DEV_ID_MBM29LV008TA:
+ asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
+ break;
+ }
+ break;
+ case FLASH_MANUF_ID_INTEL:
+ switch (dev_id) {
+ case FLASH_DEV_ID_I28LV00TAT:
+ asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
+ break;
+ }
+ break;
+ case FLASH_MANUF_ID_MACRONIX:
+ switch (dev_id) {
+ case FLASH_DEV_ID_I28LV00TAT:
+ asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
+ break;
+ }
+ break;
+ default:
+ return FAIL_FIND_FLASH_ID;
+ }
+ }
+
+ if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN)
+ return FAIL_FIND_FLASH_ID;
+
+ asd_ha->hw_prof.flash.manuf = manuf_id;
+ asd_ha->hw_prof.flash.dev_id = dev_id;
+ asd_ha->hw_prof.flash.sec_prot = sec_prot;
+ return 0;
+}
diff --git a/drivers/scsi/aic94xx/aic94xx_sds.h b/drivers/scsi/aic94xx/aic94xx_sds.h
new file mode 100644
index 00000000000..a06dc0114b8
--- /dev/null
+++ b/drivers/scsi/aic94xx/aic94xx_sds.h
@@ -0,0 +1,121 @@
+/*
+ * Aic94xx SAS/SATA driver hardware interface header file.
+ *
+ * Copyright (C) 2005 Adaptec, Inc. All rights reserved.
+ * Copyright (C) 2005 Gilbert Wu <gilbert_wu@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_SDS_H_
+#define _AIC94XX_SDS_H_
+
+enum {
+ FLASH_METHOD_UNKNOWN,
+ FLASH_METHOD_A,
+ FLASH_METHOD_B
+};
+
+#define FLASH_MANUF_ID_AMD 0x01
+#define FLASH_MANUF_ID_ST 0x20
+#define FLASH_MANUF_ID_FUJITSU 0x04
+#define FLASH_MANUF_ID_MACRONIX 0xC2
+#define FLASH_MANUF_ID_INTEL 0x89
+#define FLASH_MANUF_ID_UNKNOWN 0xFF
+
+#define FLASH_DEV_ID_AM29LV008BT 0x3E
+#define FLASH_DEV_ID_AM29LV800DT 0xDA
+#define FLASH_DEV_ID_STM29W800DT 0xD7
+#define FLASH_DEV_ID_STM29LV640 0xDE
+#define FLASH_DEV_ID_STM29008 0xEA
+#define FLASH_DEV_ID_MBM29LV800TE 0xDA
+#define FLASH_DEV_ID_MBM29DL800TA 0x4A
+#define FLASH_DEV_ID_MBM29LV008TA 0x3E
+#define FLASH_DEV_ID_AM29LV640MT 0x7E
+#define FLASH_DEV_ID_AM29F800B 0xD6
+#define FLASH_DEV_ID_MX29LV800BT 0xDA
+#define FLASH_DEV_ID_MX29LV008CT 0xDA
+#define FLASH_DEV_ID_I28LV00TAT 0x3E
+#define FLASH_DEV_ID_UNKNOWN 0xFF
+
+/* status bit mask values */
+#define FLASH_STATUS_BIT_MASK_DQ6 0x40
+#define FLASH_STATUS_BIT_MASK_DQ5 0x20
+#define FLASH_STATUS_BIT_MASK_DQ2 0x04
+
+/* minimum value in micro seconds needed for checking status */
+#define FLASH_STATUS_ERASE_DELAY_COUNT 50
+#define FLASH_STATUS_WRITE_DELAY_COUNT 25
+
+#define FLASH_SECTOR_SIZE 0x010000
+#define FLASH_SECTOR_SIZE_MASK 0xffff0000
+
+#define FLASH_OK 0x000000
+#define FAIL_OPEN_BIOS_FILE 0x000100
+#define FAIL_CHECK_PCI_ID 0x000200
+#define FAIL_CHECK_SUM 0x000300
+#define FAIL_UNKNOWN 0x000400
+#define FAIL_VERIFY 0x000500
+#define FAIL_RESET_FLASH 0x000600
+#define FAIL_FIND_FLASH_ID 0x000700
+#define FAIL_ERASE_FLASH 0x000800
+#define FAIL_WRITE_FLASH 0x000900
+#define FAIL_FILE_SIZE 0x000a00
+#define FAIL_PARAMETERS 0x000b00
+#define FAIL_OUT_MEMORY 0x000c00
+#define FLASH_IN_PROGRESS 0x001000
+
+struct controller_id {
+ u32 vendor; /* PCI Vendor ID */
+ u32 device; /* PCI Device ID */
+ u32 sub_vendor; /* PCI Subvendor ID */
+ u32 sub_device; /* PCI Subdevice ID */
+};
+
+struct image_info {
+ u32 ImageId; /* Identifies the image */
+ u32 ImageOffset; /* Offset the beginning of the file */
+ u32 ImageLength; /* length of the image */
+ u32 ImageChecksum; /* Image checksum */
+ u32 ImageVersion; /* Version of the image, could be build number */
+};
+
+struct bios_file_header {
+ u8 signature[32]; /* Signature/Cookie to identify the file */
+ u32 checksum; /*Entire file checksum with this field zero */
+ u32 antidote; /* Entire file checksum with this field 0xFFFFFFFF */
+ struct controller_id contrl_id; /*PCI id to identify the controller */
+ u32 filelen; /*Length of the entire file*/
+ u32 chunk_num; /*The chunk/part number for multiple Image files */
+ u32 total_chunks; /*Total number of chunks/parts in the image file */
+ u32 num_images; /* Number of images in the file */
+ u32 build_num; /* Build number of this image */
+ struct image_info image_header;
+};
+
+int asd_verify_flash_seg(struct asd_ha_struct *asd_ha,
+ const void *src, u32 dest_offset, u32 bytes_to_verify);
+int asd_write_flash_seg(struct asd_ha_struct *asd_ha,
+ const void *src, u32 dest_offset, u32 bytes_to_write);
+int asd_chk_write_status(struct asd_ha_struct *asd_ha,
+ u32 sector_addr, u8 erase_flag);
+int asd_check_flash_type(struct asd_ha_struct *asd_ha);
+int asd_erase_nv_sector(struct asd_ha_struct *asd_ha,
+ u32 flash_addr, u32 size);
+#endif
diff --git a/drivers/scsi/aic94xx/aic94xx_seq.c b/drivers/scsi/aic94xx/aic94xx_seq.c
index 56e4b3ba6a0..5fdca93892a 100644
--- a/drivers/scsi/aic94xx/aic94xx_seq.c
+++ b/drivers/scsi/aic94xx/aic94xx_seq.c
@@ -27,6 +27,7 @@
*/
#include <linux/delay.h>
+#include <linux/gfp.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/firmware.h>
@@ -44,10 +45,9 @@
#define PAUSE_TRIES 1000
static const struct firmware *sequencer_fw;
-static const char *sequencer_version;
static u16 cseq_vecs[CSEQ_NUM_VECS], lseq_vecs[LSEQ_NUM_VECS], mode2_task,
cseq_idle_loop, lseq_idle_loop;
-static u8 *cseq_code, *lseq_code;
+static const u8 *cseq_code, *lseq_code;
static u32 cseq_code_size, lseq_code_size;
static u16 first_scb_site_no = 0xFFFF;
@@ -61,7 +61,7 @@ static u16 last_scb_site_no;
*
* Return 0 on success, negative on failure.
*/
-int asd_pause_cseq(struct asd_ha_struct *asd_ha)
+static int asd_pause_cseq(struct asd_ha_struct *asd_ha)
{
int count = PAUSE_TRIES;
u32 arp2ctl;
@@ -88,7 +88,7 @@ int asd_pause_cseq(struct asd_ha_struct *asd_ha)
*
* Return 0 on success, negative on error.
*/
-int asd_unpause_cseq(struct asd_ha_struct *asd_ha)
+static int asd_unpause_cseq(struct asd_ha_struct *asd_ha)
{
u32 arp2ctl;
int count = PAUSE_TRIES;
@@ -116,7 +116,7 @@ int asd_unpause_cseq(struct asd_ha_struct *asd_ha)
*
* Return 0 on success, negative on error.
*/
-static inline int asd_seq_pause_lseq(struct asd_ha_struct *asd_ha, int lseq)
+static int asd_seq_pause_lseq(struct asd_ha_struct *asd_ha, int lseq)
{
u32 arp2ctl;
int count = PAUSE_TRIES;
@@ -144,7 +144,7 @@ static inline int asd_seq_pause_lseq(struct asd_ha_struct *asd_ha, int lseq)
*
* Return 0 on success, negative on failure.
*/
-int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask)
+static int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask)
{
int lseq;
int err = 0;
@@ -165,7 +165,7 @@ int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask)
*
* Return 0 on success, negative on error.
*/
-static inline int asd_seq_unpause_lseq(struct asd_ha_struct *asd_ha, int lseq)
+static int asd_seq_unpause_lseq(struct asd_ha_struct *asd_ha, int lseq)
{
u32 arp2ctl;
int count = PAUSE_TRIES;
@@ -187,27 +187,6 @@ static inline int asd_seq_unpause_lseq(struct asd_ha_struct *asd_ha, int lseq)
}
-/**
- * asd_unpause_lseq - unpause the link sequencer(s)
- * @asd_ha: pointer to host adapter structure
- * @lseq_mask: mask of link sequencers of interest
- *
- * Return 0 on success, negative on failure.
- */
-int asd_unpause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask)
-{
- int lseq;
- int err = 0;
-
- for_each_sequencer(lseq_mask, lseq_mask, lseq) {
- err = asd_seq_unpause_lseq(asd_ha, lseq);
- if (err)
- return err;
- }
-
- return err;
-}
-
/* ---------- Downloading CSEQ/LSEQ microcode ---------- */
static int asd_verify_cseq(struct asd_ha_struct *asd_ha, const u8 *_prog,
@@ -609,7 +588,7 @@ static void asd_init_cseq_mdp(struct asd_ha_struct *asd_ha)
* asd_init_cseq_scratch -- setup and init CSEQ
* @asd_ha: pointer to host adapter structure
*
- * Setup and initialize Central sequencers. Initialiaze the mode
+ * Setup and initialize Central sequencers. Initialize the mode
* independent and dependent scratch page to the default settings.
*/
static void asd_init_cseq_scratch(struct asd_ha_struct *asd_ha)
@@ -803,20 +782,22 @@ static void asd_init_lseq_mdp(struct asd_ha_struct *asd_ha, int lseq)
asd_write_reg_word(asd_ha, LmSEQ_OOB_INT_ENABLES(lseq), 0);
/*
* Set the desired interval between transmissions of the NOTIFY
- * (ENABLE SPINUP) primitive. Must be initilized to val - 1.
+ * (ENABLE SPINUP) primitive. Must be initialized to val - 1.
*/
asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_TIMEOUT(lseq),
ASD_NOTIFY_TIMEOUT - 1);
/* No delay for the first NOTIFY to be sent to the attached target. */
asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_DOWN_COUNT(lseq),
ASD_NOTIFY_DOWN_COUNT);
+ asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_INITIAL_COUNT(lseq),
+ ASD_NOTIFY_DOWN_COUNT);
/* LSEQ Mode dependent, mode 0 and 1, page 1 setup. */
for (i = 0; i < 2; i++) {
int j;
/* Start from Page 1 of Mode 0 and 1. */
moffs = LSEQ_PAGE_SIZE + i*LSEQ_MODE_SCRATCH_SIZE;
- /* All the fields of page 1 can be intialized to 0. */
+ /* All the fields of page 1 can be initialized to 0. */
for (j = 0; j < LSEQ_PAGE_SIZE; j += 4)
asd_write_reg_dword(asd_ha, LmSCRATCH(lseq)+moffs+j,0);
}
@@ -907,6 +888,16 @@ static void asd_init_scb_sites(struct asd_ha_struct *asd_ha)
for (i = 0; i < ASD_SCB_SIZE; i += 4)
asd_scbsite_write_dword(asd_ha, site_no, i, 0);
+ /* Initialize SCB Site Opcode field to invalid. */
+ asd_scbsite_write_byte(asd_ha, site_no,
+ offsetof(struct scb_header, opcode),
+ 0xFF);
+
+ /* Initialize SCB Site Flags field to mean a response
+ * frame has been received. This means inadvertent
+ * frames received to be dropped. */
+ asd_scbsite_write_byte(asd_ha, site_no, 0x49, 0x01);
+
/* Workaround needed by SEQ to fix a SATA issue is to exclude
* certain SCB sites from the free list. */
if (!SCB_SITE_VALID(site_no))
@@ -922,16 +913,6 @@ static void asd_init_scb_sites(struct asd_ha_struct *asd_ha)
/* Q_NEXT field of the last SCB is invalidated. */
asd_scbsite_write_word(asd_ha, site_no, 0, first_scb_site_no);
- /* Initialize SCB Site Opcode field to invalid. */
- asd_scbsite_write_byte(asd_ha, site_no,
- offsetof(struct scb_header, opcode),
- 0xFF);
-
- /* Initialize SCB Site Flags field to mean a response
- * frame has been received. This means inadvertent
- * frames received to be dropped. */
- asd_scbsite_write_byte(asd_ha, site_no, 0x49, 0x01);
-
first_scb_site_no = site_no;
max_scbs++;
}
@@ -957,7 +938,7 @@ static void asd_init_cseq_cio(struct asd_ha_struct *asd_ha)
asd_write_reg_dword(asd_ha, SCBPRO, 0);
asd_write_reg_dword(asd_ha, CSEQCON, 0);
- /* Intialize CSEQ Mode 11 Interrupt Vectors.
+ /* Initialize CSEQ Mode 11 Interrupt Vectors.
* The addresses are 16 bit wide and in dword units.
* The values of their macros are in byte units.
* Thus we have to divide by 4. */
@@ -980,7 +961,7 @@ static void asd_init_cseq_cio(struct asd_ha_struct *asd_ha)
asd_write_reg_word(asd_ha, CPRGMCNT, cseq_idle_loop);
for (i = 0; i < 8; i++) {
- /* Intialize Mode n Link m Interrupt Enable. */
+ /* Initialize Mode n Link m Interrupt Enable. */
asd_write_reg_dword(asd_ha, CMnINTEN(i), EN_CMnRSPMBXF);
/* Initialize Mode n Request Mailbox. */
asd_write_reg_dword(asd_ha, CMnREQMBX(i), 0);
@@ -1173,6 +1154,16 @@ static void asd_init_ddb_0(struct asd_ha_struct *asd_ha)
set_bit(0, asd_ha->hw_prof.ddb_bitmap);
}
+static void asd_seq_init_ddb_sites(struct asd_ha_struct *asd_ha)
+{
+ unsigned int i;
+ unsigned int ddb_site;
+
+ for (ddb_site = 0 ; ddb_site < ASD_MAX_DDBS; ddb_site++)
+ for (i = 0; i < sizeof(struct asd_ddb_ssp_smp_target_port); i+= 4)
+ asd_ddbsite_write_dword(asd_ha, ddb_site, i, 0);
+}
+
/**
* asd_seq_setup_seqs -- setup and initialize central and link sequencers
* @asd_ha: pointer to host adapter structure
@@ -1182,6 +1173,9 @@ static void asd_seq_setup_seqs(struct asd_ha_struct *asd_ha)
int lseq;
u8 lseq_mask;
+ /* Initialize DDB sites */
+ asd_seq_init_ddb_sites(asd_ha);
+
/* Initialize SCB sites. Done first to compute some values which
* the rest of the init code depends on. */
asd_init_scb_sites(asd_ha);
@@ -1232,10 +1226,17 @@ static int asd_seq_start_lseq(struct asd_ha_struct *asd_ha, int lseq)
return asd_seq_unpause_lseq(asd_ha, lseq);
}
+int asd_release_firmware(void)
+{
+ release_firmware(sequencer_fw);
+ return 0;
+}
+
static int asd_request_firmware(struct asd_ha_struct *asd_ha)
{
int err, i;
- struct sequencer_file_header header, *hdr_ptr;
+ struct sequencer_file_header header;
+ const struct sequencer_file_header *hdr_ptr;
u32 csum = 0;
u16 *ptr_cseq_vecs, *ptr_lseq_vecs;
@@ -1249,12 +1250,11 @@ static int asd_request_firmware(struct asd_ha_struct *asd_ha)
if (err)
return err;
- hdr_ptr = (struct sequencer_file_header *)sequencer_fw->data;
+ hdr_ptr = (const struct sequencer_file_header *)sequencer_fw->data;
header.csum = le32_to_cpu(hdr_ptr->csum);
header.major = le32_to_cpu(hdr_ptr->major);
header.minor = le32_to_cpu(hdr_ptr->minor);
- sequencer_version = hdr_ptr->version;
header.cseq_table_offset = le32_to_cpu(hdr_ptr->cseq_table_offset);
header.cseq_table_size = le32_to_cpu(hdr_ptr->cseq_table_size);
header.lseq_table_offset = le32_to_cpu(hdr_ptr->lseq_table_offset);
@@ -1281,6 +1281,16 @@ static int asd_request_firmware(struct asd_ha_struct *asd_ha)
return -EINVAL;
}
+ asd_printk("Found sequencer Firmware version %d.%d (%s)\n",
+ header.major, header.minor, hdr_ptr->version);
+
+ if (header.major != SAS_RAZOR_SEQUENCER_FW_MAJOR) {
+ asd_printk("Firmware Major Version Mismatch;"
+ "driver requires version %d.X",
+ SAS_RAZOR_SEQUENCER_FW_MAJOR);
+ return -EINVAL;
+ }
+
ptr_cseq_vecs = (u16 *)&sequencer_fw->data[header.cseq_table_offset];
ptr_lseq_vecs = (u16 *)&sequencer_fw->data[header.lseq_table_offset];
mode2_task = header.mode2_task;
@@ -1313,7 +1323,6 @@ int asd_init_seqs(struct asd_ha_struct *asd_ha)
return err;
}
- asd_printk("using sequencer %s\n", sequencer_version);
err = asd_seq_download_seqs(asd_ha);
if (err) {
asd_printk("couldn't download sequencers for %s\n",
@@ -1369,14 +1378,15 @@ int asd_start_seqs(struct asd_ha_struct *asd_ha)
* port_map_by_links is also used as the conn_mask byte in the
* initiator/target port DDB.
*/
-void asd_update_port_links(struct asd_sas_phy *sas_phy)
+void asd_update_port_links(struct asd_ha_struct *asd_ha, struct asd_phy *phy)
{
- struct asd_ha_struct *asd_ha = sas_phy->ha->lldd_ha;
- const u8 phy_mask = (u8) sas_phy->port->phy_mask;
+ const u8 phy_mask = (u8) phy->asd_port->phy_mask;
u8 phy_is_up;
u8 mask;
int i, err;
+ unsigned long flags;
+ spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
for_each_phy(phy_mask, mask, i)
asd_ddbsite_write_byte(asd_ha, 0,
offsetof(struct asd_ddb_seq_shared,
@@ -1396,6 +1406,7 @@ void asd_update_port_links(struct asd_sas_phy *sas_phy)
break;
}
}
+ spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
if (err)
asd_printk("couldn't update DDB 0:error:%d\n", err);
diff --git a/drivers/scsi/aic94xx/aic94xx_seq.h b/drivers/scsi/aic94xx/aic94xx_seq.h
index 42281c36153..ad787c55525 100644
--- a/drivers/scsi/aic94xx/aic94xx_seq.h
+++ b/drivers/scsi/aic94xx/aic94xx_seq.h
@@ -31,6 +31,7 @@
#define LSEQ_NUM_VECS 11
#define SAS_RAZOR_SEQUENCER_FW_FILE "aic94xx-seq.fw"
+#define SAS_RAZOR_SEQUENCER_FW_MAJOR 1
/* Note: All quantites in the sequencer file are little endian */
struct sequencer_file_header {
@@ -57,14 +58,11 @@ struct sequencer_file_header {
} __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);
+int asd_release_firmware(void);
-void asd_update_port_links(struct asd_sas_phy *phy);
+void asd_update_port_links(struct asd_ha_struct *asd_ha, struct asd_phy *phy);
#endif
#endif
diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c
index d202ed5a670..59b86e260ce 100644
--- a/drivers/scsi/aic94xx/aic94xx_task.c
+++ b/drivers/scsi/aic94xx/aic94xx_task.c
@@ -33,7 +33,7 @@ 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)
+static void asd_can_dequeue(struct asd_ha_struct *asd_ha, int num)
{
unsigned long flags;
@@ -51,9 +51,9 @@ static const u8 data_dir_flags[] = {
[PCI_DMA_NONE] = DATA_DIR_NONE, /* NO TRANSFER */
};
-static inline int asd_map_scatterlist(struct sas_task *task,
- struct sg_el *sg_arr,
- gfp_t gfp_flags)
+static int asd_map_scatterlist(struct sas_task *task,
+ struct sg_el *sg_arr,
+ gfp_t gfp_flags)
{
struct asd_ascb *ascb = task->lldd_task;
struct asd_ha_struct *asd_ha = ascb->ha;
@@ -74,8 +74,13 @@ static inline int asd_map_scatterlist(struct sas_task *task,
return 0;
}
- num_sg = pci_map_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
- task->data_dir);
+ /* STP tasks come from libata which has already mapped
+ * the SG list */
+ if (sas_protocol_ata(task->task_proto))
+ num_sg = task->num_scatter;
+ else
+ num_sg = pci_map_sg(asd_ha->pcidev, task->scatter,
+ task->num_scatter, task->data_dir);
if (num_sg == 0)
return -ENOMEM;
@@ -89,7 +94,7 @@ static inline int asd_map_scatterlist(struct sas_task *task,
res = -ENOMEM;
goto err_unmap;
}
- for (sc = task->scatter, i = 0; i < num_sg; i++, sc++) {
+ for_each_sg(task->scatter, sc, num_sg, i) {
struct sg_el *sg =
&((struct sg_el *)ascb->sg_arr->vaddr)[i];
sg->bus_addr = cpu_to_le64((u64)sg_dma_address(sc));
@@ -98,7 +103,7 @@ static inline int asd_map_scatterlist(struct sas_task *task,
sg->flags |= ASD_SG_EL_LIST_EOL;
}
- for (sc = task->scatter, i = 0; i < 2; i++, sc++) {
+ for_each_sg(task->scatter, sc, 2, i) {
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));
@@ -110,7 +115,7 @@ static inline int asd_map_scatterlist(struct sas_task *task,
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++) {
+ for_each_sg(task->scatter, sc, num_sg, i) {
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));
@@ -120,12 +125,13 @@ static inline int asd_map_scatterlist(struct sas_task *task,
return 0;
err_unmap:
- pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
- task->data_dir);
+ if (sas_protocol_ata(task->task_proto))
+ 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)
+static void asd_unmap_scatterlist(struct asd_ascb *ascb)
{
struct asd_ha_struct *asd_ha = ascb->ha;
struct sas_task *task = ascb->uldd_task;
@@ -142,8 +148,9 @@ static inline void asd_unmap_scatterlist(struct asd_ascb *ascb)
}
asd_free_coherent(asd_ha, ascb->sg_arr);
- pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
- task->data_dir);
+ if (task->task_proto != SAS_PROTOCOL_STP)
+ pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
+ task->data_dir);
}
/* ---------- Task complete tasklet ---------- */
@@ -180,29 +187,13 @@ static void asd_get_response_tasklet(struct asd_ascb *ascb,
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) {
+ if (task->task_proto == SAS_PROTOCOL_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);
- }
- }
+
+ sas_ssp_task_response(&asd_ha->pcidev->dev, task, iu);
} else {
struct ata_task_resp *resp = (void *) &ts->buf[0];
@@ -210,7 +201,7 @@ static void asd_get_response_tasklet(struct asd_ascb *ascb,
if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) {
resp->frame_len = le16_to_cpu(*(__le16 *)(r+6));
- memcpy(&resp->ending_fis[0], r+16, 24);
+ memcpy(&resp->ending_fis[0], r+16, ATA_RESP_FIS_SIZE);
ts->buf_valid_size = sizeof(*resp);
}
}
@@ -232,7 +223,7 @@ Again:
switch (opcode) {
case TC_NO_ERROR:
ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAM_GOOD;
+ ts->stat = SAM_STAT_GOOD;
break;
case TC_UNDERRUN:
ts->resp = SAS_TASK_COMPLETE;
@@ -329,19 +320,19 @@ Again:
case TC_RESUME:
case TC_PARTIAL_SG_LIST:
default:
- ASD_DPRINTK("%s: dl opcode: 0x%x?\n", __FUNCTION__, opcode);
+ ASD_DPRINTK("%s: dl opcode: 0x%x?\n", __func__, opcode);
break;
}
switch (task->task_proto) {
- case SATA_PROTO:
- case SAS_PROTO_STP:
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
asd_unbuild_ata_ascb(ascb);
break;
- case SAS_PROTO_SMP:
+ case SAS_PROTOCOL_SMP:
asd_unbuild_smp_ascb(ascb);
break;
- case SAS_PROTO_SSP:
+ case SAS_PROTOCOL_SSP:
asd_unbuild_ssp_ascb(ascb);
default:
break;
@@ -349,13 +340,16 @@ Again:
spin_lock_irqsave(&task->task_state_lock, flags);
task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
task->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((task->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ struct completion *completion = ascb->completion;
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);
+ if (completion)
+ complete(completion);
} else {
spin_unlock_irqrestore(&task->task_state_lock, flags);
task->lldd_task = NULL;
@@ -390,7 +384,6 @@ static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task,
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 */
@@ -444,7 +437,7 @@ static int asd_build_smp_ascb(struct asd_ascb *ascb, struct sas_task *task,
struct scb *scb;
pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_req, 1,
- PCI_DMA_FROMDEVICE);
+ PCI_DMA_TODEVICE);
pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_resp, 1,
PCI_DMA_FROMDEVICE);
@@ -479,7 +472,7 @@ static void asd_unbuild_smp_ascb(struct asd_ascb *a)
BUG_ON(!task);
pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_req, 1,
- PCI_DMA_FROMDEVICE);
+ PCI_DMA_TODEVICE);
pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_resp, 1,
PCI_DMA_FROMDEVICE);
}
@@ -512,7 +505,8 @@ static int asd_build_ssp_ascb(struct asd_ascb *ascb, struct sas_task *task,
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);
+ memcpy(scb->ssp_task.ssp_cmd.cdb, task->ssp_task.cmd->cmnd,
+ task->ssp_task.cmd->cmd_len);
scb->ssp_task.sister_scb = cpu_to_le16(0xFFFF);
scb->ssp_task.conn_handle = cpu_to_le16(
@@ -534,7 +528,7 @@ static void asd_unbuild_ssp_ascb(struct asd_ascb *a)
/* ---------- Execute Task ---------- */
-static inline int asd_can_queue(struct asd_ha_struct *asd_ha, int num)
+static int asd_can_queue(struct asd_ha_struct *asd_ha, int num)
{
int res = 0;
unsigned long flags;
@@ -557,6 +551,7 @@ int asd_execute_task(struct sas_task *task, const int num,
struct sas_task *t = task;
struct asd_ascb *ascb = NULL, *a;
struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
+ unsigned long flags;
res = asd_can_queue(asd_ha, num);
if (res)
@@ -578,17 +573,17 @@ int asd_execute_task(struct sas_task *task, const int num,
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;
+ if (t->task_proto & SAS_PROTOCOL_STP)
+ t->task_proto = SAS_PROTOCOL_STP;
switch (t->task_proto) {
- case SATA_PROTO:
- case SAS_PROTO_STP:
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
res = asd_build_ata_ascb(a, t, gfp_flags);
break;
- case SAS_PROTO_SMP:
+ case SAS_PROTOCOL_SMP:
res = asd_build_smp_ascb(a, t, gfp_flags);
break;
- case SAS_PROTO_SSP:
+ case SAS_PROTOCOL_SSP:
res = asd_build_ssp_ascb(a, t, gfp_flags);
break;
default:
@@ -599,6 +594,10 @@ int asd_execute_task(struct sas_task *task, const int num,
}
if (res)
goto out_err_unmap;
+
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags |= SAS_TASK_AT_INITIATOR;
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
}
list_del_init(&alist);
@@ -617,15 +616,18 @@ out_err_unmap:
if (a == b)
break;
t = a->uldd_task;
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
switch (t->task_proto) {
- case SATA_PROTO:
- case SAS_PROTO_STP:
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
asd_unbuild_ata_ascb(a);
break;
- case SAS_PROTO_SMP:
+ case SAS_PROTOCOL_SMP:
asd_unbuild_smp_ascb(a);
break;
- case SAS_PROTO_SSP:
+ case SAS_PROTOCOL_SSP:
asd_unbuild_ssp_ascb(a);
default:
break;
diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c
index 61234384503..d4c35df3d4a 100644
--- a/drivers/scsi/aic94xx/aic94xx_tmf.c
+++ b/drivers/scsi/aic94xx/aic94xx_tmf.c
@@ -25,6 +25,7 @@
*/
#include <linux/spinlock.h>
+#include <linux/gfp.h>
#include "aic94xx.h"
#include "aic94xx_sas.h"
#include "aic94xx_hwi.h"
@@ -53,64 +54,79 @@ static int asd_enqueue_internal(struct asd_ascb *ascb,
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;
+/* ---------- CLEAR NEXUS ---------- */
- spin_lock_irqsave(&seq->pend_q_lock, flags);
- seq->pending--;
- list_del_init(&ascb->list);
- spin_unlock_irqrestore(&seq->pend_q_lock, flags);
-}
+struct tasklet_completion_status {
+ int dl_opcode;
+ int tmf_state;
+ u8 tag_valid:1;
+ __be16 tag;
+};
+
+#define DECLARE_TCS(tcs) \
+ struct tasklet_completion_status tcs = { \
+ .dl_opcode = 0, \
+ .tmf_state = 0, \
+ .tag_valid = 0, \
+ .tag = 0, \
+ }
-/* ---------- CLEAR NEXUS ---------- */
static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,
struct done_list_struct *dl)
{
- ASD_DPRINTK("%s: here\n", __FUNCTION__);
+ struct tasklet_completion_status *tcs = ascb->uldd_task;
+ ASD_DPRINTK("%s: here\n", __func__);
if (!del_timer(&ascb->timer)) {
- ASD_DPRINTK("%s: couldn't delete timer\n", __FUNCTION__);
+ ASD_DPRINTK("%s: couldn't delete timer\n", __func__);
return;
}
- ASD_DPRINTK("%s: opcode: 0x%x\n", __FUNCTION__, dl->opcode);
- ascb->uldd_task = (void *) (unsigned long) dl->opcode;
- complete(&ascb->completion);
+ ASD_DPRINTK("%s: opcode: 0x%x\n", __func__, dl->opcode);
+ tcs->dl_opcode = dl->opcode;
+ complete(ascb->completion);
+ asd_ascb_free(ascb);
}
static void asd_clear_nexus_timedout(unsigned long data)
{
- struct asd_ascb *ascb = (void *) data;
+ struct asd_ascb *ascb = (void *)data;
+ struct tasklet_completion_status *tcs = ascb->uldd_task;
- ASD_DPRINTK("%s: here\n", __FUNCTION__);
- asd_timedout_common(data);
- ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED;
- complete(&ascb->completion);
+ ASD_DPRINTK("%s: here\n", __func__);
+ tcs->dl_opcode = TMF_RESP_FUNC_FAILED;
+ complete(ascb->completion);
}
#define CLEAR_NEXUS_PRE \
- ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \
+ struct asd_ascb *ascb; \
+ struct scb *scb; \
+ int res; \
+ DECLARE_COMPLETION_ONSTACK(completion); \
+ DECLARE_TCS(tcs); \
+ \
+ ASD_DPRINTK("%s: PRE\n", __func__); \
res = 1; \
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \
if (!ascb) \
return -ENOMEM; \
\
+ ascb->completion = &completion; \
+ ascb->uldd_task = &tcs; \
scb = ascb->scb; \
scb->header.opcode = CLEAR_NEXUS
#define CLEAR_NEXUS_POST \
- ASD_DPRINTK("%s: POST\n", __FUNCTION__); \
+ ASD_DPRINTK("%s: POST\n", __func__); \
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; \
+ ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __func__); \
+ wait_for_completion(&completion); \
+ res = tcs.dl_opcode; \
if (res == TC_NO_ERROR) \
res = TMF_RESP_FUNC_COMPLETE; \
+ return res; \
out_err: \
asd_ascb_free(ascb); \
return res
@@ -118,9 +134,6 @@ out_err: \
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;
@@ -130,9 +143,6 @@ int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha)
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;
@@ -140,37 +150,80 @@ int asd_clear_nexus_port(struct asd_sas_port *port)
CLEAR_NEXUS_POST;
}
-#if 0
-static int asd_clear_nexus_I_T(struct domain_device *dev)
+enum clear_nexus_phase {
+ NEXUS_PHASE_PRE,
+ NEXUS_PHASE_POST,
+ NEXUS_PHASE_RESUME,
+};
+
+static int asd_clear_nexus_I_T(struct domain_device *dev,
+ enum clear_nexus_phase phase)
{
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;
+ switch (phase) {
+ case NEXUS_PHASE_PRE:
+ scb->clear_nexus.flags = EXEC_Q | SUSPEND_TX;
+ break;
+ case NEXUS_PHASE_POST:
+ scb->clear_nexus.flags = SEND_Q | NOTINQ;
+ break;
+ case NEXUS_PHASE_RESUME:
+ scb->clear_nexus.flags = RESUME_TX;
+ }
scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
dev->lldd_dev);
CLEAR_NEXUS_POST;
}
-#endif
+
+int asd_I_T_nexus_reset(struct domain_device *dev)
+{
+ int res, tmp_res, i;
+ struct sas_phy *phy = sas_get_local_phy(dev);
+ /* Standard mandates link reset for ATA (type 0) and
+ * hard reset for SSP (type 1) */
+ int reset_type = (dev->dev_type == SAS_SATA_DEV ||
+ (dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
+
+ asd_clear_nexus_I_T(dev, NEXUS_PHASE_PRE);
+ /* send a hard reset */
+ ASD_DPRINTK("sending %s reset to %s\n",
+ reset_type ? "hard" : "soft", dev_name(&phy->dev));
+ res = sas_phy_reset(phy, reset_type);
+ if (res == TMF_RESP_FUNC_COMPLETE || res == -ENODEV) {
+ /* wait for the maximum settle time */
+ msleep(500);
+ /* clear all outstanding commands (keep nexus suspended) */
+ asd_clear_nexus_I_T(dev, NEXUS_PHASE_POST);
+ }
+ for (i = 0 ; i < 3; i++) {
+ tmp_res = asd_clear_nexus_I_T(dev, NEXUS_PHASE_RESUME);
+ if (tmp_res == TC_RESUME)
+ goto out;
+ msleep(500);
+ }
+
+ /* This is a bit of a problem: the sequencer is still suspended
+ * and is refusing to resume. Hope it will resume on a bigger hammer
+ * or the disk is lost */
+ dev_printk(KERN_ERR, &phy->dev,
+ "Failed to resume nexus after reset 0x%x\n", tmp_res);
+
+ res = TMF_RESP_FUNC_FAILED;
+ out:
+ sas_put_local_phy(phy);
+ return res;
+}
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);
@@ -181,9 +234,6 @@ 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;
@@ -199,9 +249,6 @@ 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;
@@ -217,11 +264,11 @@ static int asd_clear_nexus_index(struct sas_task *task)
static void asd_tmf_timedout(unsigned long data)
{
struct asd_ascb *ascb = (void *) data;
+ struct tasklet_completion_status *tcs = ascb->uldd_task;
ASD_DPRINTK("tmf timed out\n");
- asd_timedout_common(data);
- ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED;
- complete(&ascb->completion);
+ tcs->tmf_state = TMF_RESP_FUNC_FAILED;
+ complete(ascb->completion);
}
static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
@@ -273,35 +320,48 @@ static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
static void asd_tmf_tasklet_complete(struct asd_ascb *ascb,
struct done_list_struct *dl)
{
+ struct tasklet_completion_status *tcs;
+
if (!del_timer(&ascb->timer))
return;
+ tcs = ascb->uldd_task;
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;
+ tcs->dl_opcode = dl->opcode;
- complete(&ascb->completion);
+ if (dl->opcode == TC_SSP_RESP) {
+ tcs->tmf_state = asd_get_tmf_resp_tasklet(ascb, dl);
+ tcs->tag_valid = ascb->tag_valid;
+ tcs->tag = ascb->tag;
+ }
+
+ complete(ascb->completion);
+ asd_ascb_free(ascb);
}
-static inline int asd_clear_nexus(struct sas_task *task)
+static int asd_clear_nexus(struct sas_task *task)
{
int res = TMF_RESP_FUNC_FAILED;
+ int leftover;
struct asd_ascb *tascb = task->lldd_task;
+ DECLARE_COMPLETION_ONSTACK(completion);
unsigned long flags;
+ tascb->completion = &completion;
+
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);
+ leftover = wait_for_completion_timeout(&completion,
+ AIC94XX_SCB_TIMEOUT);
+ tascb->completion = NULL;
ASD_DPRINTK("came back from clear nexus\n");
spin_lock_irqsave(&task->task_state_lock, flags);
+ if (leftover < 1)
+ res = TMF_RESP_FUNC_FAILED;
if (task->task_state_flags & SAS_TASK_STATE_DONE)
res = TMF_RESP_FUNC_COMPLETE;
spin_unlock_irqrestore(&task->task_state_lock, flags);
@@ -350,12 +410,18 @@ int asd_abort_task(struct sas_task *task)
unsigned long flags;
struct asd_ascb *ascb = NULL;
struct scb *scb;
+ int leftover;
+ DECLARE_TCS(tcs);
+ DECLARE_COMPLETION_ONSTACK(completion);
+ DECLARE_COMPLETION_ONSTACK(tascb_completion);
+
+ tascb->completion = &tascb_completion;
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);
+ ASD_DPRINTK("%s: task 0x%p done\n", __func__, task);
goto out_done;
}
spin_unlock_irqrestore(&task->task_state_lock, flags);
@@ -363,26 +429,28 @@ int asd_abort_task(struct sas_task *task)
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
if (!ascb)
return -ENOMEM;
- scb = ascb->scb;
- scb->header.opcode = ABORT_TASK;
+ ascb->uldd_task = &tcs;
+ ascb->completion = &completion;
+ scb = ascb->scb;
+ scb->header.opcode = SCB_ABORT_TASK;
switch (task->task_proto) {
- case SATA_PROTO:
- case SAS_PROTO_STP:
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
scb->abort_task.proto_conn_rate = (1 << 5); /* STP */
break;
- case SAS_PROTO_SSP:
+ case SAS_PROTOCOL_SSP:
scb->abort_task.proto_conn_rate = (1 << 4); /* SSP */
scb->abort_task.proto_conn_rate |= task->dev->linkrate;
break;
- case SAS_PROTO_SMP:
+ case SAS_PROTOCOL_SMP:
break;
default:
break;
}
- if (task->task_proto == SAS_PROTO_SSP) {
+ if (task->task_proto == SAS_PROTOCOL_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);
@@ -406,78 +474,84 @@ int asd_abort_task(struct sas_task *task)
res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
asd_tmf_timedout);
if (res)
- goto out;
- wait_for_completion(&ascb->completion);
+ goto out_free;
+ wait_for_completion(&completion);
ASD_DPRINTK("tmf came back\n");
- res = (int) (unsigned long) ascb->uldd_task;
- tascb->tag = ascb->tag;
- tascb->tag_valid = ascb->tag_valid;
+ tascb->tag = tcs.tag;
+ tascb->tag_valid = tcs.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);
+ ASD_DPRINTK("%s: task 0x%p done\n", __func__, 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 */
+ if (tcs.dl_opcode == TC_SSP_RESP) {
+ /* The task to be aborted has been sent to the device.
+ * We got a Response IU for the ABORT TASK TMF. */
+ if (tcs.tmf_state == TMF_RESP_FUNC_COMPLETE)
+ res = asd_clear_nexus(task);
+ else
+ res = tcs.tmf_state;
+ } else if (tcs.dl_opcode == TC_NO_ERROR &&
+ tcs.tmf_state == TMF_RESP_FUNC_FAILED) {
+ /* timeout */
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)
+ } else {
+ /* In the following we assume that the managing layer
+ * will _never_ make a mistake, when issuing ABORT
+ * TASK.
+ */
+ switch (tcs.dl_opcode) {
+ default:
+ res = asd_clear_nexus(task);
+ /* fallthrough */
+ case TC_NO_ERROR:
+ 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:
+ res = TMF_RESP_INVALID_FRAME;
+ break;
+ case TF_TMF_TASK_DONE: /* done but not reported yet */
+ res = TMF_RESP_FUNC_FAILED;
+ leftover =
+ wait_for_completion_timeout(&tascb_completion,
+ AIC94XX_SCB_TIMEOUT);
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (leftover < 1)
+ res = TMF_RESP_FUNC_FAILED;
+ if (task->task_state_flags & SAS_TASK_STATE_DONE)
+ res = TMF_RESP_FUNC_COMPLETE;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ break;
+ case TF_TMF_NO_TAG:
+ case TF_TMF_TAG_FREE: /* the tag is in the free list */
+ case TF_TMF_NO_CONN_HANDLE: /* no such device */
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;
+ break;
+ case TF_TMF_NO_CTX: /* not in seq, or proto != SSP */
+ res = TMF_RESP_FUNC_ESUPP;
+ break;
+ }
}
-out_done:
+ out_done:
+ tascb->completion = NULL;
if (res == TMF_RESP_FUNC_COMPLETE) {
task->lldd_task = NULL;
mb();
asd_ascb_free(tascb);
}
-out:
+ ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res);
+ return res;
+
+ out_free:
asd_ascb_free(ascb);
ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res);
return res;
@@ -505,13 +579,18 @@ static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun,
struct asd_ascb *ascb;
int res = 1;
struct scb *scb;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ DECLARE_TCS(tcs);
- if (!(dev->tproto & SAS_PROTO_SSP))
+ if (!(dev->tproto & SAS_PROTOCOL_SSP))
return TMF_RESP_FUNC_ESUPP;
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
if (!ascb)
return -ENOMEM;
+
+ ascb->completion = &completion;
+ ascb->uldd_task = &tcs;
scb = ascb->scb;
if (tmf == TMF_QUERY_TASK)
@@ -544,33 +623,32 @@ static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun,
asd_tmf_timedout);
if (res)
goto out_err;
- wait_for_completion(&ascb->completion);
- res = (int) (unsigned long) ascb->uldd_task;
+ wait_for_completion(&completion);
- switch (res) {
- case TC_NO_ERROR + 0xFF00:
+ switch (tcs.dl_opcode) {
+ case TC_NO_ERROR:
res = TMF_RESP_FUNC_COMPLETE;
break;
- case TF_NAK_RECV + 0xFF00:
+ case TF_NAK_RECV:
res = TMF_RESP_INVALID_FRAME;
break;
- case TF_TMF_TASK_DONE + 0xFF00:
+ case TF_TMF_TASK_DONE:
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 */
+ case TF_TMF_NO_TAG:
+ case TF_TMF_TAG_FREE: /* the tag is in the free list */
+ case TF_TMF_NO_CONN_HANDLE: /* no such device */
res = TMF_RESP_FUNC_COMPLETE;
break;
- case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */
+ case TF_TMF_NO_CTX: /* 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;
+ /* Allow TMF response codes to propagate upwards */
+ res = tcs.dl_opcode;
break;
}
+ return res;
out_err:
asd_ascb_free(ascb);
return res;