dpt_pci.c revision 34480
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.4 1998/02/20 13:11:50 bde 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 ( !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