diff options
Diffstat (limited to 'drivers/scsi/aic94xx')
| -rw-r--r-- | drivers/scsi/aic94xx/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/Makefile | 6 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx.h | 27 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_dev.c | 88 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_dump.c | 18 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_dump.h | 9 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_hwi.c | 77 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_hwi.h | 33 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_init.c | 410 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_reg.c | 53 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_reg_def.h | 15 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_sas.h | 4 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_scb.c | 285 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_sds.c | 416 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_sds.h | 121 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_seq.c | 111 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_seq.h | 8 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_task.c | 114 | ||||
| -rw-r--r-- | drivers/scsi/aic94xx/aic94xx_tmf.c | 356 |
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; |
