⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 aic94xx_scb.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Aic94xx SAS/SATA driver SCB management. * * Copyright (C) 2005 Adaptec, Inc.  All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> * * This file is licensed under GPLv2. * * This file is part of the aic94xx driver. * * The aic94xx driver is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; version 2 of the * License. * * The aic94xx driver is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the aic94xx driver; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA * */#include <scsi/scsi_host.h>#include "aic94xx.h"#include "aic94xx_reg.h"#include "aic94xx_hwi.h"#include "aic94xx_seq.h"#include "aic94xx_dump.h"/* ---------- EMPTY SCB ---------- */#define DL_PHY_MASK      7#define BYTES_DMAED      0#define PRIMITIVE_RECVD  0x08#define PHY_EVENT        0x10#define LINK_RESET_ERROR 0x18#define TIMER_EVENT      0x20#define REQ_TASK_ABORT   0xF0#define REQ_DEVICE_RESET 0xF1#define SIGNAL_NCQ_ERROR 0xF2#define CLEAR_NCQ_ERROR  0xF3#define PHY_EVENTS_STATUS (CURRENT_LOSS_OF_SIGNAL | CURRENT_OOB_DONE   \			   | CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \			   | CURRENT_OOB_ERROR)static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode){	struct sas_phy *sas_phy = phy->sas_phy.phy;	switch (oob_mode & 7) {	case PHY_SPEED_60:		/* FIXME: sas transport class doesn't have this */		phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS;		phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS;		break;	case PHY_SPEED_30:		phy->sas_phy.linkrate = SAS_LINK_RATE_3_0_GBPS;		phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;		break;	case PHY_SPEED_15:		phy->sas_phy.linkrate = SAS_LINK_RATE_1_5_GBPS;		phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;		break;	}	sas_phy->negotiated_linkrate = phy->sas_phy.linkrate;	sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;	sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;	sas_phy->maximum_linkrate = phy->phy_desc->max_sas_lrate;	sas_phy->minimum_linkrate = phy->phy_desc->min_sas_lrate;	if (oob_mode & SAS_MODE)		phy->sas_phy.oob_mode = SAS_OOB_MODE;	else if (oob_mode & SATA_MODE)		phy->sas_phy.oob_mode = SATA_OOB_MODE;}static inline void asd_phy_event_tasklet(struct asd_ascb *ascb,					 struct done_list_struct *dl){	struct asd_ha_struct *asd_ha = ascb->ha;	struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;	int phy_id = dl->status_block[0] & DL_PHY_MASK;	struct asd_phy *phy = &asd_ha->phys[phy_id];	u8 oob_status = dl->status_block[1] & PHY_EVENTS_STATUS;	u8 oob_mode   = dl->status_block[2];	switch (oob_status) {	case CURRENT_LOSS_OF_SIGNAL:		/* directly attached device was removed */		ASD_DPRINTK("phy%d: device unplugged\n", phy_id);		asd_turn_led(asd_ha, phy_id, 0);		sas_phy_disconnected(&phy->sas_phy);		sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL);		break;	case CURRENT_OOB_DONE:		/* hot plugged device */		asd_turn_led(asd_ha, phy_id, 1);		get_lrate_mode(phy, oob_mode);		ASD_DPRINTK("phy%d device plugged: lrate:0x%x, proto:0x%x\n",			    phy_id, phy->sas_phy.linkrate, phy->sas_phy.iproto);		sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);		break;	case CURRENT_SPINUP_HOLD:		/* hot plug SATA, no COMWAKE sent */		asd_turn_led(asd_ha, phy_id, 1);		sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD);		break;	case CURRENT_GTO_TIMEOUT:	case CURRENT_OOB_ERROR:		ASD_DPRINTK("phy%d error while OOB: oob status:0x%x\n", phy_id,			    dl->status_block[1]);		asd_turn_led(asd_ha, phy_id, 0);		sas_phy_disconnected(&phy->sas_phy);		sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR);		break;	}}/* If phys are enabled sparsely, this will do the right thing. */static inline unsigned ord_phy(struct asd_ha_struct *asd_ha,			       struct asd_phy *phy){	u8 enabled_mask = asd_ha->hw_prof.enabled_phys;	int i, k = 0;	for_each_phy(enabled_mask, enabled_mask, i) {		if (&asd_ha->phys[i] == phy)			return k;		k++;	}	return 0;}/** * asd_get_attached_sas_addr -- extract/generate attached SAS address * phy: pointer to asd_phy * sas_addr: pointer to buffer where the SAS address is to be written * * This function extracts the SAS address from an IDENTIFY frame * received.  If OOB is SATA, then a SAS address is generated from the * HA tables. * * LOCKING: the frame_rcvd_lock needs to be held since this parses the frame * buffer. */static inline void asd_get_attached_sas_addr(struct asd_phy *phy, u8 *sas_addr){	if (phy->sas_phy.frame_rcvd[0] == 0x34	    && phy->sas_phy.oob_mode == SATA_OOB_MODE) {		struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha;		/* FIS device-to-host */		u64 addr = be64_to_cpu(*(__be64 *)phy->phy_desc->sas_addr);		addr += asd_ha->hw_prof.sata_name_base + ord_phy(asd_ha, phy);		*(__be64 *)sas_addr = cpu_to_be64(addr);	} else {		struct sas_identify_frame *idframe =			(void *) phy->sas_phy.frame_rcvd;		memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE);	}}static 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",		    __FUNCTION__, 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 inline void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb,					   struct done_list_struct *dl,					   int edb_id, int phy_id){	unsigned long flags;	int edb_el = edb_id + ascb->edb_index;	struct asd_dma_tok *edb = ascb->ha->seq.edb_arr[edb_el];	struct asd_phy *phy = &ascb->ha->phys[phy_id];	struct sas_ha_struct *sas_ha = phy->sas_phy.ha;	u16 size = ((dl->status_block[3] & 7) << 8) | dl->status_block[2];	size = min(size, (u16) sizeof(phy->frame_rcvd));	spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);	memcpy(phy->sas_phy.frame_rcvd, edb->vaddr, size);	phy->sas_phy.frame_rcvd_size = size;	asd_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);	spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);	asd_dump_frame_rcvd(phy, dl);	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){	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];	switch (lr_error) {	case 0:		ASD_DPRINTK("phy%d: Receive ID timer expired\n", phy_id);		break;	case 1:		ASD_DPRINTK("phy%d: Loss of signal\n", phy_id);		break;	case 2:		ASD_DPRINTK("phy%d: Loss of dword sync\n", phy_id);		break;	case 3:		ASD_DPRINTK("phy%d: Receive FIS timeout\n", phy_id);		break;	default:		ASD_DPRINTK("phy%d: unknown link reset error code: 0x%x\n",			    phy_id, lr_error);		break;	}	asd_turn_led(asd_ha, phy_id, 0);	sas_phy_disconnected(sas_phy);	asd_deform_port(asd_ha, phy);	sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);	if (retries_left == 0) {		int num = 1;		struct asd_ascb *cp = asd_ascb_alloc_list(ascb->ha, &num,							  GFP_ATOMIC);		if (!cp) {			asd_printk("%s: out of memory\n", __FUNCTION__);			goto out;		}		ASD_DPRINTK("phy%d: retries:0 performing link reset seq\n",			    phy_id);		asd_build_control_phy(cp, phy_id, ENABLE_PHY);		if (asd_post_ascb_list(ascb->ha, cp, 1) != 0)			asd_ascb_free(cp);	}out:	;}static inline void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb,					      struct done_list_struct *dl,					      int phy_id){	unsigned long flags;	struct sas_ha_struct *sas_ha = &ascb->ha->sas_ha;	struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];	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);	reg &= ~3;	switch (reg) {	case LmPRMSTAT0BYTE0:		switch (cont) {		case LmBROADCH:		case LmBROADRVCH0:		case LmBROADRVCH1:		case LmBROADSES:			ASD_DPRINTK("phy%d: BROADCAST change received:%d\n",				    phy_id, cont);			spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);			sas_phy->sas_prim = ffs(cont);			spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);			sas_ha->notify_port_event(sas_phy,PORTE_BROADCAST_RCVD);			break;		case LmUNKNOWNP:			ASD_DPRINTK("phy%d: unknown BREAK\n", phy_id);			break;		default:			ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n",				    phy_id, reg, cont);			break;		}		break;	case LmPRMSTAT1BYTE0:		switch (cont) {		case LmHARDRST:			ASD_DPRINTK("phy%d: HARD_RESET primitive rcvd\n",				    phy_id);			/* The sequencer disables all phys on that port.			 * We have to re-enable the phys ourselves. */			asd_deform_port(asd_ha, phy);			sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET);			break;		default:			ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n",				    phy_id, reg, cont);			break;		}		break;	default:		ASD_DPRINTK("unknown primitive register:0x%x\n",			    dl->status_block[1]);		break;	}}/** * asd_invalidate_edb -- invalidate an EDB and if necessary post the ESCB * @ascb: pointer to Empty SCB * @edb_id: index [0,6] to the empty data buffer which is to be invalidated * * After an EDB has been invalidated, if all EDBs in this ESCB have been * invalidated, the ESCB is posted back to the sequencer. * Context is tasklet/IRQ. */void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id){	struct asd_seq_data *seq = &ascb->ha->seq;	struct empty_scb *escb = &ascb->scb->escb;	struct sg_el     *eb   = &escb->eb[edb_id];	struct asd_dma_tok *edb = seq->edb_arr[ascb->edb_index + edb_id];	memset(edb->vaddr, 0, ASD_EDB_SIZE);	eb->flags |= ELEMENT_NOT_VALID;	escb->num_valid--;	if (escb->num_valid == 0) {		int i;		/* ASD_DPRINTK("reposting escb: vaddr: 0x%p, "			    "dma_handle: 0x%08llx, next: 0x%08llx, "			    "index:%d, opcode:0x%02x\n",			    ascb->dma_scb.vaddr,			    (u64)ascb->dma_scb.dma_handle,			    le64_to_cpu(ascb->scb->header.next_scb),			    le16_to_cpu(ascb->scb->header.index),			    ascb->scb->header.opcode);		*/		escb->num_valid = ASD_EDBS_PER_SCB;		for (i = 0; i < ASD_EDBS_PER_SCB; i++)			escb->eb[i].flags = 0;		if (!list_empty(&ascb->list))			list_del_init(&ascb->list);		i = asd_post_escb_list(ascb->ha, ascb, 1);		if (i)			asd_printk("couldn't post escb, err:%d\n", i);	}}static void escb_tasklet_complete(struct asd_ascb *ascb,				  struct done_list_struct *dl){	struct asd_ha_struct *asd_ha = ascb->ha;	struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;	int edb = (dl->opcode & DL_PHY_MASK) - 1; /* [0xc1,0xc7] -> [0,6] */	u8  sb_opcode = dl->status_block[0];	int phy_id = sb_opcode & DL_PHY_MASK;	struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];	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",			    edb, dl->opcode);		ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n",			    sb_opcode, phy_id);		ASD_DPRINTK("escb: vaddr: 0x%p, "			    "dma_handle: 0x%llx, next: 0x%llx, "			    "index:%d, opcode:0x%02x\n",			    ascb->dma_scb.vaddr,			    (unsigned long long)ascb->dma_scb.dma_handle,			    (unsigned long long)			    le64_to_cpu(ascb->scb->header.next_scb),			    le16_to_cpu(ascb->scb->header.index),			    ascb->scb->header.opcode);	}	/* 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",			    __FUNCTION__, 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 = ascb->uldd_task;			if (task && a->tc_index == tc_abort) {				failed_dev = task->dev;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -