833 lines · 21.0 KB · C
RawDownload
/* *	Adaptec AAC series RAID controller driver *	(c) Copyright 2001 Red Hat Inc. * * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * * Copyright (c) 2000-2010 Adaptec, Inc. *               2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com) * * This program 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; either version 2, or (at your option) * any later version. * * This program 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 this program; see the file COPYING.  If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * Module Name: *  src.c * * Abstract: Hardware Device Interface for PMC SRC based controllers * */ #include <linux/kernel.h>#include <linux/init.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/completion.h>#include <linux/time.h>#include <linux/interrupt.h>#include <scsi/scsi_host.h> #include "aacraid.h" static irqreturn_t aac_src_intr_message(int irq, void *dev_id){	struct aac_dev *dev = dev_id;	unsigned long bellbits, bellbits_shifted;	int our_interrupt = 0;	int isFastResponse;	u32 index, handle; 	bellbits = src_readl(dev, MUnit.ODR_R);	if (bellbits & PmDoorBellResponseSent) {		bellbits = PmDoorBellResponseSent;		/* handle async. status */		src_writel(dev, MUnit.ODR_C, bellbits);		src_readl(dev, MUnit.ODR_C);		our_interrupt = 1;		index = dev->host_rrq_idx;		for (;;) {			isFastResponse = 0;			/* remove toggle bit (31) */			handle = le32_to_cpu(dev->host_rrq[index]) & 0x7fffffff;			/* check fast response bit (30) */			if (handle & 0x40000000)				isFastResponse = 1;			handle &= 0x0000ffff;			if (handle == 0)				break; 			aac_intr_normal(dev, handle-1, 0, isFastResponse, NULL); 			dev->host_rrq[index++] = 0;			if (index == dev->scsi_host_ptr->can_queue +						AAC_NUM_MGT_FIB)				index = 0;			dev->host_rrq_idx = index;		}	} else {		bellbits_shifted = (bellbits >> SRC_ODR_SHIFT);		if (bellbits_shifted & DoorBellAifPending) {			src_writel(dev, MUnit.ODR_C, bellbits);			src_readl(dev, MUnit.ODR_C);			our_interrupt = 1;			/* handle AIF */			aac_intr_normal(dev, 0, 2, 0, NULL);		} else if (bellbits_shifted & OUTBOUNDDOORBELL_0) {			unsigned long sflags;			struct list_head *entry;			int send_it = 0;			extern int aac_sync_mode; 			src_writel(dev, MUnit.ODR_C, bellbits);			src_readl(dev, MUnit.ODR_C); 			if (!aac_sync_mode) {				src_writel(dev, MUnit.ODR_C, bellbits);				src_readl(dev, MUnit.ODR_C);				our_interrupt = 1;			} 			if (dev->sync_fib) {				our_interrupt = 1;				if (dev->sync_fib->callback)					dev->sync_fib->callback(dev->sync_fib->callback_data,						dev->sync_fib);				spin_lock_irqsave(&dev->sync_fib->event_lock, sflags);				if (dev->sync_fib->flags & FIB_CONTEXT_FLAG_WAIT) {					dev->management_fib_count--;					up(&dev->sync_fib->event_wait);				}				spin_unlock_irqrestore(&dev->sync_fib->event_lock, sflags);				spin_lock_irqsave(&dev->sync_lock, sflags);				if (!list_empty(&dev->sync_fib_list)) {					entry = dev->sync_fib_list.next;					dev->sync_fib = list_entry(entry, struct fib, fiblink);					list_del(entry);					send_it = 1;				} else {					dev->sync_fib = NULL;				}				spin_unlock_irqrestore(&dev->sync_lock, sflags);				if (send_it) {					aac_adapter_sync_cmd(dev, SEND_SYNCHRONOUS_FIB,						(u32)dev->sync_fib->hw_fib_pa, 0, 0, 0, 0, 0,						NULL, NULL, NULL, NULL, NULL);				}			}		}	} 	if (our_interrupt) {		return IRQ_HANDLED;	}	return IRQ_NONE;} /** *	aac_src_disable_interrupt	-	Disable interrupts *	@dev: Adapter */ static void aac_src_disable_interrupt(struct aac_dev *dev){	src_writel(dev, MUnit.OIMR, dev->OIMR = 0xffffffff);} /** *	aac_src_enable_interrupt_message	-	Enable interrupts *	@dev: Adapter */ static void aac_src_enable_interrupt_message(struct aac_dev *dev){	src_writel(dev, MUnit.OIMR, dev->OIMR = 0xfffffff8);} /** *	src_sync_cmd	-	send a command and wait *	@dev: Adapter *	@command: Command to execute *	@p1: first parameter *	@ret: adapter status * *	This routine will send a synchronous command to the adapter and wait *	for its	completion. */ static int src_sync_cmd(struct aac_dev *dev, u32 command,	u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6,	u32 *status, u32 * r1, u32 * r2, u32 * r3, u32 * r4){	unsigned long start;	int ok; 	/*	 *	Write the command into Mailbox 0	 */	writel(command, &dev->IndexRegs->Mailbox[0]);	/*	 *	Write the parameters into Mailboxes 1 - 6	 */	writel(p1, &dev->IndexRegs->Mailbox[1]);	writel(p2, &dev->IndexRegs->Mailbox[2]);	writel(p3, &dev->IndexRegs->Mailbox[3]);	writel(p4, &dev->IndexRegs->Mailbox[4]); 	/*	 *	Clear the synch command doorbell to start on a clean slate.	 */	src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT); 	/*	 *	Disable doorbell interrupts	 */	src_writel(dev, MUnit.OIMR, dev->OIMR = 0xffffffff); 	/*	 *	Force the completion of the mask register write before issuing	 *	the interrupt.	 */	src_readl(dev, MUnit.OIMR); 	/*	 *	Signal that there is a new synch command	 */	src_writel(dev, MUnit.IDR, INBOUNDDOORBELL_0 << SRC_IDR_SHIFT); 	if (!dev->sync_mode || command != SEND_SYNCHRONOUS_FIB) {		ok = 0;		start = jiffies; 		/*		 *	Wait up to 5 minutes		 */		while (time_before(jiffies, start+300*HZ)) {			udelay(5);	/* Delay 5 microseconds to let Mon960 get info. */			/*			 *	Mon960 will set doorbell0 bit when it has completed the command.			 */			if ((src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT) & OUTBOUNDDOORBELL_0) {				/*				 *	Clear the doorbell.				 */				src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);				ok = 1;				break;			}			/*			 *	Yield the processor in case we are slow			 */			msleep(1);		}		if (unlikely(ok != 1)) {			/*			 *	Restore interrupt mask even though we timed out			 */			aac_adapter_enable_int(dev);			return -ETIMEDOUT;		}		/*		 *	Pull the synch status from Mailbox 0.		 */		if (status)			*status = readl(&dev->IndexRegs->Mailbox[0]);		if (r1)			*r1 = readl(&dev->IndexRegs->Mailbox[1]);		if (r2)			*r2 = readl(&dev->IndexRegs->Mailbox[2]);		if (r3)			*r3 = readl(&dev->IndexRegs->Mailbox[3]);		if (r4)			*r4 = readl(&dev->IndexRegs->Mailbox[4]); 		/*		 *	Clear the synch command doorbell.		 */		src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);	} 	/*	 *	Restore interrupt mask	 */	aac_adapter_enable_int(dev);	return 0;} /** *	aac_src_interrupt_adapter	-	interrupt adapter *	@dev: Adapter * *	Send an interrupt to the i960 and breakpoint it. */ static void aac_src_interrupt_adapter(struct aac_dev *dev){	src_sync_cmd(dev, BREAKPOINT_REQUEST,		0, 0, 0, 0, 0, 0,		NULL, NULL, NULL, NULL, NULL);} /** *	aac_src_notify_adapter		-	send an event to the adapter *	@dev: Adapter *	@event: Event to send * *	Notify the i960 that something it probably cares about has *	happened. */ static void aac_src_notify_adapter(struct aac_dev *dev, u32 event){	switch (event) { 	case AdapNormCmdQue:		src_writel(dev, MUnit.ODR_C,			INBOUNDDOORBELL_1 << SRC_ODR_SHIFT);		break;	case HostNormRespNotFull:		src_writel(dev, MUnit.ODR_C,			INBOUNDDOORBELL_4 << SRC_ODR_SHIFT);		break;	case AdapNormRespQue:		src_writel(dev, MUnit.ODR_C,			INBOUNDDOORBELL_2 << SRC_ODR_SHIFT);		break;	case HostNormCmdNotFull:		src_writel(dev, MUnit.ODR_C,			INBOUNDDOORBELL_3 << SRC_ODR_SHIFT);		break;	case FastIo:		src_writel(dev, MUnit.ODR_C,			INBOUNDDOORBELL_6 << SRC_ODR_SHIFT);		break;	case AdapPrintfDone:		src_writel(dev, MUnit.ODR_C,			INBOUNDDOORBELL_5 << SRC_ODR_SHIFT);		break;	default:		BUG();		break;	}} /** *	aac_src_start_adapter		-	activate adapter *	@dev:	Adapter * *	Start up processing on an i960 based AAC adapter */ static void aac_src_start_adapter(struct aac_dev *dev){	struct aac_init *init; 	 /* reset host_rrq_idx first */	dev->host_rrq_idx = 0; 	init = dev->init;	init->HostElapsedSeconds = cpu_to_le32(get_seconds()); 	/* We can only use a 32 bit address here */	src_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa,	  0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL);} /** *	aac_src_check_health *	@dev: device to check if healthy * *	Will attempt to determine if the specified adapter is alive and *	capable of handling requests, returning 0 if alive. */static int aac_src_check_health(struct aac_dev *dev){	u32 status = src_readl(dev, MUnit.OMR); 	/*	 *	Check to see if the board failed any self tests.	 */	if (unlikely(status & SELF_TEST_FAILED))		return -1; 	/*	 *	Check to see if the board panic'd.	 */	if (unlikely(status & KERNEL_PANIC))		return (status >> 16) & 0xFF;	/*	 *	Wait for the adapter to be up and running.	 */	if (unlikely(!(status & KERNEL_UP_AND_RUNNING)))		return -3;	/*	 *	Everything is OK	 */	return 0;} /** *	aac_src_deliver_message *	@fib: fib to issue * *	Will send a fib, returning 0 if successful. */static int aac_src_deliver_message(struct fib *fib){	struct aac_dev *dev = fib->dev;	struct aac_queue *q = &dev->queues->queue[AdapNormCmdQueue];	unsigned long qflags;	u32 fibsize;	dma_addr_t address;	struct aac_fib_xporthdr *pFibX;	u16 hdr_size = le16_to_cpu(fib->hw_fib_va->header.Size); 	spin_lock_irqsave(q->lock, qflags);	q->numpending++;	spin_unlock_irqrestore(q->lock, qflags); 	if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) {		/* Calculate the amount to the fibsize bits */		fibsize = (hdr_size + 127) / 128 - 1;		if (fibsize > (ALIGN32 - 1))			return -EMSGSIZE;		/* New FIB header, 32-bit */		address = fib->hw_fib_pa;		fib->hw_fib_va->header.StructType = FIB_MAGIC2;		fib->hw_fib_va->header.SenderFibAddress = (u32)address;		fib->hw_fib_va->header.u.TimeStamp = 0;		BUG_ON(upper_32_bits(address) != 0L);		address |= fibsize;	} else {		/* Calculate the amount to the fibsize bits */		fibsize = (sizeof(struct aac_fib_xporthdr) + hdr_size + 127) / 128 - 1;		if (fibsize > (ALIGN32 - 1))			return -EMSGSIZE; 		/* Fill XPORT header */		pFibX = (void *)fib->hw_fib_va - sizeof(struct aac_fib_xporthdr);		pFibX->Handle = cpu_to_le32(fib->hw_fib_va->header.Handle);		pFibX->HostAddress = cpu_to_le64(fib->hw_fib_pa);		pFibX->Size = cpu_to_le32(hdr_size); 		/*		 * The xport header has been 32-byte aligned for us so that fibsize		 * can be masked out of this address by hardware. -- BenC		 */		address = fib->hw_fib_pa - sizeof(struct aac_fib_xporthdr);		if (address & (ALIGN32 - 1))			return -EINVAL;		address |= fibsize;	} 	src_writel(dev, MUnit.IQ_H, upper_32_bits(address) & 0xffffffff);	src_writel(dev, MUnit.IQ_L, address & 0xffffffff); 	return 0;} /** *	aac_src_ioremap *	@size: mapping resize request * */static int aac_src_ioremap(struct aac_dev *dev, u32 size){	if (!size) {		iounmap(dev->regs.src.bar1);		dev->regs.src.bar1 = NULL;		iounmap(dev->regs.src.bar0);		dev->base = dev->regs.src.bar0 = NULL;		return 0;	}	dev->regs.src.bar1 = ioremap(pci_resource_start(dev->pdev, 2),		AAC_MIN_SRC_BAR1_SIZE);	dev->base = NULL;	if (dev->regs.src.bar1 == NULL)		return -1;	dev->base = dev->regs.src.bar0 = ioremap(dev->base_start, size);	if (dev->base == NULL) {		iounmap(dev->regs.src.bar1);		dev->regs.src.bar1 = NULL;		return -1;	}	dev->IndexRegs = &((struct src_registers __iomem *)		dev->base)->u.tupelo.IndexRegs;	return 0;} /** *  aac_srcv_ioremap *	@size: mapping resize request * */static int aac_srcv_ioremap(struct aac_dev *dev, u32 size){	if (!size) {		iounmap(dev->regs.src.bar0);		dev->base = dev->regs.src.bar0 = NULL;		return 0;	}	dev->base = dev->regs.src.bar0 = ioremap(dev->base_start, size);	if (dev->base == NULL)		return -1;	dev->IndexRegs = &((struct src_registers __iomem *)		dev->base)->u.denali.IndexRegs;	return 0;} static int aac_src_restart_adapter(struct aac_dev *dev, int bled){	u32 var, reset_mask; 	if (bled >= 0) {		if (bled)			printk(KERN_ERR "%s%d: adapter kernel panic'd %x.\n",				dev->name, dev->id, bled);

Lines 1–500 of 833.