1436 lines · 33.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *	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-2015 PMC-Sierra, Inc. (aacraid@pmc-sierra.com) *		 2016-2017 Microsemi Corp. (aacraid@microsemi.com) * * 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 int aac_src_get_sync_status(struct aac_dev *dev); static irqreturn_t aac_src_intr_message(int irq, void *dev_id){	struct aac_msix_ctx *ctx;	struct aac_dev *dev;	unsigned long bellbits, bellbits_shifted;	int vector_no;	int isFastResponse, mode;	u32 index, handle; 	ctx = (struct aac_msix_ctx *)dev_id;	dev = ctx->dev;	vector_no = ctx->vector_no; 	if (dev->msi_enabled) {		mode = AAC_INT_MODE_MSI;		if (vector_no == 0) {			bellbits = src_readl(dev, MUnit.ODR_MSI);			if (bellbits & 0x40000)				mode |= AAC_INT_MODE_AIF;			if (bellbits & 0x1000)				mode |= AAC_INT_MODE_SYNC;		}	} else {		mode = AAC_INT_MODE_INTX;		bellbits = src_readl(dev, MUnit.ODR_R);		if (bellbits & PmDoorBellResponseSent) {			bellbits = PmDoorBellResponseSent;			src_writel(dev, MUnit.ODR_C, bellbits);			src_readl(dev, MUnit.ODR_C);		} else {			bellbits_shifted = (bellbits >> SRC_ODR_SHIFT);			src_writel(dev, MUnit.ODR_C, bellbits);			src_readl(dev, MUnit.ODR_C); 			if (bellbits_shifted & DoorBellAifPending)				mode |= AAC_INT_MODE_AIF;			else if (bellbits_shifted & OUTBOUNDDOORBELL_0)				mode |= AAC_INT_MODE_SYNC;		}	} 	if (mode & AAC_INT_MODE_SYNC) {		unsigned long sflags;		struct list_head *entry;		int send_it = 0;		extern int aac_sync_mode; 		if (!aac_sync_mode && !dev->msi_enabled) {			src_writel(dev, MUnit.ODR_C, bellbits);			src_readl(dev, MUnit.ODR_C);		} 		if (dev->sync_fib) {			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--;				complete(&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 (!dev->msi_enabled)			mode = 0; 	} 	if (mode & AAC_INT_MODE_AIF) {		/* handle AIF */		if (dev->sa_firmware) {			u32 events = src_readl(dev, MUnit.SCR0); 			aac_intr_normal(dev, events, 1, 0, NULL);			writel(events, &dev->IndexRegs->Mailbox[0]);			src_writel(dev, MUnit.IDR, 1 << 23);		} else {			if (dev->aif_thread && dev->fsa_dev)				aac_intr_normal(dev, 0, 2, 0, NULL);		}		if (dev->msi_enabled)			aac_src_access_devreg(dev, AAC_CLEAR_AIF_BIT);		mode = 0;	} 	if (mode) {		index = dev->host_rrq_idx[vector_no]; 		for (;;) {			isFastResponse = 0;			/* remove toggle bit (31) */			handle = le32_to_cpu((dev->host_rrq[index])				& 0x7fffffff);			/* check fast response bits (30, 1) */			if (handle & 0x40000000)				isFastResponse = 1;			handle &= 0x0000ffff;			if (handle == 0)				break;			handle >>= 2;			if (dev->msi_enabled && dev->max_msix > 1)				atomic_dec(&dev->rrq_outstanding[vector_no]);			aac_intr_normal(dev, handle, 0, isFastResponse, NULL);			dev->host_rrq[index++] = 0;			if (index == (vector_no + 1) * dev->vector_cap)				index = vector_no * dev->vector_cap;			dev->host_rrq_idx[vector_no] = index;		}		mode = 0;	} 	return IRQ_HANDLED;} /** *	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){	aac_src_access_devreg(dev, AAC_ENABLE_INTERRUPT);} /** *	src_sync_cmd	-	send a command and wait *	@dev: Adapter *	@command: Command to execute *	@p1: first parameter *	@p2: second parameter *	@p3: third parameter *	@p4: forth parameter *	@p5: fifth parameter *	@p6: sixth parameter *	@status: adapter status *	@r1: first return value *	@r2: second return valu *	@r3: third return value *	@r4: forth return value * *	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;	unsigned long delay;	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.	 */	if (!dev->msi_enabled)		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) &&		!dev->in_soft_reset) {		ok = 0;		start = jiffies; 		if (command == IOP_RESET_ALWAYS) {			/* Wait up to 10 sec */			delay = 10*HZ;		} else {			/* Wait up to 5 minutes */			delay = 300*HZ;		}		while (time_before(jiffies, start+delay)) {			udelay(5);	/* Delay 5 microseconds to let Mon960 get info. */			/*			 *	Mon960 will set doorbell0 bit when it has completed the command.			 */			if (aac_src_get_sync_status(dev) & OUTBOUNDDOORBELL_0) {				/*				 *	Clear the doorbell.				 */				if (dev->msi_enabled)					aac_src_access_devreg(dev,						AAC_CLEAR_SYNC_BIT);				else					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]);		if (command == GET_COMM_PREFERRED_SETTINGS)			dev->max_msix =				readl(&dev->IndexRegs->Mailbox[5]) & 0xFFFF;		/*		 *	Clear the synch command doorbell.		 */		if (!dev->msi_enabled)			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){	union aac_init *init;	int i; 	 /* reset host_rrq_idx first */	for (i = 0; i < dev->max_msix; i++) {		dev->host_rrq_idx[i] = i * dev->vector_cap;		atomic_set(&dev->rrq_outstanding[i], 0);	}	atomic_set(&dev->msix_counter, 0);	dev->fibs_pushed_no = 0; 	init = dev->init;	if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE3) {		init->r8.host_elapsed_seconds =			cpu_to_le32(ktime_get_real_seconds());		src_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS,			lower_32_bits(dev->init_pa),			upper_32_bits(dev->init_pa),			sizeof(struct _r8) +			AAC_MAX_HRRQ * sizeof(struct _rrq),			0, 0, 0, NULL, NULL, NULL, NULL, NULL);	} else {		init->r7.host_elapsed_seconds =			cpu_to_le32(ktime_get_real_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 panic'd.	 */	if (unlikely(status & KERNEL_PANIC))		goto err_blink; 	/*	 *	Check to see if the board failed any self tests.	 */	if (unlikely(status & SELF_TEST_FAILED))		goto err_out; 	/*	 *	Check to see if the board failed any self tests.	 */	if (unlikely(status & MONITOR_PANIC))		goto err_out; 	/*	 *	Wait for the adapter to be up and running.	 */	if (unlikely(!(status & KERNEL_UP_AND_RUNNING)))		return -3;	/*	 *	Everything is OK	 */	return 0; err_out:	return -1; err_blink:	return (status >> 16) & 0xFF;} static inline u32 aac_get_vector(struct aac_dev *dev){	return atomic_inc_return(&dev->msix_counter)%dev->max_msix;} /** *	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];	u32 fibsize;	dma_addr_t address;	struct aac_fib_xporthdr *pFibX;	int native_hba;#if !defined(writeq)	unsigned long flags;#endif 	u16 vector_no; 	atomic_inc(&q->numpending); 	native_hba = (fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA) ? 1 : 0; 

Lines 1–500 of 1436.