📄 aic94xx_hwi.c
字号:
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 + -