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