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

📄 aic94xx_hwi.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
		asd_printk("hmm, EXSI interrupted but no error?\n");		return;	}	if (stat0r & ASIFMTERR) {		asd_printk("ASI SEEPROM format error for %s\n",			   pci_name(asd_ha->pcidev));	} else if (stat0r & ASISEECHKERR) {		u32 stat1r = asd_read_reg_dword(asd_ha, ASISTAT1R);		asd_printk("ASI SEEPROM checksum 0x%x error for %s\n",			   stat1r & CHECKSUM_MASK,			   pci_name(asd_ha->pcidev));	} else {		u32 statr = asd_read_reg_dword(asd_ha, ASIERRSTATR);		if (!(statr & CPI2ASIMSTERR_MASK)) {			ASD_DPRINTK("hmm, ASIERR?\n");			return;		} else {			u32 addr = asd_read_reg_dword(asd_ha, ASIERRADDR);			u32 data = asd_read_reg_dword(asd_ha, ASIERRDATAR);			asd_printk("%s: CPI2 xfer err: addr: 0x%x, wdata: 0x%x, "				   "count: 0x%x, byteen: 0x%x, targerr: 0x%x "				   "master id: 0x%x, master err: 0x%x\n",				   pci_name(asd_ha->pcidev),				   addr, data,				   (statr & CPI2ASIBYTECNT_MASK) >> 16,				   (statr & CPI2ASIBYTEEN_MASK) >> 12,				   (statr & CPI2ASITARGERR_MASK) >> 8,				   (statr & CPI2ASITARGMID_MASK) >> 4,				   (statr & CPI2ASIMSTERR_MASK));		}	}	asd_chip_reset(asd_ha);}/** * asd_hst_pcix_isr -- process host interface interrupts * @asd_ha: pointer to host adapter structure * * Asserted on PCIX errors: target abort, etc. */static inline void asd_hst_pcix_isr(struct asd_ha_struct *asd_ha){	u16 status;	u32 pcix_status;	u32 ecc_status;	pci_read_config_word(asd_ha->pcidev, PCI_STATUS, &status);	pci_read_config_dword(asd_ha->pcidev, PCIX_STATUS, &pcix_status);	pci_read_config_dword(asd_ha->pcidev, ECC_CTRL_STAT, &ecc_status);	if (status & PCI_STATUS_DETECTED_PARITY)		asd_printk("parity error for %s\n", pci_name(asd_ha->pcidev));	else if (status & PCI_STATUS_REC_MASTER_ABORT)		asd_printk("master abort for %s\n", pci_name(asd_ha->pcidev));	else if (status & PCI_STATUS_REC_TARGET_ABORT)		asd_printk("target abort for %s\n", pci_name(asd_ha->pcidev));	else if (status & PCI_STATUS_PARITY)		asd_printk("data parity for %s\n", pci_name(asd_ha->pcidev));	else if (pcix_status & RCV_SCE) {		asd_printk("received split completion error for %s\n",			   pci_name(asd_ha->pcidev));		pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status);		/* XXX: Abort task? */		return;	} else if (pcix_status & UNEXP_SC) {		asd_printk("unexpected split completion for %s\n",			   pci_name(asd_ha->pcidev));		pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status);		/* ignore */		return;	} else if (pcix_status & SC_DISCARD)		asd_printk("split completion discarded for %s\n",			   pci_name(asd_ha->pcidev));	else if (ecc_status & UNCOR_ECCERR)		asd_printk("uncorrectable ECC error for %s\n",			   pci_name(asd_ha->pcidev));	asd_chip_reset(asd_ha);}/** * asd_hw_isr -- host adapter interrupt service routine * @irq: ignored * @dev_id: pointer to host adapter structure * * The ISR processes done list entries and level 3 error handling. */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);	if (!chimint)		return IRQ_NONE;	asd_write_reg_dword(asd_ha, CHIMINT, chimint);	(void) asd_read_reg_dword(asd_ha, CHIMINT);	if (chimint & DLAVAIL)		asd_process_donelist_isr(asd_ha);	if (chimint & COMINT)		asd_com_sas_isr(asd_ha);	if (chimint & DEVINT)		asd_dch_sas_isr(asd_ha);	if (chimint & INITERR)		asd_rbi_exsi_isr(asd_ha);	if (chimint & HOSTERR)		asd_hst_pcix_isr(asd_ha);	return IRQ_HANDLED;}/* ---------- SCB handling ---------- */static inline struct asd_ascb *asd_ascb_alloc(struct asd_ha_struct *asd_ha,					      gfp_t gfp_flags){	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_zalloc(asd_ascb_cache, gfp_flags);	if (ascb) {		ascb->dma_scb.size = sizeof(struct scb);		ascb->dma_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool,						     gfp_flags,						    &ascb->dma_scb.dma_handle);		if (!ascb->dma_scb.vaddr) {			kmem_cache_free(asd_ascb_cache, ascb);			return NULL;		}		memset(ascb->dma_scb.vaddr, 0, sizeof(struct scb));		asd_init_ascb(asd_ha, ascb);		spin_lock_irqsave(&seq->tc_index_lock, flags);		ascb->tc_index = asd_tc_index_get(seq, ascb);		spin_unlock_irqrestore(&seq->tc_index_lock, flags);		if (ascb->tc_index == -1)			goto undo;		ascb->scb->header.index = cpu_to_le16((u16)ascb->tc_index);	}	return ascb;undo:	dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr,		      ascb->dma_scb.dma_handle);	kmem_cache_free(asd_ascb_cache, ascb);	ASD_DPRINTK("no index for ascb\n");	return NULL;}/** * asd_ascb_alloc_list -- allocate a list of aSCBs * @asd_ha: pointer to host adapter structure * @num: pointer to integer number of aSCBs * @gfp_flags: GFP_ flags. * * This is the only function which is used to allocate aSCBs. * It can allocate one or many. If more than one, then they form * a linked list in two ways: by their list field of the ascb struct * and by the next_scb field of the scb_header. * * Returns NULL if no memory was available, else pointer to a list * of ascbs.  When this function returns, @num would be the number * of SCBs which were not able to be allocated, 0 if all requested * were able to be allocated. */struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct				     *asd_ha, int *num,				     gfp_t gfp_flags){	struct asd_ascb *first = NULL;	for ( ; *num > 0; --*num) {		struct asd_ascb *ascb = asd_ascb_alloc(asd_ha, gfp_flags);		if (!ascb)			break;		else if (!first)			first = ascb;		else {			struct asd_ascb *last = list_entry(first->list.prev,							   struct asd_ascb,							   list);			list_add_tail(&ascb->list, &first->list);			last->scb->header.next_scb =				cpu_to_le64(((u64)ascb->dma_scb.dma_handle));		}	}	return first;}/** * asd_swap_head_scb -- swap the head scb * @asd_ha: pointer to host adapter structure * @ascb: pointer to the head of an ascb list * * The sequencer knows the DMA address of the next SCB to be DMAed to * the host adapter, from initialization or from the last list DMAed. * seq->next_scb keeps the address of this SCB.  The sequencer will * DMA to the host adapter this list of SCBs.  But the head (first * element) of this list is not known to the sequencer.  Here we swap * the head of the list with the known SCB (memcpy()). * Only one memcpy() is required per list so it is in our interest * to keep the list of SCB as long as possible so that the ratio * of number of memcpy calls to the number of SCB DMA-ed is as small * as possible. * * 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){	struct asd_seq_data *seq = &asd_ha->seq;	struct asd_ascb *last = list_entry(ascb->list.prev,					   struct asd_ascb,					   list);	struct asd_dma_tok t = ascb->dma_scb;	memcpy(seq->next_scb.vaddr, ascb->scb, sizeof(*ascb->scb));	ascb->dma_scb = seq->next_scb;	ascb->scb = ascb->dma_scb.vaddr;	seq->next_scb = t;	last->scb->header.next_scb =		cpu_to_le64(((u64)seq->next_scb.dma_handle));}/** * asd_start_timers -- (add and) start timers of SCBs * @list: pointer to struct list_head of the scbs * @to: timeout in jiffies * * If an SCB in the @list has no timer function, assign the default * one,  then start the timer of the SCB.  This function is * 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){	struct asd_ascb *ascb;	list_for_each_entry(ascb, list, list) {		if (!ascb->uldd_timer) {			ascb->timer.data = (unsigned long) ascb;			ascb->timer.function = asd_ascb_timedout;			ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;			add_timer(&ascb->timer);		}	}}/** * asd_post_ascb_list -- post a list of 1 or more aSCBs to the host adapter * @asd_ha: pointer to a host adapter structure * @ascb: pointer to the first aSCB in the list * @num: number of aSCBs in the list (to be posted) * * See queueing comment in asd_post_escb_list(). * * Additional note on queuing: In order to minimize the ratio of memcpy() * to the number of ascbs sent, we try to batch-send as many ascbs as possible * in one go. * Two cases are possible: *    A) can_queue >= num, *    B) can_queue < num. * Case A: we can send the whole batch at once.  Increment "pending" * in the beginning of this function, when it is checked, in order to * eliminate races when this function is called by multiple processes. * Case B: should never happen if the managing layer considers * lldd_queue_size. */int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,		       int num){	unsigned long flags;	LIST_HEAD(list);	int can_queue;	spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);	can_queue = asd_ha->hw_prof.max_scbs - asd_ha->seq.pending;	if (can_queue >= num)		asd_ha->seq.pending += num;	else		can_queue = 0;	if (!can_queue) {		spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);		asd_printk("%s: scb queue full\n", pci_name(asd_ha->pcidev));		return -SAS_QUEUE_FULL;	}	asd_swap_head_scb(asd_ha, ascb);	__list_add(&list, ascb->list.prev, &ascb->list);	asd_start_scb_timers(&list);	asd_ha->seq.scbpro += num;	list_splice_init(&list, asd_ha->seq.pend_q.prev);	asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro);	spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);	return 0;}/** * asd_post_escb_list -- post a list of 1 or more empty scb * @asd_ha: pointer to a host adapter structure * @ascb: pointer to the first empty SCB in the list * @num: number of aSCBs in the list (to be posted) * * This is essentially the same as asd_post_ascb_list, but we do not * increment pending, add those to the pending list or get indexes. * See asd_init_escbs() and asd_init_post_escbs(). * * Since sending a list of ascbs is a superset of sending a single * ascb, this function exists to generalize this.  More specifically, * when sending a list of those, we want to do only a _single_ * memcpy() at swap head, as opposed to for each ascb sent (in the * case of sending them one by one).  That is, we want to minimize the * ratio of memcpy() operations to the number of ascbs sent.  The same * logic applies to asd_post_ascb_list(). */int asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,		       int num){	unsigned long flags;	spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);	asd_swap_head_scb(asd_ha, ascb);	asd_ha->seq.scbpro += num;	asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro);	spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);	return 0;}/* ---------- LED ---------- *//** * asd_turn_led -- turn on/off an LED * @asd_ha: pointer to host adapter structure * @phy_id: the PHY id whose LED we want to manupulate * @op: 1 to turn on, 0 to turn off */void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op){	if (phy_id < ASD_MAX_PHYS) {		u32 v = asd_read_reg_dword(asd_ha, LmCONTROL(phy_id));		if (op)			v |= LEDPOL;		else			v &= ~LEDPOL;		asd_write_reg_dword(asd_ha, LmCONTROL(phy_id), v);	}}/** * asd_control_led -- enable/disable an LED on the board * @asd_ha: pointer to host adapter structure * @phy_id: integer, the phy id * @op: integer, 1 to enable, 0 to disable the LED * * First we output enable the LED, then we set the source * to be an external module. */void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op){	if (phy_id < ASD_MAX_PHYS) {		u32 v;		v = asd_read_reg_dword(asd_ha, GPIOOER);		if (op)			v |= (1 << phy_id);		else			v &= ~(1 << phy_id);		asd_write_reg_dword(asd_ha, GPIOOER, v);		v = asd_read_reg_dword(asd_ha, GPIOCNFGR);		if (op)			v |= (1 << phy_id);		else			v &= ~(1 << phy_id);		asd_write_reg_dword(asd_ha, GPIOCNFGR, v);	}}/* ---------- PHY enable ---------- */static int asd_enable_phy(struct asd_ha_struct *asd_ha, int phy_id){	struct asd_phy *phy = &asd_ha->phys[phy_id];	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, INT_ENABLE_2), 0);	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, HOT_PLUG_DELAY),			   HOTPLUG_DELAY_TIMEOUT);	/* Get defaults from manuf. sector */	/* XXX we need defaults for those in case MS is broken. */	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_0),			   phy->phy_desc->phy_control_0);	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_1),			   phy->phy_desc->phy_control_1);	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_2),			   phy->phy_desc->phy_control_2);	asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_3),			   phy->phy_desc->phy_control_3);	asd_write_reg_dword(asd_ha, LmSEQ_TEN_MS_COMINIT_TIMEOUT(phy_id),			    ASD_COMINIT_TIMEOUT);	asd_write_reg_addr(asd_ha, LmSEQ_TX_ID_ADDR_FRAME(phy_id),			   phy->id_frm_tok->dma_handle);	asd_control_led(asd_ha, phy_id, 1);	return 0;}int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask){	u8  phy_m;	u8  i;	int num = 0, k;	struct asd_ascb *ascb;	struct asd_ascb *ascb_list;	if (!phy_mask) {		asd_printk("%s called with phy_mask of 0!?\n", __FUNCTION__);		return 0;	}	for_each_phy(phy_mask, phy_m, i) {		num++;		asd_enable_phy(asd_ha, i);	}	k = num;	ascb_list = asd_ascb_alloc_list(asd_ha, &k, GFP_KERNEL);	if (!ascb_list) {		asd_printk("no memory for control phy ascb list\n");		return -ENOMEM;	}	num -= k;	ascb = ascb_list;	for_each_phy(phy_mask, phy_m, i) {		asd_build_control_phy(ascb, i, ENABLE_PHY);		ascb = list_entry(ascb->list.next, struct asd_ascb, list);	}	ASD_DPRINTK("posting %d control phy scbs\n", num);	k = asd_post_ascb_list(asd_ha, ascb_list, num);	if (k)		asd_ascb_free_list(ascb_list);	return k;}

⌨️ 快捷键说明

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