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