dpt_pci.c revision 33676
1/* 2 * Copyright (c) 1997 by Simon Shapiro 3 * All Rights Reserved 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions, and the following disclaimer, 10 * without modification, immediately at the beginning of the file. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31/* 32 * dptpci.c: Pseudo device drivers for DPT on PCI on FreeBSD 33 * 34 * caveats: We may need an eisa and an isa files too 35 */ 36 37#ident "$Id: dpt_pci.c,v 1.3 1998/02/10 17:36:41 eivind Exp $" 38 39#include "opt_devfs.h" 40#include "opt_dpt.h" 41 42#include <sys/param.h> 43#include <sys/systm.h> 44#include <sys/malloc.h> 45#include <sys/buf.h> 46#include <sys/kernel.h> 47 48#include <scsi/scsiconf.h> 49 50#include <pci/pcireg.h> 51#include <pci/pcivar.h> 52 53#include <sys/dpt.h> 54#include <pci/dpt_pci.h> 55 56#include <vm/vm.h> 57#include <vm/pmap.h> 58 59#define PCI_BASEADR0 PCI_MAP_REG_START /* I/O Address */ 60#define PCI_BASEADR1 PCI_MAP_REG_START + 4 /* Mem I/O Address */ 61 62#define ISA_PRIMARY_WD_ADDRESS 0x1f8 63 64/* Global variables */ 65 66int dpt_controllers_present = 0; 67 68/* Function Prototypes */ 69 70static char *dpt_pci_probe(pcici_t tag, pcidi_t type); 71static void dpt_pci_attach(pcici_t config_id, int unit); 72static int dpt_pci_shutdown(int foo, int bar); 73 74extern struct cdevsw dpt_cdevsw; 75 76static struct pci_device dpt_pci_driver = 77{ 78 "dpt", 79 dpt_pci_probe, 80 dpt_pci_attach, 81 &dpt_unit, 82 dpt_pci_shutdown 83}; 84 85DATA_SET(pcidevice_set, dpt_pci_driver); 86 87/* 88 * Probe the PCI device. 89 * Some of this work will have to be duplicated in _attach 90 * because we do not know for sure how the two relate. 91 */ 92 93static char * 94dpt_pci_probe(pcici_t tag, pcidi_t type) 95{ 96 static char silly_message[64]; 97 static int already_announced = 0; 98 99 u_int32_t dpt_id; 100 u_int32_t command; 101 u_int32_t class; 102 103#define pci_device tag.cfg2.port 104#define pci_bus tag.cfg2.forward 105#define pci_index tag.cfg2.enable 106 107#ifndef PCI_COMMAND_MASTER_ENABLE 108#define PCI_COMMAND_MASTER_ENABLE 0x00000004 109#endif 110 111#ifndef PCI_SUBCLASS_MASS_STORAGE_SCSI 112#define PCI_SUBCLASS_MASS_STORAGE_SCSI 0x00000000 113#endif 114 115 if ( !already_announced ) { 116 printf("DPT: PCI SCSI HBA Driver, version %d.%d.%d\n", 117 DPT_RELEASE, DPT_VERSION, DPT_PATCH); 118 ++already_announced; 119 } 120 121 if ((dpt_id = (type & 0xffff0000) >> 16) == DPT_DEVICE_ID) { 122 /* This one appears to belong to us, but what is it? */ 123 class = pci_conf_read(tag, PCI_CLASS_REG); 124 if (((class & PCI_CLASS_MASK) == PCI_CLASS_MASS_STORAGE) && 125 ((class & PCI_SUBCLASS_MASK) == PCI_SUBCLASS_MASS_STORAGE_SCSI) ) { 126 /* It is a SCSI storage device. How do talk to it? */ 127 command = pci_conf_read(tag, PCI_COMMAND_STATUS_REG); 128#ifdef DPT_ALLOW_MEMIO 129 if ( ((command & PCI_COMMAND_IO_ENABLE) == 0) 130 && ((command & PCI_COMMAND_MEM_ENABLE) == 0) ) 131#else 132 if ( ((command & PCI_COMMAND_IO_ENABLE) == 0) ) 133#endif /* DPT_ALLOW_MEMIO */ 134 { 135 printf("DPT: Cannot map the controller registers :-(\n"); 136 return(NULL); 137 } 138 } else { 139 printf("DPT: Device is not Mass Storage, nor SCSI controller\n"); 140 return(NULL); 141 } 142 143 command = pci_conf_read(tag, PCI_COMMAND_STATUS_REG); 144 if ( (command & PCI_COMMAND_MASTER_ENABLE) == 0 ) { 145 printf("DPT: Cannot be functional without BUSMASTER. :-(\n"); 146 return (NULL); 147 } 148 149#ifdef DPT_DEBUG_PCI 150 printf("DPT: Controller is %s mapable\n", 151 (command & PCI_COMMAND_MEM_ENABLE) 152 ? "MEMORY" 153 : ((command & PCI_COMMAND_IO_ENABLE) 154 ? "I/O" 155 : "NOT")); 156#endif 157 return ("DPT Caching SCSI RAID Controller"); 158 } 159 160#if defined(DPT_DEBUG_PCI) && defined(DPT_DEBUG_WARN) 161 printf("DPT: Unknown Controller Type %x Found\n", dpt_id); 162 printf(" (class = %x, command = %x\n", class, command); 163#endif 164 return (NULL); 165} 166 167static void 168dpt_pci_attach(pcici_t config_id, int unit) 169{ 170 int ospl; 171 int result; 172 int ndx; 173 174 vm_offset_t vaddr; 175 vm_offset_t paddr; 176 u_int16_t io_base; 177 u_int32_t command; 178 u_int32_t data; 179 dpt_conf_t *config; 180 dpt_softc_t *dpt; 181 182 if (dpt_controllers_present >= DPT_MAX_ADAPTERS) { 183 printf("dpt%d: More than %d Adapters found! Adapter rejected\n", 184 unit, DPT_MAX_ADAPTERS); 185 return; 186 } 187 188 if ((dpt = (dpt_softc_t *) malloc(sizeof(dpt_softc_t), M_DEVBUF, M_NOWAIT)) 189 == NULL) { 190 printf("dpt%d: Failed to allocate %d bytes for a DPT softc\n", 191 unit, sizeof(dpt_softc_t)); 192 return; 193 } 194 195 /* 196 * Initialize the queues. See dpt.h for details. We do this here, 197 * as we may get hit with interrupts at any moment and we want to 198 * have a minimal structure in place to handle them. We also want to 199 * register interrupts correctly. To do so, we need a valid dpt 200 * structure. To have that, we need this minimal setup here. 201 */ 202 bzero(dpt, sizeof(dpt_softc_t)); 203 204 TAILQ_INIT(&dpt->free_ccbs); 205 TAILQ_INIT(&dpt->waiting_ccbs); 206 TAILQ_INIT(&dpt->submitted_ccbs); 207 TAILQ_INIT(&dpt->completed_ccbs); 208 209 if (TAILQ_EMPTY(&dpt_softc_list)) { 210 TAILQ_INIT(&dpt_softc_list); 211 } 212 213 TAILQ_INSERT_TAIL(&dpt_softc_list, dpt, links); 214 dpt->queue_status = DPT_QUEUES_NONE_ACTIVE; 215 dpt->commands_processed = 0; 216 217#ifdef DPT_MEASURE_PERFORMANCE 218 /* Zero out all command counters */ 219 bzero((void *)&dpt->performance, sizeof(dpt_perf_t)); 220 for ( ndx = 0; ndx < 256; ndx ++ ) 221 dpt->performance.min_command_time[ndx] = BIG_ENOUGH; 222 223 dpt->performance.min_intr_time = BIG_ENOUGH; 224 dpt->performance.min_waiting_time = BIG_ENOUGH; 225 dpt->performance.min_submit_time = BIG_ENOUGH; 226 dpt->performance.min_complete_time = BIG_ENOUGH; 227 dpt->performance.min_eata_tries = BIG_ENOUGH; 228 229 for (ndx = 0; ndx < 10; ndx++ ) { 230 dpt->performance.read_by_size_min_time[ndx] = BIG_ENOUGH; 231 dpt->performance.write_by_size_min_time[ndx] = BIG_ENOUGH; 232 } 233#endif /* DPT_MEASURE_PERFORMANCE */ 234 235 dpt->unit = unit; 236 dpt->handle_interrupts = 0; /* 237 * Do not set to 1 until all 238 * initialization is done 239 */ 240 dpt->v_membase = NULL; 241 dpt->p_membase = NULL; 242 io_base = 0; 243 vaddr = 0; 244 paddr = 0; 245 command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); 246 247#ifdef DPT_ALLOW_MEMIO 248 if ( (command & PCI_COMMAND_MEM_ENABLE) == 0 ) { 249#ifdef DPT_DEBUG_PCI 250 printf("dpt%d: Cannot be memory mapped\n", unit); 251#endif 252 force_io: 253 if ((command & PCI_COMMAND_IO_ENABLE) == 0 ) { 254 printf("dpt%d: Cannot be I/O mapped either :-(\n", unit); 255 free(dpt, M_DEVBUF); 256 return; 257 } else { 258 data = pci_conf_read(config_id, PCI_MAP_REG_START); 259 if ( pci_map_port(config_id, PCI_MAP_REG_START, &io_base) == 0 ) { 260#ifdef DPT_DEBUG_ERROR 261 printf("dpt%d: Failed to map as I/O :-(\n", unit); 262#endif 263 free(dpt, M_DEVBUF); 264 return; 265 } else { 266 dpt->io_base = io_base + 0x10; 267#ifdef DPT_DEBUG_PCI 268 printf("dpt%d: Mapped registers to I/O space, " 269 "starting at %x\n", 270 dpt->unit, dpt->io_base); 271#endif 272 } 273 } 274 } else { 275 if ( pci_map_mem(config_id, PCI_MAP_REG_START + 4, &vaddr, 276 &paddr) == 0 ) { 277#ifdef DPT_DEBUG_ERROR 278 printf("dpt%d: Failed to map as MEMORY.\n" 279 " Attemting to force I/O mapping\n", unit); 280#endif 281 goto force_io; 282 } else { 283 dpt->v_membase = (volatile u_int8_t *)(vaddr + 0x10); 284 dpt->p_membase = (volatile u_int8_t *)(paddr + 0x10); 285#ifdef DPT_DEBUG_PCI 286 printf("dpt%d: Mapped registers to MEMORY space, " 287 "starting at %x/%x\n", 288 dpt->unit, dpt->v_membase, dpt->p_membase); 289#endif 290 } 291 } 292 293#else /* !DPT_ALLOW_MEMIO */ 294 data = pci_conf_read(config_id, PCI_MAP_REG_START); 295 if ((command & PCI_COMMAND_IO_ENABLE) == 0 ) { 296 printf("dpt%d: Registers cannot be I/O mapped :-(\n", unit); 297 free(dpt, M_DEVBUF); 298 return; 299 } else { 300 if ( pci_map_port(config_id, PCI_MAP_REG_START, &io_base) == 0 ) { 301#ifdef DPT_DEBUG_ERROR 302 printf("dpt%d: Failed to map registers as I/O :-(\n", unit); 303#endif 304 free(dpt, M_DEVBUF); 305 return; 306 } else { 307 dpt->io_base = io_base + 0x10; 308#ifdef DPT_DEBUG_PCI 309 printf("dpt%d: Mapped registers to I/O space, starting at %x\n", 310 dpt->unit, dpt->io_base); 311#endif 312 } 313 } 314#endif /* !DPT_ALLOW_MEMIO */ 315 316 if (pci_map_int(config_id, dpt_intr, (void *)dpt, &cam_imask) == 0) { 317#ifdef DPT_DEBUG_WARN 318 printf("dpt%d: Failed to map interrupt :-(\n", unit); 319#endif 320 free(dpt, M_DEVBUF); 321 return; 322 } 323 324 /* If the DPT is mapped as an IDE controller, let it be IDE controller */ 325 if (io_base == (ISA_PRIMARY_WD_ADDRESS)) { 326#ifdef DPT_DEBUG_WARN 327 printf("dpt%d: Mapped as an IDE controller. " 328 "Disabling SCSI setup\n", unit); 329#endif 330 free(dpt, M_DEVBUF); 331 return; 332 } else { 333 if ((config = dpt_get_conf(dpt, 0xc1, 7, 334 sizeof(dpt_conf_t), 1)) == NULL) { 335#ifdef DPT_DEBUG_ERROR 336 printf("dpt%d: Failed to get board configuration (%x)\n", 337 unit, BaseRegister(dpt)); 338#endif 339 free(dpt, M_DEVBUF); 340 return; 341 } 342 } 343 344 dpt->max_id = config->MAX_ID; 345 dpt->max_lun = config->MAX_LUN; 346 dpt->irq = config->IRQ; 347 dpt->channels = config->MAX_CHAN; 348 dpt->dma_channel = (8 - config->DMA_channel) & 7; 349 350#ifdef DPT_DEBUG_SETUP 351 printf("dpt%d: max_id = %d, max_chan = %d, max_lun = %d\n", 352 dpt->unit, dpt->max_id, dpt->channels, dpt->max_lun); 353#endif 354 355 if (result = dpt_setup(dpt, config)) { 356 free(config, M_TEMP); 357 free(dpt, M_DEVBUF); 358 printf("dpt%d: dpt_setup failed (%d). Driver Disabled :-(\n", 359 dpt->unit, result); 360 } else { 361 /* clean up the informational data, and display */ 362 char clean_vendor[9]; 363 char clean_model[17]; 364 char clean_firmware[5]; 365 char clean_protocol[5]; 366 char clean_other[7]; 367 368 int ndx; 369 370 strncpy(clean_other, dpt->board_data.otherData, 8); 371 clean_other[6] = '\0'; 372 for (ndx = 5; ndx >= 0; ndx--) { 373 if (clean_other[ndx] == ' ') 374 clean_other[ndx] = '\0'; 375 else 376 break; 377 } 378 strncpy(dpt->board_data.otherData, clean_other, 6); 379 380 strncpy(clean_vendor, dpt->board_data.vendor, 8); 381 clean_vendor[8] = '\0'; 382 for (ndx = 7; ndx >= 0; ndx--) { 383 if (clean_vendor[ndx] == ' ') 384 clean_vendor[ndx] = '\0'; 385 else 386 break; 387 } 388 strncpy(dpt->board_data.vendor, clean_vendor, 8); 389 390 strncpy(clean_model, dpt->board_data.modelNum, 16); 391 clean_model[16] = '\0'; 392 for (ndx = 15; ndx >= 0; ndx--) { 393 if (clean_model[ndx] == ' ') 394 clean_model[ndx] = '\0'; 395 else 396 break; 397 } 398 strncpy(dpt->board_data.modelNum, clean_model, 16); 399 400 strncpy(clean_firmware, dpt->board_data.firmware, 4); 401 clean_firmware[4] = '\0'; 402 for (ndx = 3; ndx >= 0; ndx--) { 403 if (clean_firmware[ndx] == ' ') 404 clean_firmware[ndx] = '\0'; 405 else 406 break; 407 } 408 strncpy(dpt->board_data.firmware, clean_firmware, 4); 409 410 strncpy(clean_protocol, dpt->board_data.protocol, 4); 411 clean_protocol[4] = '\0'; 412 for (ndx = 3; ndx >= 0; ndx--) { 413 if (clean_protocol[ndx] == ' ') 414 clean_protocol[ndx] = '\0'; 415 else 416 break; 417 } 418 strncpy(dpt->board_data.protocol, clean_protocol, 4); 419 420 dpt_detect_cache(dpt); 421 422 printf("dpt%d: %s type %x, model %s firmware %s, Protocol %s \n" 423 " on port %x with %s cache. LED = %s\n", 424 dpt->unit, clean_vendor, dpt->board_data.deviceType, 425 clean_model, clean_firmware, clean_protocol, dpt->io_base, 426 (dpt->cache_type == DPT_NO_CACHE) 427 ? "Disabled" 428 : (dpt->cache_type == DPT_CACHE_WRITETHROUGH) 429 ? "Write-Through" 430 : "Write-Back", 431 i2bin(dpt_blinking_led(dpt), 8)); 432 printf("dpt%d: Enabled Options:\n", dpt->unit); 433#ifdef DPT_LOST_IRQ 434 printf(" Recover Lost Interrupts\n"); 435#endif 436#ifdef DPT_VERIFY_HINTR 437 printf(" Verify Lost Transactions\n"); 438#endif 439#ifdef DPT_RESTRICTED_FREELIST 440 printf(" Restrict the Freelist Size\n"); 441#endif 442#ifdef DPT_MEASURE_PERFORMANCE 443 printf(" Collect Metrics\n"); 444#endif 445#ifdef DPT_FREELIST_IS_STACK 446 printf(" Optimize CPU Cache\n"); 447#endif 448#ifdef DPT_HANDLE_TIMEOUTS 449 printf(" Handle Timeouts\n"); 450#endif 451#ifdef DPT_ALLOW_MEMIO 452 printf(" Allow I/O to be Memeory Mapped\n"); 453#endif 454#ifdef DPT_HINTR_CHECK_SOFTC 455 printf(" Validate SoftC at Interrupt\n"); 456#endif 457 458 /* register shutdown handlers */ 459 result = at_shutdown((bootlist_fn)dpt_shutdown, (void *)dpt, 460 SHUTDOWN_POST_SYNC); 461 switch ( result ) { 462 case 0: 463#ifdef DPT_DEBUG_SHUTDOWN 464 printf("dpt%d: Shutdown handler registered\n", dpt->unit); 465#endif 466 break; 467 default: 468#ifdef DPT_DEBUG_WARN 469 printf("dpt%d: Failed to register shutdown handler (%d)\n", 470 dpt->unit, result); 471#endif 472 break; 473 } 474 475 /* Attach SCSI devices */ 476 dpt_attach(dpt); 477 ++dpt_controllers_present; 478 479 /* 480 * Now we create the DEVFS entry. 481 * This would be normally done from dpt_control.c, 482 * But since it appears to be called before we do here, 483 * We never get the entries made. 484 */ 485#ifdef DEVFS 486 dpt->devfs_data_token = devfs_add_devswf(&dpt_cdevsw, dpt->unit, DV_CHR, 487 UID_ROOT, GID_WHEEL, 0600, 488 "dpt%d", dpt->unit); 489 dpt->devfs_ctl_token = devfs_add_devswf(&dpt_cdevsw, 490 dpt->unit | SCSI_CONTROL_MASK, 491 DV_CHR, 492 UID_ROOT, GID_WHEEL, 0600, 493 "dpt%d.ctl", dpt->unit); 494#endif 495 } 496} 497 498static int 499dpt_pci_shutdown(int foo, int bar) 500{ 501#ifdef DPT_DEBUG_WARN 502 printf("dpt_pci_shutdown(%x, %x)\n", foo, bar); 503#endif 504 return (0); 505} 506 507/* End of the DPT PCI part of the driver */ 508 509/* 510 * Hello emacs, these are the 511 * Local Variables: 512 * c-indent-level: 8 513 * c-continued-statement-offset: 8 514 * c-continued-brace-offset: 0 515 * c-brace-offset: -8 516 * c-brace-imaginary-offset: 0 517 * c-argdecl-indent: 8 518 * c-label-offset: -8 519 * c++-hanging-braces: 1 520 * c++-access-specifier-offset: -8 521 * c++-empty-arglist-indent: 8 522 * c++-friend-offset: 0 523 * End: 524 */ 525