sa.c revision 7c00ffa314bf0fb0e23858bbebad33b48b6abbb9
1/*
2 *	Adaptec AAC series RAID controller driver
3 *	(c) Copyright 2001 Red Hat Inc.	<alan@redhat.com>
4 *
5 * based on the old aacraid driver that is..
6 * Adaptec aacraid device driver for Linux.
7 *
8 * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING.  If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 * Module Name:
25 *  sa.c
26 *
27 * Abstract: Drawbridge specific support functions
28 *
29 */
30
31#include <linux/kernel.h>
32#include <linux/init.h>
33#include <linux/types.h>
34#include <linux/sched.h>
35#include <linux/pci.h>
36#include <linux/spinlock.h>
37#include <linux/slab.h>
38#include <linux/blkdev.h>
39#include <linux/delay.h>
40#include <linux/completion.h>
41#include <linux/time.h>
42#include <linux/interrupt.h>
43#include <asm/semaphore.h>
44
45#include <scsi/scsi_host.h>
46
47#include "aacraid.h"
48
49static irqreturn_t aac_sa_intr(int irq, void *dev_id, struct pt_regs *regs)
50{
51	struct aac_dev *dev = dev_id;
52	unsigned short intstat, mask;
53
54	intstat = sa_readw(dev, DoorbellReg_p);
55	/*
56	 *	Read mask and invert because drawbridge is reversed.
57	 *	This allows us to only service interrupts that have been enabled.
58	 */
59	mask = ~(sa_readw(dev, SaDbCSR.PRISETIRQMASK));
60
61	/* Check to see if this is our interrupt.  If it isn't just return */
62
63	if (intstat & mask) {
64		if (intstat & PrintfReady) {
65			aac_printf(dev, sa_readl(dev, Mailbox5));
66			sa_writew(dev, DoorbellClrReg_p, PrintfReady); /* clear PrintfReady */
67			sa_writew(dev, DoorbellReg_s, PrintfDone);
68		} else if (intstat & DOORBELL_1) {	// dev -> Host Normal Command Ready
69			aac_command_normal(&dev->queues->queue[HostNormCmdQueue]);
70			sa_writew(dev, DoorbellClrReg_p, DOORBELL_1);
71		} else if (intstat & DOORBELL_2) {	// dev -> Host Normal Response Ready
72			aac_response_normal(&dev->queues->queue[HostNormRespQueue]);
73			sa_writew(dev, DoorbellClrReg_p, DOORBELL_2);
74		} else if (intstat & DOORBELL_3) {	// dev -> Host Normal Command Not Full
75			sa_writew(dev, DoorbellClrReg_p, DOORBELL_3);
76		} else if (intstat & DOORBELL_4) {	// dev -> Host Normal Response Not Full
77			sa_writew(dev, DoorbellClrReg_p, DOORBELL_4);
78		}
79		return IRQ_HANDLED;
80	}
81	return IRQ_NONE;
82}
83
84/**
85 *	aac_sa_notify_adapter		-	handle adapter notification
86 *	@dev:	Adapter that notification is for
87 *	@event:	Event to notidy
88 *
89 *	Notify the adapter of an event
90 */
91
92static void aac_sa_notify_adapter(struct aac_dev *dev, u32 event)
93{
94	switch (event) {
95
96	case AdapNormCmdQue:
97		sa_writew(dev, DoorbellReg_s,DOORBELL_1);
98		break;
99	case HostNormRespNotFull:
100		sa_writew(dev, DoorbellReg_s,DOORBELL_4);
101		break;
102	case AdapNormRespQue:
103		sa_writew(dev, DoorbellReg_s,DOORBELL_2);
104		break;
105	case HostNormCmdNotFull:
106		sa_writew(dev, DoorbellReg_s,DOORBELL_3);
107		break;
108	case HostShutdown:
109		/*
110		sa_sync_cmd(dev, HOST_CRASHING, 0, 0, 0, 0, 0, 0,
111		NULL, NULL, NULL, NULL, NULL);
112		*/
113		break;
114	case FastIo:
115		sa_writew(dev, DoorbellReg_s,DOORBELL_6);
116		break;
117	case AdapPrintfDone:
118		sa_writew(dev, DoorbellReg_s,DOORBELL_5);
119		break;
120	default:
121		BUG();
122		break;
123	}
124}
125
126
127/**
128 *	sa_sync_cmd	-	send a command and wait
129 *	@dev: Adapter
130 *	@command: Command to execute
131 *	@p1: first parameter
132 *	@ret: adapter status
133 *
134 *	This routine will send a synchronous command to the adapter and wait
135 *	for its	completion.
136 */
137
138static int sa_sync_cmd(struct aac_dev *dev, u32 command,
139		u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6,
140		u32 *ret, u32 *r1, u32 *r2, u32 *r3, u32 *r4)
141{
142	unsigned long start;
143 	int ok;
144	/*
145	 *	Write the Command into Mailbox 0
146	 */
147	sa_writel(dev, Mailbox0, command);
148	/*
149	 *	Write the parameters into Mailboxes 1 - 4
150	 */
151	sa_writel(dev, Mailbox1, p1);
152	sa_writel(dev, Mailbox2, p2);
153	sa_writel(dev, Mailbox3, p3);
154	sa_writel(dev, Mailbox4, p4);
155
156	/*
157	 *	Clear the synch command doorbell to start on a clean slate.
158	 */
159	sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
160	/*
161	 *	Signal that there is a new synch command
162	 */
163	sa_writew(dev, DoorbellReg_s, DOORBELL_0);
164
165	ok = 0;
166	start = jiffies;
167
168	while(time_before(jiffies, start+30*HZ))
169	{
170		/*
171		 *	Delay 5uS so that the monitor gets access
172		 */
173		udelay(5);
174		/*
175		 *	Mon110 will set doorbell0 bit when it has
176		 *	completed the command.
177		 */
178		if(sa_readw(dev, DoorbellReg_p) & DOORBELL_0)  {
179			ok = 1;
180			break;
181		}
182		set_current_state(TASK_UNINTERRUPTIBLE);
183		schedule_timeout(1);
184	}
185
186	if (ok != 1)
187		return -ETIMEDOUT;
188	/*
189	 *	Clear the synch command doorbell.
190	 */
191	sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
192	/*
193	 *	Pull the synch status from Mailbox 0.
194	 */
195	if (ret)
196		*ret = sa_readl(dev, Mailbox0);
197	if (r1)
198		*r1 = sa_readl(dev, Mailbox1);
199	if (r2)
200		*r2 = sa_readl(dev, Mailbox2);
201	if (r3)
202		*r3 = sa_readl(dev, Mailbox3);
203	if (r4)
204		*r4 = sa_readl(dev, Mailbox4);
205	return 0;
206}
207
208/**
209 *	aac_sa_interrupt_adapter	-	interrupt an adapter
210 *	@dev: Which adapter to enable.
211 *
212 *	Breakpoint an adapter.
213 */
214
215static void aac_sa_interrupt_adapter (struct aac_dev *dev)
216{
217	u32 ret;
218	sa_sync_cmd(dev, BREAKPOINT_REQUEST, 0, 0, 0, 0, 0, 0,
219			&ret, NULL, NULL, NULL, NULL);
220}
221
222/**
223 *	aac_sa_start_adapter		-	activate adapter
224 *	@dev:	Adapter
225 *
226 *	Start up processing on an ARM based AAC adapter
227 */
228
229static void aac_sa_start_adapter(struct aac_dev *dev)
230{
231	u32 ret;
232	struct aac_init *init;
233	/*
234	 * Fill in the remaining pieces of the init.
235	 */
236	init = dev->init;
237	init->HostElapsedSeconds = cpu_to_le32(get_seconds());
238
239	/*
240	 * Tell the adapter we are back and up and running so it will scan its command
241	 * queues and enable our interrupts
242	 */
243	dev->irq_mask =	(PrintfReady | DOORBELL_1 | DOORBELL_2 | DOORBELL_3 | DOORBELL_4);
244	/*
245	 *	First clear out all interrupts.  Then enable the one's that
246	 *	we can handle.
247	 */
248	sa_writew(dev, SaDbCSR.PRISETIRQMASK, 0xffff);
249	sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, (PrintfReady | DOORBELL_1 | DOORBELL_2 | DOORBELL_3 | DOORBELL_4));
250	/* We can only use a 32 bit address here */
251	sa_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS,
252			(u32)(ulong)dev->init_pa, 0, 0, 0, 0, 0,
253			&ret, NULL, NULL, NULL, NULL);
254}
255
256/**
257 *	aac_sa_check_health
258 *	@dev: device to check if healthy
259 *
260 *	Will attempt to determine if the specified adapter is alive and
261 *	capable of handling requests, returning 0 if alive.
262 */
263static int aac_sa_check_health(struct aac_dev *dev)
264{
265	long status = sa_readl(dev, Mailbox7);
266
267	/*
268	 *	Check to see if the board failed any self tests.
269	 */
270	if (status & SELF_TEST_FAILED)
271		return -1;
272	/*
273	 *	Check to see if the board panic'd while booting.
274	 */
275	if (status & KERNEL_PANIC)
276		return -2;
277	/*
278	 *	Wait for the adapter to be up and running. Wait up to 3 minutes
279	 */
280	if (!(status & KERNEL_UP_AND_RUNNING))
281		return -3;
282	/*
283	 *	Everything is OK
284	 */
285	return 0;
286}
287
288/**
289 *	aac_sa_init	-	initialize an ARM based AAC card
290 *	@dev: device to configure
291 *
292 *	Allocate and set up resources for the ARM based AAC variants. The
293 *	device_interface in the commregion will be allocated and linked
294 *	to the comm region.
295 */
296
297int aac_sa_init(struct aac_dev *dev)
298{
299	unsigned long start;
300	unsigned long status;
301	int instance;
302	const char *name;
303
304	instance = dev->id;
305	name     = dev->name;
306
307	/*
308	 *	Map in the registers from the adapter.
309	 */
310
311	if((dev->regs.sa = ioremap((unsigned long)dev->scsi_host_ptr->base, 8192))==NULL)
312	{
313		printk(KERN_WARNING "aacraid: unable to map ARM.\n" );
314		goto error_iounmap;
315	}
316	/*
317	 *	Check to see if the board failed any self tests.
318	 */
319	if (sa_readl(dev, Mailbox7) & SELF_TEST_FAILED) {
320		printk(KERN_WARNING "%s%d: adapter self-test failed.\n", name, instance);
321		goto error_iounmap;
322	}
323	/*
324	 *	Check to see if the board panic'd while booting.
325	 */
326	if (sa_readl(dev, Mailbox7) & KERNEL_PANIC) {
327		printk(KERN_WARNING "%s%d: adapter kernel panic'd.\n", name, instance);
328		goto error_iounmap;
329	}
330	start = jiffies;
331	/*
332	 *	Wait for the adapter to be up and running. Wait up to 3 minutes.
333	 */
334	while (!(sa_readl(dev, Mailbox7) & KERNEL_UP_AND_RUNNING)) {
335		if (time_after(jiffies, start+180*HZ)) {
336			status = sa_readl(dev, Mailbox7);
337			printk(KERN_WARNING "%s%d: adapter kernel failed to start, init status = %lx.\n",
338					name, instance, status);
339			goto error_iounmap;
340		}
341		set_current_state(TASK_UNINTERRUPTIBLE);
342		schedule_timeout(1);
343	}
344
345	if (request_irq(dev->scsi_host_ptr->irq, aac_sa_intr, SA_SHIRQ|SA_INTERRUPT, "aacraid", (void *)dev ) < 0) {
346		printk(KERN_WARNING "%s%d: Interrupt unavailable.\n", name, instance);
347		goto error_iounmap;
348	}
349
350	/*
351	 *	Fill in the function dispatch table.
352	 */
353
354	dev->a_ops.adapter_interrupt = aac_sa_interrupt_adapter;
355	dev->a_ops.adapter_notify = aac_sa_notify_adapter;
356	dev->a_ops.adapter_sync_cmd = sa_sync_cmd;
357	dev->a_ops.adapter_check_health = aac_sa_check_health;
358
359
360	if(aac_init_adapter(dev) == NULL)
361		goto error_irq;
362
363	/*
364	 *	Start any kernel threads needed
365	 */
366	dev->thread_pid = kernel_thread((int (*)(void *))aac_command_thread, dev, 0);
367	if (dev->thread_pid < 0) {
368		printk(KERN_ERR "aacraid: Unable to create command thread.\n");
369		goto error_kfree;
370	}
371
372	/*
373	 *	Tell the adapter that all is configure, and it can start
374	 *	accepting requests
375	 */
376	aac_sa_start_adapter(dev);
377	return 0;
378
379
380error_kfree:
381	kfree(dev->queues);
382
383error_irq:
384	free_irq(dev->scsi_host_ptr->irq, (void *)dev);
385
386error_iounmap:
387	iounmap(dev->regs.sa);
388
389	return -1;
390}
391
392