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