arcmsr_hba.c

来自「linux 内核源代码」· C语言 代码 · 共 2,271 行 · 第 1/5 页

C
2,271
字号
	error = arcmsr_alloc_sysfs_attr(acb);	if (error)		goto out_free_sysfs;	scsi_scan_host(host);	#ifdef CONFIG_SCSI_ARCMSR_AER	pci_enable_pcie_error_reporting(pdev);	#endif	return 0; out_free_sysfs: out_free_irq:	free_irq(pdev->irq, acb); out_free_ccb_pool:	arcmsr_free_ccb_pool(acb); out_release_regions:	pci_release_regions(pdev); out_host_put:	scsi_host_put(host); out_disable_device:	pci_disable_device(pdev); out:	return error;}static uint8_t arcmsr_hba_wait_msgint_ready(struct AdapterControlBlock *acb){	struct MessageUnit_A __iomem *reg = acb->pmuA;	uint32_t Index;	uint8_t Retries = 0x00;	do {		for (Index = 0; Index < 100; Index++) {			if (readl(&reg->outbound_intstatus) &					ARCMSR_MU_OUTBOUND_MESSAGE0_INT) {				writel(ARCMSR_MU_OUTBOUND_MESSAGE0_INT,					&reg->outbound_intstatus);				return 0x00;			}			msleep(10);		}/*max 1 seconds*/	} while (Retries++ < 20);/*max 20 sec*/	return 0xff;}static uint8_t arcmsr_hbb_wait_msgint_ready(struct AdapterControlBlock *acb){	struct MessageUnit_B *reg = acb->pmuB;	uint32_t Index;	uint8_t Retries = 0x00;	do {		for (Index = 0; Index < 100; Index++) {			if (readl(reg->iop2drv_doorbell_reg)				& ARCMSR_IOP2DRV_MESSAGE_CMD_DONE) {				writel(ARCMSR_MESSAGE_INT_CLEAR_PATTERN					, reg->iop2drv_doorbell_reg);				return 0x00;			}			msleep(10);		}/*max 1 seconds*/	} while (Retries++ < 20);/*max 20 sec*/	return 0xff;}static void arcmsr_abort_hba_allcmd(struct AdapterControlBlock *acb){	struct MessageUnit_A __iomem *reg = acb->pmuA;	writel(ARCMSR_INBOUND_MESG0_ABORT_CMD, &reg->inbound_msgaddr0);	if (arcmsr_hba_wait_msgint_ready(acb))		printk(KERN_NOTICE			"arcmsr%d: wait 'abort all outstanding command' timeout \n"			, acb->host->host_no);}static void arcmsr_abort_hbb_allcmd(struct AdapterControlBlock *acb){	struct MessageUnit_B *reg = acb->pmuB;	writel(ARCMSR_MESSAGE_ABORT_CMD, reg->drv2iop_doorbell_reg);	if (arcmsr_hbb_wait_msgint_ready(acb))		printk(KERN_NOTICE			"arcmsr%d: wait 'abort all outstanding command' timeout \n"			, acb->host->host_no);}static void arcmsr_abort_allcmd(struct AdapterControlBlock *acb){	switch (acb->adapter_type) {	case ACB_ADAPTER_TYPE_A: {		arcmsr_abort_hba_allcmd(acb);		}		break;	case ACB_ADAPTER_TYPE_B: {		arcmsr_abort_hbb_allcmd(acb);		}	}}static void arcmsr_pci_unmap_dma(struct CommandControlBlock *ccb){	struct scsi_cmnd *pcmd = ccb->pcmd;	scsi_dma_unmap(pcmd);}static void arcmsr_ccb_complete(struct CommandControlBlock *ccb, int stand_flag){	struct AdapterControlBlock *acb = ccb->acb;	struct scsi_cmnd *pcmd = ccb->pcmd;	arcmsr_pci_unmap_dma(ccb);	if (stand_flag == 1)		atomic_dec(&acb->ccboutstandingcount);	ccb->startdone = ARCMSR_CCB_DONE;	ccb->ccb_flags = 0;	list_add_tail(&ccb->list, &acb->ccb_free_list);	pcmd->scsi_done(pcmd);}static void arcmsr_flush_hba_cache(struct AdapterControlBlock *acb){	struct MessageUnit_A __iomem *reg = acb->pmuA;	int retry_count = 30;	writel(ARCMSR_INBOUND_MESG0_FLUSH_CACHE, &reg->inbound_msgaddr0);	do {		if (!arcmsr_hba_wait_msgint_ready(acb))			break;		else {			retry_count--;			printk(KERN_NOTICE "arcmsr%d: wait 'flush adapter cache' \			timeout, retry count down = %d \n", acb->host->host_no, retry_count);		}	} while (retry_count != 0);}static void arcmsr_flush_hbb_cache(struct AdapterControlBlock *acb){	struct MessageUnit_B *reg = acb->pmuB;	int retry_count = 30;	writel(ARCMSR_MESSAGE_FLUSH_CACHE, reg->drv2iop_doorbell_reg);	do {		if (!arcmsr_hbb_wait_msgint_ready(acb))			break;		else {			retry_count--;			printk(KERN_NOTICE "arcmsr%d: wait 'flush adapter cache' \			timeout,retry count down = %d \n", acb->host->host_no, retry_count);		}	} while (retry_count != 0);}static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb){	switch (acb->adapter_type) {	case ACB_ADAPTER_TYPE_A: {		arcmsr_flush_hba_cache(acb);		}		break;	case ACB_ADAPTER_TYPE_B: {		arcmsr_flush_hbb_cache(acb);		}	}}static void arcmsr_report_sense_info(struct CommandControlBlock *ccb){	struct scsi_cmnd *pcmd = ccb->pcmd;	struct SENSE_DATA *sensebuffer = (struct SENSE_DATA *)pcmd->sense_buffer;	pcmd->result = DID_OK << 16;	if (sensebuffer) {		int sense_data_length =			sizeof(struct SENSE_DATA) < sizeof(pcmd->sense_buffer)			? sizeof(struct SENSE_DATA) : sizeof(pcmd->sense_buffer);		memset(sensebuffer, 0, sizeof(pcmd->sense_buffer));		memcpy(sensebuffer, ccb->arcmsr_cdb.SenseData, sense_data_length);		sensebuffer->ErrorCode = SCSI_SENSE_CURRENT_ERRORS;		sensebuffer->Valid = 1;	}}static u32 arcmsr_disable_outbound_ints(struct AdapterControlBlock *acb){	u32 orig_mask = 0;	switch (acb->adapter_type) {	case ACB_ADAPTER_TYPE_A : {		struct MessageUnit_A __iomem *reg = acb->pmuA;		orig_mask = readl(&reg->outbound_intmask)|\				ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE;		writel(orig_mask|ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE, \						&reg->outbound_intmask);		}		break;	case ACB_ADAPTER_TYPE_B : {		struct MessageUnit_B *reg = acb->pmuB;		orig_mask = readl(reg->iop2drv_doorbell_mask_reg) & \					(~ARCMSR_IOP2DRV_MESSAGE_CMD_DONE);		writel(0, reg->iop2drv_doorbell_mask_reg);		}		break;	}	return orig_mask;}static void arcmsr_report_ccb_state(struct AdapterControlBlock *acb, \			struct CommandControlBlock *ccb, uint32_t flag_ccb){	uint8_t id, lun;	id = ccb->pcmd->device->id;	lun = ccb->pcmd->device->lun;	if (!(flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR)) {		if (acb->devstate[id][lun] == ARECA_RAID_GONE)			acb->devstate[id][lun] = ARECA_RAID_GOOD;			ccb->pcmd->result = DID_OK << 16;			arcmsr_ccb_complete(ccb, 1);	} else {		switch (ccb->arcmsr_cdb.DeviceStatus) {		case ARCMSR_DEV_SELECT_TIMEOUT: {			acb->devstate[id][lun] = ARECA_RAID_GONE;			ccb->pcmd->result = DID_NO_CONNECT << 16;			arcmsr_ccb_complete(ccb, 1);			}			break;		case ARCMSR_DEV_ABORTED:		case ARCMSR_DEV_INIT_FAIL: {			acb->devstate[id][lun] = ARECA_RAID_GONE;			ccb->pcmd->result = DID_BAD_TARGET << 16;			arcmsr_ccb_complete(ccb, 1);			}			break;		case ARCMSR_DEV_CHECK_CONDITION: {			acb->devstate[id][lun] = ARECA_RAID_GOOD;			arcmsr_report_sense_info(ccb);			arcmsr_ccb_complete(ccb, 1);			}			break;		default:				printk(KERN_NOTICE					"arcmsr%d: scsi id = %d lun = %d"					" isr get command error done, "					"but got unknown DeviceStatus = 0x%x \n"					, acb->host->host_no					, id					, lun					, ccb->arcmsr_cdb.DeviceStatus);					acb->devstate[id][lun] = ARECA_RAID_GONE;					ccb->pcmd->result = DID_NO_CONNECT << 16;					arcmsr_ccb_complete(ccb, 1);			break;		}	}}static void arcmsr_drain_donequeue(struct AdapterControlBlock *acb, uint32_t flag_ccb){	struct CommandControlBlock *ccb;	ccb = (struct CommandControlBlock *)(acb->vir2phy_offset + (flag_ccb << 5));	if ((ccb->acb != acb) || (ccb->startdone != ARCMSR_CCB_START)) {		if (ccb->startdone == ARCMSR_CCB_ABORTED) {			struct scsi_cmnd *abortcmd = ccb->pcmd;			if (abortcmd) {				abortcmd->result |= DID_ABORT << 16;				arcmsr_ccb_complete(ccb, 1);				printk(KERN_NOTICE "arcmsr%d: ccb ='0x%p' \				isr got aborted command \n", acb->host->host_no, ccb);			}		}		printk(KERN_NOTICE "arcmsr%d: isr get an illegal ccb command \				done acb = '0x%p'"				"ccb = '0x%p' ccbacb = '0x%p' startdone = 0x%x"				" ccboutstandingcount = %d \n"				, acb->host->host_no				, acb				, ccb				, ccb->acb				, ccb->startdone				, atomic_read(&acb->ccboutstandingcount));		}	arcmsr_report_ccb_state(acb, ccb, flag_ccb);}static void arcmsr_done4abort_postqueue(struct AdapterControlBlock *acb){	int i = 0;	uint32_t flag_ccb;	switch (acb->adapter_type) {	case ACB_ADAPTER_TYPE_A: {		struct MessageUnit_A __iomem *reg = acb->pmuA;		uint32_t outbound_intstatus;		outbound_intstatus = readl(&reg->outbound_intstatus) &					acb->outbound_int_enable;		/*clear and abort all outbound posted Q*/		writel(outbound_intstatus, &reg->outbound_intstatus);/*clear interrupt*/		while (((flag_ccb = readl(&reg->outbound_queueport)) != 0xFFFFFFFF)				&& (i++ < ARCMSR_MAX_OUTSTANDING_CMD)) {			arcmsr_drain_donequeue(acb, flag_ccb);		}		}		break;	case ACB_ADAPTER_TYPE_B: {		struct MessageUnit_B *reg = acb->pmuB;		/*clear all outbound posted Q*/		for (i = 0; i < ARCMSR_MAX_HBB_POSTQUEUE; i++) {			if ((flag_ccb = readl(&reg->done_qbuffer[i])) != 0) {				writel(0, &reg->done_qbuffer[i]);				arcmsr_drain_donequeue(acb, flag_ccb);			}			writel(0, &reg->post_qbuffer[i]);		}		reg->doneq_index = 0;		reg->postq_index = 0;		}		break;	}}static void arcmsr_remove(struct pci_dev *pdev){	struct Scsi_Host *host = pci_get_drvdata(pdev);	struct AdapterControlBlock *acb =		(struct AdapterControlBlock *) host->hostdata;	int poll_count = 0;	arcmsr_free_sysfs_attr(acb);	scsi_remove_host(host);	arcmsr_stop_adapter_bgrb(acb);	arcmsr_flush_adapter_cache(acb);	arcmsr_disable_outbound_ints(acb);	acb->acb_flags |= ACB_F_SCSISTOPADAPTER;	acb->acb_flags &= ~ACB_F_IOP_INITED;	for (poll_count = 0; poll_count < ARCMSR_MAX_OUTSTANDING_CMD; poll_count++) {		if (!atomic_read(&acb->ccboutstandingcount))			break;		arcmsr_interrupt(acb);/* FIXME: need spinlock */		msleep(25);	}	if (atomic_read(&acb->ccboutstandingcount)) {		int i;		arcmsr_abort_allcmd(acb);		arcmsr_done4abort_postqueue(acb);		for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {			struct CommandControlBlock *ccb = acb->pccb_pool[i];			if (ccb->startdone == ARCMSR_CCB_START) {				ccb->startdone = ARCMSR_CCB_ABORTED;				ccb->pcmd->result = DID_ABORT << 16;				arcmsr_ccb_complete(ccb, 1);			}		}	}	free_irq(pdev->irq, acb);	arcmsr_free_ccb_pool(acb);	pci_release_regions(pdev);	scsi_host_put(host);	pci_disable_device(pdev);	pci_set_drvdata(pdev, NULL);}static void arcmsr_shutdown(struct pci_dev *pdev){	struct Scsi_Host *host = pci_get_drvdata(pdev);	struct AdapterControlBlock *acb =		(struct AdapterControlBlock *)host->hostdata;	arcmsr_stop_adapter_bgrb(acb);	arcmsr_flush_adapter_cache(acb);}static int arcmsr_module_init(void){	int error = 0;	error = pci_register_driver(&arcmsr_pci_driver);	return error;}static void arcmsr_module_exit(void){	pci_unregister_driver(&arcmsr_pci_driver);}module_init(arcmsr_module_init);module_exit(arcmsr_module_exit);static void arcmsr_enable_outbound_ints(struct AdapterControlBlock *acb, \						u32 intmask_org){	u32 mask;	switch (acb->adapter_type) {	case ACB_ADAPTER_TYPE_A : {		struct MessageUnit_A __iomem *reg = acb->pmuA;		mask = intmask_org & ~(ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE |			     ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE);		writel(mask, &reg->outbound_intmask);		acb->outbound_int_enable = ~(intmask_org & mask) & 0x000000ff;		}		break;	case ACB_ADAPTER_TYPE_B : {		struct MessageUnit_B *reg = acb->pmuB;		mask = intmask_org | (ARCMSR_IOP2DRV_DATA_WRITE_OK | \			ARCMSR_IOP2DRV_DATA_READ_OK | ARCMSR_IOP2DRV_CDB_DONE);		writel(mask, reg->iop2drv_doorbell_mask_reg);		acb->outbound_int_enable = (intmask_org | mask) & 0x0000000f;		}	}}static void arcmsr_build_ccb(struct AdapterControlBlock *acb,	struct CommandControlBlock *ccb, struct scsi_cmnd *pcmd){	struct ARCMSR_CDB *arcmsr_cdb = (struct ARCMSR_CDB *)&ccb->arcmsr_cdb;	int8_t *psge = (int8_t *)&arcmsr_cdb->u;	__le32 address_lo, address_hi;	int arccdbsize = 0x30;	int nseg;	ccb->pcmd = pcmd;	memset(arcmsr_cdb, 0, sizeof(struct ARCMSR_CDB));	arcmsr_cdb->Bus = 0;	arcmsr_cdb->TargetID = pcmd->device->id;	arcmsr_cdb->LUN = pcmd->device->lun;	arcmsr_cdb->Function = 1;	arcmsr_cdb->CdbLength = (uint8_t)pcmd->cmd_len;	arcmsr_cdb->Context = (unsigned long)arcmsr_cdb;	memcpy(arcmsr_cdb->Cdb, pcmd->cmnd, pcmd->cmd_len);	nseg = scsi_dma_map(pcmd);	BUG_ON(nseg < 0);

⌨️ 快捷键说明

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