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