Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6] / drivers / scsi / aacraid / sa.c
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/spinlock.h>
35 #include <linux/slab.h>
36 #include <linux/blkdev.h>
37 #include <linux/delay.h>
38 #include <linux/completion.h>
39 #include <linux/time.h>
40 #include <linux/interrupt.h>
41 #include <asm/semaphore.h>
42
43 #include <scsi/scsi_host.h>
44
45 #include "aacraid.h"
46
47 static irqreturn_t aac_sa_intr(int irq, void *dev_id)
48 {
49         struct aac_dev *dev = dev_id;
50         unsigned short intstat, mask;
51
52         intstat = sa_readw(dev, DoorbellReg_p);
53         /*
54          *      Read mask and invert because drawbridge is reversed.
55          *      This allows us to only service interrupts that have been enabled.
56          */
57         mask = ~(sa_readw(dev, SaDbCSR.PRISETIRQMASK));
58
59         /* Check to see if this is our interrupt.  If it isn't just return */
60
61         if (intstat & mask) {
62                 if (intstat & PrintfReady) {
63                         aac_printf(dev, sa_readl(dev, Mailbox5));
64                         sa_writew(dev, DoorbellClrReg_p, PrintfReady); /* clear PrintfReady */
65                         sa_writew(dev, DoorbellReg_s, PrintfDone);
66                 } else if (intstat & DOORBELL_1) {      // dev -> Host Normal Command Ready
67                         sa_writew(dev, DoorbellClrReg_p, DOORBELL_1);
68                         aac_command_normal(&dev->queues->queue[HostNormCmdQueue]);
69                 } else if (intstat & DOORBELL_2) {      // dev -> Host Normal Response Ready
70                         sa_writew(dev, DoorbellClrReg_p, DOORBELL_2);
71                         aac_response_normal(&dev->queues->queue[HostNormRespQueue]);
72                 } else if (intstat & DOORBELL_3) {      // dev -> Host Normal Command Not Full
73                         sa_writew(dev, DoorbellClrReg_p, DOORBELL_3);
74                 } else if (intstat & DOORBELL_4) {      // dev -> Host Normal Response Not Full
75                         sa_writew(dev, DoorbellClrReg_p, DOORBELL_4);
76                 }
77                 return IRQ_HANDLED;
78         }
79         return IRQ_NONE;
80 }
81
82 /**
83  *      aac_sa_disable_interrupt        -       disable interrupt
84  *      @dev: Which adapter to enable.
85  */
86
87 static void aac_sa_disable_interrupt (struct aac_dev *dev)
88 {
89         sa_writew(dev, SaDbCSR.PRISETIRQMASK, 0xffff);
90 }
91
92 /**
93  *      aac_sa_enable_interrupt -       enable interrupt
94  *      @dev: Which adapter to enable.
95  */
96
97 static void aac_sa_enable_interrupt (struct aac_dev *dev)
98 {
99         sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, (PrintfReady | DOORBELL_1 |
100                                 DOORBELL_2 | DOORBELL_3 | DOORBELL_4));
101 }
102
103 /**
104  *      aac_sa_notify_adapter           -       handle adapter notification
105  *      @dev:   Adapter that notification is for
106  *      @event: Event to notidy
107  *
108  *      Notify the adapter of an event
109  */
110  
111 static void aac_sa_notify_adapter(struct aac_dev *dev, u32 event)
112 {
113         switch (event) {
114
115         case AdapNormCmdQue:
116                 sa_writew(dev, DoorbellReg_s,DOORBELL_1);
117                 break;
118         case HostNormRespNotFull:
119                 sa_writew(dev, DoorbellReg_s,DOORBELL_4);
120                 break;
121         case AdapNormRespQue:
122                 sa_writew(dev, DoorbellReg_s,DOORBELL_2);
123                 break;
124         case HostNormCmdNotFull:
125                 sa_writew(dev, DoorbellReg_s,DOORBELL_3);
126                 break;
127         case HostShutdown:
128                 /*
129                 sa_sync_cmd(dev, HOST_CRASHING, 0, 0, 0, 0, 0, 0,
130                 NULL, NULL, NULL, NULL, NULL);
131                 */
132                 break;
133         case FastIo:
134                 sa_writew(dev, DoorbellReg_s,DOORBELL_6);
135                 break;
136         case AdapPrintfDone:
137                 sa_writew(dev, DoorbellReg_s,DOORBELL_5);
138                 break;
139         default:
140                 BUG();
141                 break;
142         }
143 }
144
145
146 /**
147  *      sa_sync_cmd     -       send a command and wait
148  *      @dev: Adapter
149  *      @command: Command to execute
150  *      @p1: first parameter
151  *      @ret: adapter status
152  *
153  *      This routine will send a synchronous command to the adapter and wait 
154  *      for its completion.
155  */
156
157 static int sa_sync_cmd(struct aac_dev *dev, u32 command, 
158                 u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6,
159                 u32 *ret, u32 *r1, u32 *r2, u32 *r3, u32 *r4)
160 {
161         unsigned long start;
162         int ok;
163         /*
164          *      Write the Command into Mailbox 0
165          */
166         sa_writel(dev, Mailbox0, command);
167         /*
168          *      Write the parameters into Mailboxes 1 - 4
169          */
170         sa_writel(dev, Mailbox1, p1);
171         sa_writel(dev, Mailbox2, p2);
172         sa_writel(dev, Mailbox3, p3);
173         sa_writel(dev, Mailbox4, p4);
174
175         /*
176          *      Clear the synch command doorbell to start on a clean slate.
177          */
178         sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
179         /*
180          *      Signal that there is a new synch command
181          */
182         sa_writew(dev, DoorbellReg_s, DOORBELL_0);
183
184         ok = 0;
185         start = jiffies;
186
187         while(time_before(jiffies, start+30*HZ))
188         {
189                 /*
190                  *      Delay 5uS so that the monitor gets access
191                  */
192                 udelay(5);
193                 /*
194                  *      Mon110 will set doorbell0 bit when it has 
195                  *      completed the command.
196                  */
197                 if(sa_readw(dev, DoorbellReg_p) & DOORBELL_0)  {
198                         ok = 1;
199                         break;
200                 }
201                 msleep(1);
202         }
203
204         if (ok != 1)
205                 return -ETIMEDOUT;
206         /*
207          *      Clear the synch command doorbell.
208          */
209         sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
210         /*
211          *      Pull the synch status from Mailbox 0.
212          */
213         if (ret)
214                 *ret = sa_readl(dev, Mailbox0);
215         if (r1)
216                 *r1 = sa_readl(dev, Mailbox1);
217         if (r2)
218                 *r2 = sa_readl(dev, Mailbox2);
219         if (r3)
220                 *r3 = sa_readl(dev, Mailbox3);
221         if (r4)
222                 *r4 = sa_readl(dev, Mailbox4);
223         return 0;
224 }
225
226 /**
227  *      aac_sa_interrupt_adapter        -       interrupt an adapter
228  *      @dev: Which adapter to enable.
229  *
230  *      Breakpoint an adapter.
231  */
232  
233 static void aac_sa_interrupt_adapter (struct aac_dev *dev)
234 {
235         sa_sync_cmd(dev, BREAKPOINT_REQUEST, 0, 0, 0, 0, 0, 0,
236                         NULL, NULL, NULL, NULL, NULL);
237 }
238
239 /**
240  *      aac_sa_start_adapter            -       activate adapter
241  *      @dev:   Adapter
242  *
243  *      Start up processing on an ARM based AAC adapter
244  */
245
246 static void aac_sa_start_adapter(struct aac_dev *dev)
247 {
248         struct aac_init *init;
249         /*
250          * Fill in the remaining pieces of the init.
251          */
252         init = dev->init;
253         init->HostElapsedSeconds = cpu_to_le32(get_seconds());
254         /* We can only use a 32 bit address here */
255         sa_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, 
256                         (u32)(ulong)dev->init_pa, 0, 0, 0, 0, 0,
257                         NULL, NULL, NULL, NULL, NULL);
258 }
259
260 /**
261  *      aac_sa_check_health
262  *      @dev: device to check if healthy
263  *
264  *      Will attempt to determine if the specified adapter is alive and
265  *      capable of handling requests, returning 0 if alive.
266  */
267 static int aac_sa_check_health(struct aac_dev *dev)
268 {
269         long status = sa_readl(dev, Mailbox7);
270
271         /*
272          *      Check to see if the board failed any self tests.
273          */
274         if (status & SELF_TEST_FAILED)
275                 return -1;
276         /*
277          *      Check to see if the board panic'd while booting.
278          */
279         if (status & KERNEL_PANIC)
280                 return -2;
281         /*
282          *      Wait for the adapter to be up and running. Wait up to 3 minutes
283          */
284         if (!(status & KERNEL_UP_AND_RUNNING))
285                 return -3;
286         /*
287          *      Everything is OK
288          */
289         return 0;
290 }
291
292 /**
293  *      aac_sa_ioremap
294  *      @size: mapping resize request
295  *
296  */
297 static int aac_sa_ioremap(struct aac_dev * dev, u32 size)
298 {
299         if (!size) {
300                 iounmap(dev->regs.sa);
301                 return 0;
302         }
303         dev->base = dev->regs.sa = ioremap(dev->scsi_host_ptr->base, size);
304         return (dev->base == NULL) ? -1 : 0;
305 }
306
307 /**
308  *      aac_sa_init     -       initialize an ARM based AAC card
309  *      @dev: device to configure
310  *
311  *      Allocate and set up resources for the ARM based AAC variants. The 
312  *      device_interface in the commregion will be allocated and linked 
313  *      to the comm region.
314  */
315
316 int aac_sa_init(struct aac_dev *dev)
317 {
318         unsigned long start;
319         unsigned long status;
320         int instance;
321         const char *name;
322
323         instance = dev->id;
324         name     = dev->name;
325
326         if (aac_sa_ioremap(dev, dev->base_size)) {
327                 printk(KERN_WARNING "%s: unable to map adapter.\n", name);
328                 goto error_iounmap;
329         }
330
331         /*
332          *      Check to see if the board failed any self tests.
333          */
334         if (sa_readl(dev, Mailbox7) & SELF_TEST_FAILED) {
335                 printk(KERN_WARNING "%s%d: adapter self-test failed.\n", name, instance);
336                 goto error_iounmap;
337         }
338         /*
339          *      Check to see if the board panic'd while booting.
340          */
341         if (sa_readl(dev, Mailbox7) & KERNEL_PANIC) {
342                 printk(KERN_WARNING "%s%d: adapter kernel panic'd.\n", name, instance);
343                 goto error_iounmap;
344         }
345         start = jiffies;
346         /*
347          *      Wait for the adapter to be up and running. Wait up to 3 minutes.
348          */
349         while (!(sa_readl(dev, Mailbox7) & KERNEL_UP_AND_RUNNING)) {
350                 if (time_after(jiffies, start+startup_timeout*HZ)) {
351                         status = sa_readl(dev, Mailbox7);
352                         printk(KERN_WARNING "%s%d: adapter kernel failed to start, init status = %lx.\n", 
353                                         name, instance, status);
354                         goto error_iounmap;
355                 }
356                 msleep(1);
357         }
358
359         /*
360          *      Fill in the function dispatch table.
361          */
362
363         dev->a_ops.adapter_interrupt = aac_sa_interrupt_adapter;
364         dev->a_ops.adapter_disable_int = aac_sa_disable_interrupt;
365         dev->a_ops.adapter_enable_int = aac_sa_enable_interrupt;
366         dev->a_ops.adapter_notify = aac_sa_notify_adapter;
367         dev->a_ops.adapter_sync_cmd = sa_sync_cmd;
368         dev->a_ops.adapter_check_health = aac_sa_check_health;
369         dev->a_ops.adapter_intr = aac_sa_intr;
370         dev->a_ops.adapter_ioremap = aac_sa_ioremap;
371
372         /*
373          *      First clear out all interrupts.  Then enable the one's that 
374          *      we can handle.
375          */
376         aac_adapter_disable_int(dev);
377         aac_adapter_enable_int(dev);
378
379         if(aac_init_adapter(dev) == NULL)
380                 goto error_irq;
381         if (request_irq(dev->scsi_host_ptr->irq, dev->a_ops.adapter_intr,
382                         IRQF_SHARED|IRQF_DISABLED,
383                         "aacraid", (void *)dev ) < 0) {
384                 printk(KERN_WARNING "%s%d: Interrupt unavailable.\n",
385                         name, instance);
386                 goto error_iounmap;
387         }
388         aac_adapter_enable_int(dev);
389
390         /*
391          *      Tell the adapter that all is configure, and it can start 
392          *      accepting requests
393          */
394         aac_sa_start_adapter(dev);
395         return 0;
396
397 error_irq:
398         aac_sa_disable_interrupt(dev);
399         free_irq(dev->scsi_host_ptr->irq, (void *)dev);
400
401 error_iounmap:
402
403         return -1;
404 }
405