initio.c

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

C
2,386
字号
	int i;	u16 chksum = 0;	u16 *np, *np1;	i91unvramp = &i91unvram;	/* Calculate checksum first */	np = (u16 *) i91udftNvRam;	for (i = 0; i < 31; i++)		chksum += *np++;	*np = chksum;	initio_se2_ew_en(base);	/* Enable write  */	np = (u16 *) i91udftNvRam;	np1 = (u16 *) i91unvramp;	for (i = 0; i < 32; i++, np++, np1++) {		if (*np != *np1)			initio_se2_wr(base, i, *np);	}	initio_se2_ew_ds(base);	/* Disable write   */}/** *	initio_read_eeprom		-	Retrieve configuration *	@base: Base of InitIO Host Adapter * *	Retrieve the host adapter configuration data from E2Prom. If the *	data is invalid then the defaults are used and are also restored *	into the E2PROM. This forms the access point for the SCSI driver *	into the E2PROM layer, the other functions for the E2PROM are all *	internal use. * *	Must be called single threaded, uses a shared global area. */static void initio_read_eeprom(unsigned long base){	u8 gctrl;	i91unvramp = &i91unvram;	/* Enable EEProm programming */	gctrl = inb(base + TUL_GCTRL);	outb(gctrl | TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL);	if (initio_se2_rd_all(base) != 1) {		initio_se2_update_all(base);	/* setup default pattern */		initio_se2_rd_all(base);	/* load again  */	}	/* Disable EEProm programming */	gctrl = inb(base + TUL_GCTRL);	outb(gctrl & ~TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL);}/** *	initio_stop_bm		-	stop bus master *	@host: InitIO we are stopping * *	Stop any pending DMA operation, aborting the DMA if neccessary */static void initio_stop_bm(struct initio_host * host){	if (inb(host->addr + TUL_XStatus) & XPEND) {	/* if DMA xfer is pending, abort DMA xfer */		outb(TAX_X_ABT | TAX_X_CLR_FIFO, host->addr + TUL_XCmd);		/* wait Abort DMA xfer done */		while ((inb(host->addr + TUL_Int) & XABT) == 0)			cpu_relax();	}	outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0);}/** *	initio_reset_scsi		-	Reset SCSI host controller *	@host: InitIO host to reset *	@seconds: Recovery time * *	Perform a full reset of the SCSI subsystem. */static int initio_reset_scsi(struct initio_host * host, int seconds){	outb(TSC_RST_BUS, host->addr + TUL_SCtrl0);	while (!((host->jsint = inb(host->addr + TUL_SInt)) & TSS_SCSIRST_INT))		cpu_relax();	/* reset tulip chip */	outb(0, host->addr + TUL_SSignal);	/* Stall for a while, wait for target's firmware ready,make it 2 sec ! */	/* SONY 5200 tape drive won't work if only stall for 1 sec */	/* FIXME: this is a very long busy wait right now */	initio_do_pause(seconds * HZ);	inb(host->addr + TUL_SInt);	return SCSI_RESET_SUCCESS;}/** *	initio_init		-	set up an InitIO host adapter *	@host: InitIO host adapter *	@num_scbs: Number of SCBS *	@bios_addr: BIOS address * *	Set up the host adapter and devices according to the configuration *	retrieved from the E2PROM. * *	Locking: Calls E2PROM layer code which is not re-enterable so must *	run single threaded for now. */static void initio_init(struct initio_host * host, u8 *bios_addr){	int i;	u8 *flags;	u8 *heads;	/* Get E2Prom configuration */	initio_read_eeprom(host->addr);	if (i91unvramp->NVM_SCSIInfo[0].NVM_NumOfTarg == 8)		host->max_tar = 8;	else		host->max_tar = 16;	host->config = i91unvramp->NVM_SCSIInfo[0].NVM_ChConfig1;	host->scsi_id = i91unvramp->NVM_SCSIInfo[0].NVM_ChSCSIID;	host->idmask = ~(1 << host->scsi_id);#ifdef CHK_PARITY	/* Enable parity error response */	outb(inb(host->addr + TUL_PCMD) | 0x40, host->addr + TUL_PCMD);#endif	/* Mask all the interrupt       */	outb(0x1F, host->addr + TUL_Mask);	initio_stop_bm(host);	/* --- Initialize the tulip --- */	outb(TSC_RST_CHIP, host->addr + TUL_SCtrl0);	/* program HBA's SCSI ID        */	outb(host->scsi_id << 4, host->addr + TUL_SScsiId);	/* Enable Initiator Mode ,phase latch,alternate sync period mode,	   disable SCSI reset */	if (host->config & HCC_EN_PAR)		host->sconf1 = (TSC_INITDEFAULT | TSC_EN_SCSI_PAR);	else		host->sconf1 = (TSC_INITDEFAULT);	outb(host->sconf1, host->addr + TUL_SConfig);	/* Enable HW reselect */	outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1);	outb(0, host->addr + TUL_SPeriod);	/* selection time out = 250 ms */	outb(153, host->addr + TUL_STimeOut);	/* Enable SCSI terminator */	outb((host->config & (HCC_ACT_TERM1 | HCC_ACT_TERM2)),		host->addr + TUL_XCtrl);	outb(((host->config & HCC_AUTO_TERM) >> 4) |		(inb(host->addr + TUL_GCTRL1) & 0xFE),		host->addr + TUL_GCTRL1);	for (i = 0,	     flags = & (i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config),	     heads = bios_addr + 0x180;	     i < host->max_tar;	     i++, flags++) {		host->targets[i].flags = *flags & ~(TCF_SYNC_DONE | TCF_WDTR_DONE);		if (host->targets[i].flags & TCF_EN_255)			host->targets[i].drv_flags = TCF_DRV_255_63;		else			host->targets[i].drv_flags = 0;		host->targets[i].js_period = 0;		host->targets[i].sconfig0 = host->sconf1;		host->targets[i].heads = *heads++;		if (host->targets[i].heads == 255)			host->targets[i].drv_flags = TCF_DRV_255_63;		else			host->targets[i].drv_flags = 0;		host->targets[i].sectors = *heads++;		host->targets[i].flags &= ~TCF_BUSY;		host->act_tags[i] = 0;		host->max_tags[i] = 0xFF;	}			/* for                          */	printk("i91u: PCI Base=0x%04X, IRQ=%d, BIOS=0x%04X0, SCSI ID=%d\n",	       host->addr, host->pci_dev->irq,	       host->bios_addr, host->scsi_id);	/* Reset SCSI Bus */	if (host->config & HCC_SCSI_RESET) {		printk(KERN_INFO "i91u: Reset SCSI Bus ... \n");		initio_reset_scsi(host, 10);	}	outb(0x17, host->addr + TUL_SCFG1);	outb(0xE9, host->addr + TUL_SIntEnable);}/** *	initio_alloc_scb		-	Allocate an SCB *	@host: InitIO host we are allocating for * *	Walk the SCB list for the controller and allocate a free SCB if *	one exists. */static struct scsi_ctrl_blk *initio_alloc_scb(struct initio_host *host){	struct scsi_ctrl_blk *scb;	unsigned long flags;	spin_lock_irqsave(&host->avail_lock, flags);	if ((scb = host->first_avail) != NULL) {#if DEBUG_QUEUE		printk("find scb at %p\n", scb);#endif		if ((host->first_avail = scb->next) == NULL)			host->last_avail = NULL;		scb->next = NULL;		scb->status = SCB_RENT;	}	spin_unlock_irqrestore(&host->avail_lock, flags);	return scb;}/** *	initio_release_scb		-	Release an SCB *	@host: InitIO host that owns the SCB *	@cmnd: SCB command block being returned * *	Return an allocated SCB to the host free list */static void initio_release_scb(struct initio_host * host, struct scsi_ctrl_blk * cmnd){	unsigned long flags;#if DEBUG_QUEUE	printk("Release SCB %p; ", cmnd);#endif	spin_lock_irqsave(&(host->avail_lock), flags);	cmnd->srb = NULL;	cmnd->status = 0;	cmnd->next = NULL;	if (host->last_avail != NULL) {		host->last_avail->next = cmnd;		host->last_avail = cmnd;	} else {		host->first_avail = cmnd;		host->last_avail = cmnd;	}	spin_unlock_irqrestore(&(host->avail_lock), flags);}/***************************************************************************/static void initio_append_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp){#if DEBUG_QUEUE	printk("Append pend SCB %p; ", scbp);#endif	scbp->status = SCB_PEND;	scbp->next = NULL;	if (host->last_pending != NULL) {		host->last_pending->next = scbp;		host->last_pending = scbp;	} else {		host->first_pending = scbp;		host->last_pending = scbp;	}}/***************************************************************************/static void initio_push_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp){#if DEBUG_QUEUE	printk("Push pend SCB %p; ", scbp);#endif	scbp->status = SCB_PEND;	if ((scbp->next = host->first_pending) != NULL) {		host->first_pending = scbp;	} else {		host->first_pending = scbp;		host->last_pending = scbp;	}}static struct scsi_ctrl_blk *initio_find_first_pend_scb(struct initio_host * host){	struct scsi_ctrl_blk *first;	first = host->first_pending;	while (first != NULL) {		if (first->opcode != ExecSCSI)			return first;		if (first->tagmsg == 0) {			if ((host->act_tags[first->target] == 0) &&			    !(host->targets[first->target].flags & TCF_BUSY))				return first;		} else {			if ((host->act_tags[first->target] >=			  host->max_tags[first->target]) |			    (host->targets[first->target].flags & TCF_BUSY)) {				first = first->next;				continue;			}			return first;		}		first = first->next;	}	return first;}static void initio_unlink_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scb){	struct scsi_ctrl_blk *tmp, *prev;#if DEBUG_QUEUE	printk("unlink pend SCB %p; ", scb);#endif	prev = tmp = host->first_pending;	while (tmp != NULL) {		if (scb == tmp) {	/* Unlink this SCB              */			if (tmp == host->first_pending) {				if ((host->first_pending = tmp->next) == NULL)					host->last_pending = NULL;			} else {				prev->next = tmp->next;				if (tmp == host->last_pending)					host->last_pending = prev;			}			tmp->next = NULL;			break;		}		prev = tmp;		tmp = tmp->next;	}}static void initio_append_busy_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp){#if DEBUG_QUEUE	printk("append busy SCB %p; ", scbp);#endif	if (scbp->tagmsg)		host->act_tags[scbp->target]++;	else		host->targets[scbp->target].flags |= TCF_BUSY;	scbp->status = SCB_BUSY;	scbp->next = NULL;	if (host->last_busy != NULL) {		host->last_busy->next = scbp;		host->last_busy = scbp;	} else {		host->first_busy = scbp;		host->last_busy = scbp;	}}/***************************************************************************/static struct scsi_ctrl_blk *initio_pop_busy_scb(struct initio_host * host){	struct scsi_ctrl_blk *tmp;	if ((tmp = host->first_busy) != NULL) {		if ((host->first_busy = tmp->next) == NULL)			host->last_busy = NULL;		tmp->next = NULL;		if (tmp->tagmsg)			host->act_tags[tmp->target]--;		else			host->targets[tmp->target].flags &= ~TCF_BUSY;	}#if DEBUG_QUEUE	printk("Pop busy SCB %p; ", tmp);#endif	return tmp;}/***************************************************************************/static void initio_unlink_busy_scb(struct initio_host * host, struct scsi_ctrl_blk * scb){	struct scsi_ctrl_blk *tmp, *prev;#if DEBUG_QUEUE	printk("unlink busy SCB %p; ", scb);#endif	prev = tmp = host->first_busy;	while (tmp != NULL) {		if (scb == tmp) {	/* Unlink this SCB              */			if (tmp == host->first_busy) {				if ((host->first_busy = tmp->next) == NULL)					host->last_busy = NULL;			} else {				prev->next = tmp->next;				if (tmp == host->last_busy)					host->last_busy = prev;			}			tmp->next = NULL;			if (tmp->tagmsg)				host->act_tags[tmp->target]--;			else				host->targets[tmp->target].flags &= ~TCF_BUSY;			break;		}		prev = tmp;		tmp = tmp->next;	}	return;}struct scsi_ctrl_blk *initio_find_busy_scb(struct initio_host * host, u16 tarlun){	struct scsi_ctrl_blk *tmp, *prev;	u16 scbp_tarlun;	prev = tmp = host->first_busy;	while (tmp != NULL) {		scbp_tarlun = (tmp->lun << 8) | (tmp->target);		if (scbp_tarlun == tarlun) {	/* Unlink this SCB              */			break;		}		prev = tmp;		tmp = tmp->next;	}#if DEBUG_QUEUE	printk("find busy SCB %p; ", tmp);#endif	return tmp;}static void initio_append_done_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp){#if DEBUG_QUEUE	printk("append done SCB %p; ", scbp);#endif	scbp->status = SCB_DONE;	scbp->next = NULL;	if (host->last_done != NULL) {		host->last_done->next = scbp;		host->last_done = scbp;	} else {		host->first_done = scbp;		host->last_done = scbp;	}}struct scsi_ctrl_blk *initio_find_done_scb(struct initio_host * host){	struct scsi_ctrl_blk *tmp;	if ((tmp = host->first_done) != NULL) {		if ((host->first_done = tmp->next) == NULL)			host->last_done = NULL;		tmp->next = NULL;	}#if DEBUG_QUEUE	printk("find done SCB %p; ",tmp);#endif	return tmp;}static int initio_abort_srb(struct initio_host * host, struct scsi_cmnd *srbp){	unsigned long flags;	struct scsi_ctrl_blk *tmp, *prev;	spin_lock_irqsave(&host->semaph_lock, flags);

⌨️ 快捷键说明

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