diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/aacraid/rx.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/aacraid/rx.c')
-rw-r--r-- | drivers/scsi/aacraid/rx.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c new file mode 100644 index 000000000000..630b99e1fe83 --- /dev/null +++ b/drivers/scsi/aacraid/rx.c | |||
@@ -0,0 +1,441 @@ | |||
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 | * rx.c | ||
26 | * | ||
27 | * Abstract: Hardware miniport for Drawbridge specific hardware 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 | |||
49 | static irqreturn_t aac_rx_intr(int irq, void *dev_id, struct pt_regs *regs) | ||
50 | { | ||
51 | struct aac_dev *dev = dev_id; | ||
52 | unsigned long bellbits; | ||
53 | u8 intstat, mask; | ||
54 | intstat = rx_readb(dev, MUnit.OISR); | ||
55 | /* | ||
56 | * Read mask and invert because drawbridge is reversed. | ||
57 | * This allows us to only service interrupts that have | ||
58 | * been enabled. | ||
59 | */ | ||
60 | mask = ~(dev->OIMR); | ||
61 | /* Check to see if this is our interrupt. If it isn't just return */ | ||
62 | if (intstat & mask) | ||
63 | { | ||
64 | bellbits = rx_readl(dev, OutboundDoorbellReg); | ||
65 | if (bellbits & DoorBellPrintfReady) { | ||
66 | aac_printf(dev, le32_to_cpu(rx_readl (dev, IndexRegs.Mailbox[5]))); | ||
67 | rx_writel(dev, MUnit.ODR,DoorBellPrintfReady); | ||
68 | rx_writel(dev, InboundDoorbellReg,DoorBellPrintfDone); | ||
69 | } | ||
70 | else if (bellbits & DoorBellAdapterNormCmdReady) { | ||
71 | rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdReady); | ||
72 | aac_command_normal(&dev->queues->queue[HostNormCmdQueue]); | ||
73 | } | ||
74 | else if (bellbits & DoorBellAdapterNormRespReady) { | ||
75 | aac_response_normal(&dev->queues->queue[HostNormRespQueue]); | ||
76 | rx_writel(dev, MUnit.ODR,DoorBellAdapterNormRespReady); | ||
77 | } | ||
78 | else if (bellbits & DoorBellAdapterNormCmdNotFull) { | ||
79 | rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull); | ||
80 | } | ||
81 | else if (bellbits & DoorBellAdapterNormRespNotFull) { | ||
82 | rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull); | ||
83 | rx_writel(dev, MUnit.ODR, DoorBellAdapterNormRespNotFull); | ||
84 | } | ||
85 | return IRQ_HANDLED; | ||
86 | } | ||
87 | return IRQ_NONE; | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * rx_sync_cmd - send a command and wait | ||
92 | * @dev: Adapter | ||
93 | * @command: Command to execute | ||
94 | * @p1: first parameter | ||
95 | * @ret: adapter status | ||
96 | * | ||
97 | * This routine will send a synchronous command to the adapter and wait | ||
98 | * for its completion. | ||
99 | */ | ||
100 | |||
101 | static int rx_sync_cmd(struct aac_dev *dev, u32 command, u32 p1, u32 *status) | ||
102 | { | ||
103 | unsigned long start; | ||
104 | int ok; | ||
105 | /* | ||
106 | * Write the command into Mailbox 0 | ||
107 | */ | ||
108 | rx_writel(dev, InboundMailbox0, command); | ||
109 | /* | ||
110 | * Write the parameters into Mailboxes 1 - 4 | ||
111 | */ | ||
112 | rx_writel(dev, InboundMailbox1, p1); | ||
113 | rx_writel(dev, InboundMailbox2, 0); | ||
114 | rx_writel(dev, InboundMailbox3, 0); | ||
115 | rx_writel(dev, InboundMailbox4, 0); | ||
116 | /* | ||
117 | * Clear the synch command doorbell to start on a clean slate. | ||
118 | */ | ||
119 | rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0); | ||
120 | /* | ||
121 | * Disable doorbell interrupts | ||
122 | */ | ||
123 | rx_writeb(dev, MUnit.OIMR, dev->OIMR |= 0x04); | ||
124 | /* | ||
125 | * Force the completion of the mask register write before issuing | ||
126 | * the interrupt. | ||
127 | */ | ||
128 | rx_readb (dev, MUnit.OIMR); | ||
129 | /* | ||
130 | * Signal that there is a new synch command | ||
131 | */ | ||
132 | rx_writel(dev, InboundDoorbellReg, INBOUNDDOORBELL_0); | ||
133 | |||
134 | ok = 0; | ||
135 | start = jiffies; | ||
136 | |||
137 | /* | ||
138 | * Wait up to 30 seconds | ||
139 | */ | ||
140 | while (time_before(jiffies, start+30*HZ)) | ||
141 | { | ||
142 | udelay(5); /* Delay 5 microseconds to let Mon960 get info. */ | ||
143 | /* | ||
144 | * Mon960 will set doorbell0 bit when it has completed the command. | ||
145 | */ | ||
146 | if (rx_readl(dev, OutboundDoorbellReg) & OUTBOUNDDOORBELL_0) { | ||
147 | /* | ||
148 | * Clear the doorbell. | ||
149 | */ | ||
150 | rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0); | ||
151 | ok = 1; | ||
152 | break; | ||
153 | } | ||
154 | /* | ||
155 | * Yield the processor in case we are slow | ||
156 | */ | ||
157 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
158 | schedule_timeout(1); | ||
159 | } | ||
160 | if (ok != 1) { | ||
161 | /* | ||
162 | * Restore interrupt mask even though we timed out | ||
163 | */ | ||
164 | rx_writeb(dev, MUnit.OIMR, dev->OIMR &= 0xfb); | ||
165 | return -ETIMEDOUT; | ||
166 | } | ||
167 | /* | ||
168 | * Pull the synch status from Mailbox 0. | ||
169 | */ | ||
170 | if (status) | ||
171 | *status = rx_readl(dev, IndexRegs.Mailbox[0]); | ||
172 | /* | ||
173 | * Clear the synch command doorbell. | ||
174 | */ | ||
175 | rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0); | ||
176 | /* | ||
177 | * Restore interrupt mask | ||
178 | */ | ||
179 | rx_writeb(dev, MUnit.OIMR, dev->OIMR &= 0xfb); | ||
180 | return 0; | ||
181 | |||
182 | } | ||
183 | |||
184 | /** | ||
185 | * aac_rx_interrupt_adapter - interrupt adapter | ||
186 | * @dev: Adapter | ||
187 | * | ||
188 | * Send an interrupt to the i960 and breakpoint it. | ||
189 | */ | ||
190 | |||
191 | static void aac_rx_interrupt_adapter(struct aac_dev *dev) | ||
192 | { | ||
193 | u32 ret; | ||
194 | rx_sync_cmd(dev, BREAKPOINT_REQUEST, 0, &ret); | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * aac_rx_notify_adapter - send an event to the adapter | ||
199 | * @dev: Adapter | ||
200 | * @event: Event to send | ||
201 | * | ||
202 | * Notify the i960 that something it probably cares about has | ||
203 | * happened. | ||
204 | */ | ||
205 | |||
206 | static void aac_rx_notify_adapter(struct aac_dev *dev, u32 event) | ||
207 | { | ||
208 | switch (event) { | ||
209 | |||
210 | case AdapNormCmdQue: | ||
211 | rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_1); | ||
212 | break; | ||
213 | case HostNormRespNotFull: | ||
214 | rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_4); | ||
215 | break; | ||
216 | case AdapNormRespQue: | ||
217 | rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_2); | ||
218 | break; | ||
219 | case HostNormCmdNotFull: | ||
220 | rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_3); | ||
221 | break; | ||
222 | case HostShutdown: | ||
223 | // rx_sync_cmd(dev, HOST_CRASHING, 0, 0, 0, 0, &ret); | ||
224 | break; | ||
225 | case FastIo: | ||
226 | rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_6); | ||
227 | break; | ||
228 | case AdapPrintfDone: | ||
229 | rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_5); | ||
230 | break; | ||
231 | default: | ||
232 | BUG(); | ||
233 | break; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * aac_rx_start_adapter - activate adapter | ||
239 | * @dev: Adapter | ||
240 | * | ||
241 | * Start up processing on an i960 based AAC adapter | ||
242 | */ | ||
243 | |||
244 | static void aac_rx_start_adapter(struct aac_dev *dev) | ||
245 | { | ||
246 | u32 status; | ||
247 | struct aac_init *init; | ||
248 | |||
249 | init = dev->init; | ||
250 | init->HostElapsedSeconds = cpu_to_le32(get_seconds()); | ||
251 | /* | ||
252 | * Tell the adapter we are back and up and running so it will scan | ||
253 | * its command queues and enable our interrupts | ||
254 | */ | ||
255 | dev->irq_mask = (DoorBellPrintfReady | OUTBOUNDDOORBELL_1 | OUTBOUNDDOORBELL_2 | OUTBOUNDDOORBELL_3 | OUTBOUNDDOORBELL_4); | ||
256 | /* | ||
257 | * First clear out all interrupts. Then enable the one's that we | ||
258 | * can handle. | ||
259 | */ | ||
260 | rx_writeb(dev, MUnit.OIMR, 0xff); | ||
261 | rx_writel(dev, MUnit.ODR, 0xffffffff); | ||
262 | // rx_writeb(dev, MUnit.OIMR, ~(u8)OUTBOUND_DOORBELL_INTERRUPT_MASK); | ||
263 | rx_writeb(dev, MUnit.OIMR, dev->OIMR = 0xfb); | ||
264 | |||
265 | // We can only use a 32 bit address here | ||
266 | rx_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa, &status); | ||
267 | } | ||
268 | |||
269 | /** | ||
270 | * aac_rx_check_health | ||
271 | * @dev: device to check if healthy | ||
272 | * | ||
273 | * Will attempt to determine if the specified adapter is alive and | ||
274 | * capable of handling requests, returning 0 if alive. | ||
275 | */ | ||
276 | static int aac_rx_check_health(struct aac_dev *dev) | ||
277 | { | ||
278 | u32 status = rx_readl(dev, MUnit.OMRx[0]); | ||
279 | |||
280 | /* | ||
281 | * Check to see if the board failed any self tests. | ||
282 | */ | ||
283 | if (status & SELF_TEST_FAILED) | ||
284 | return -1; | ||
285 | /* | ||
286 | * Check to see if the board panic'd. | ||
287 | */ | ||
288 | if (status & KERNEL_PANIC) { | ||
289 | char * buffer; | ||
290 | struct POSTSTATUS { | ||
291 | u32 Post_Command; | ||
292 | u32 Post_Address; | ||
293 | } * post; | ||
294 | dma_addr_t paddr, baddr; | ||
295 | int ret; | ||
296 | |||
297 | if ((status & 0xFF000000L) == 0xBC000000L) | ||
298 | return (status >> 16) & 0xFF; | ||
299 | buffer = pci_alloc_consistent(dev->pdev, 512, &baddr); | ||
300 | ret = -2; | ||
301 | if (buffer == NULL) | ||
302 | return ret; | ||
303 | post = pci_alloc_consistent(dev->pdev, | ||
304 | sizeof(struct POSTSTATUS), &paddr); | ||
305 | if (post == NULL) { | ||
306 | pci_free_consistent(dev->pdev, 512, buffer, baddr); | ||
307 | return ret; | ||
308 | } | ||
309 | memset(buffer, 0, 512); | ||
310 | post->Post_Command = cpu_to_le32(COMMAND_POST_RESULTS); | ||
311 | post->Post_Address = cpu_to_le32(baddr); | ||
312 | rx_writel(dev, MUnit.IMRx[0], paddr); | ||
313 | rx_sync_cmd(dev, COMMAND_POST_RESULTS, baddr, &status); | ||
314 | pci_free_consistent(dev->pdev, sizeof(struct POSTSTATUS), | ||
315 | post, paddr); | ||
316 | if ((buffer[0] == '0') && (buffer[1] == 'x')) { | ||
317 | ret = (buffer[2] <= '9') ? (buffer[2] - '0') : (buffer[2] - 'A' + 10); | ||
318 | ret <<= 4; | ||
319 | ret += (buffer[3] <= '9') ? (buffer[3] - '0') : (buffer[3] - 'A' + 10); | ||
320 | } | ||
321 | pci_free_consistent(dev->pdev, 512, buffer, baddr); | ||
322 | return ret; | ||
323 | } | ||
324 | /* | ||
325 | * Wait for the adapter to be up and running. | ||
326 | */ | ||
327 | if (!(status & KERNEL_UP_AND_RUNNING)) | ||
328 | return -3; | ||
329 | /* | ||
330 | * Everything is OK | ||
331 | */ | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | /** | ||
336 | * aac_rx_init - initialize an i960 based AAC card | ||
337 | * @dev: device to configure | ||
338 | * | ||
339 | * Allocate and set up resources for the i960 based AAC variants. The | ||
340 | * device_interface in the commregion will be allocated and linked | ||
341 | * to the comm region. | ||
342 | */ | ||
343 | |||
344 | int aac_rx_init(struct aac_dev *dev) | ||
345 | { | ||
346 | unsigned long start; | ||
347 | unsigned long status; | ||
348 | int instance; | ||
349 | const char * name; | ||
350 | |||
351 | instance = dev->id; | ||
352 | name = dev->name; | ||
353 | |||
354 | /* | ||
355 | * Map in the registers from the adapter. | ||
356 | */ | ||
357 | if((dev->regs.rx = ioremap((unsigned long)dev->scsi_host_ptr->base, 8192))==NULL) | ||
358 | { | ||
359 | printk(KERN_WARNING "aacraid: unable to map i960.\n" ); | ||
360 | return -1; | ||
361 | } | ||
362 | /* | ||
363 | * Check to see if the board failed any self tests. | ||
364 | */ | ||
365 | if (rx_readl(dev, MUnit.OMRx[0]) & SELF_TEST_FAILED) { | ||
366 | printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance); | ||
367 | goto error_iounmap; | ||
368 | } | ||
369 | /* | ||
370 | * Check to see if the board panic'd while booting. | ||
371 | */ | ||
372 | if (rx_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) { | ||
373 | printk(KERN_ERR "%s%d: adapter kernel panic.\n", dev->name, instance); | ||
374 | goto error_iounmap; | ||
375 | } | ||
376 | /* | ||
377 | * Check to see if the monitor panic'd while booting. | ||
378 | */ | ||
379 | if (rx_readl(dev, MUnit.OMRx[0]) & MONITOR_PANIC) { | ||
380 | printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance); | ||
381 | goto error_iounmap; | ||
382 | } | ||
383 | start = jiffies; | ||
384 | /* | ||
385 | * Wait for the adapter to be up and running. Wait up to 3 minutes | ||
386 | */ | ||
387 | while ((!(rx_readl(dev, IndexRegs.Mailbox[7]) & KERNEL_UP_AND_RUNNING)) | ||
388 | || (!(rx_readl(dev, MUnit.OMRx[0]) & KERNEL_UP_AND_RUNNING))) | ||
389 | { | ||
390 | if(time_after(jiffies, start+180*HZ)) | ||
391 | { | ||
392 | status = rx_readl(dev, IndexRegs.Mailbox[7]); | ||
393 | printk(KERN_ERR "%s%d: adapter kernel failed to start, init status = %lx.\n", | ||
394 | dev->name, instance, status); | ||
395 | goto error_iounmap; | ||
396 | } | ||
397 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
398 | schedule_timeout(1); | ||
399 | } | ||
400 | if (request_irq(dev->scsi_host_ptr->irq, aac_rx_intr, SA_SHIRQ|SA_INTERRUPT, "aacraid", (void *)dev)<0) | ||
401 | { | ||
402 | printk(KERN_ERR "%s%d: Interrupt unavailable.\n", name, instance); | ||
403 | goto error_iounmap; | ||
404 | } | ||
405 | /* | ||
406 | * Fill in the function dispatch table. | ||
407 | */ | ||
408 | dev->a_ops.adapter_interrupt = aac_rx_interrupt_adapter; | ||
409 | dev->a_ops.adapter_notify = aac_rx_notify_adapter; | ||
410 | dev->a_ops.adapter_sync_cmd = rx_sync_cmd; | ||
411 | dev->a_ops.adapter_check_health = aac_rx_check_health; | ||
412 | |||
413 | if (aac_init_adapter(dev) == NULL) | ||
414 | goto error_irq; | ||
415 | /* | ||
416 | * Start any kernel threads needed | ||
417 | */ | ||
418 | dev->thread_pid = kernel_thread((int (*)(void *))aac_command_thread, dev, 0); | ||
419 | if(dev->thread_pid < 0) | ||
420 | { | ||
421 | printk(KERN_ERR "aacraid: Unable to create rx thread.\n"); | ||
422 | goto error_kfree; | ||
423 | } | ||
424 | /* | ||
425 | * Tell the adapter that all is configured, and it can start | ||
426 | * accepting requests | ||
427 | */ | ||
428 | aac_rx_start_adapter(dev); | ||
429 | return 0; | ||
430 | |||
431 | error_kfree: | ||
432 | kfree(dev->queues); | ||
433 | |||
434 | error_irq: | ||
435 | free_irq(dev->scsi_host_ptr->irq, (void *)dev); | ||
436 | |||
437 | error_iounmap: | ||
438 | iounmap(dev->regs.rx); | ||
439 | |||
440 | return -1; | ||
441 | } | ||