dpt_pci.c revision 38115
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.6 1998/06/02 00:32:38 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
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    dpt_reset_performance(dpt);
217#endif        /* DPT_MEASURE_PERFORMANCE */
218
219    dpt->unit = unit;
220    dpt->handle_interrupts = 0;  /*
221								  * Do not set to 1 until all
222								  * initialization is done
223								  */
224    dpt->v_membase = NULL;
225    dpt->p_membase = NULL;
226    io_base = 0;
227    vaddr   = 0;
228    paddr   = 0;
229    command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);
230
231#ifdef DPT_ALLOW_MEMIO
232    if ( (command & PCI_COMMAND_MEM_ENABLE) == 0 ) {
233#ifdef DPT_DEBUG_PCI
234	  printf("dpt%d: Cannot be memory mapped\n", unit);
235#endif
236	force_io:
237	  if ((command & PCI_COMMAND_IO_ENABLE) == 0 ) {
238	    printf("dpt%d: Cannot be I/O mapped either :-(\n", unit);
239	    free(dpt, M_DEVBUF);
240	    return;
241	  } else {
242	    data = pci_conf_read(config_id, PCI_MAP_REG_START);
243	    if ( pci_map_port(config_id, PCI_MAP_REG_START, &io_base) == 0 ) {
244#ifdef DPT_DEBUG_ERROR
245		  printf("dpt%d: Failed to map as I/O :-(\n", unit);
246#endif
247		  free(dpt, M_DEVBUF);
248		  return;
249	    } else {
250		  dpt->io_base = io_base + 0x10;
251#ifdef DPT_DEBUG_PCI
252		  printf("dpt%d: Mapped registers to I/O space, "
253				 "starting at %x\n",
254				 dpt->unit, dpt->io_base);
255#endif
256	    }
257	  }
258    } else {
259	  if ( pci_map_mem(config_id, PCI_MAP_REG_START + 4, &vaddr,
260					   &paddr) == 0 ) {
261#ifdef DPT_DEBUG_ERROR
262	    printf("dpt%d: Failed to map as MEMORY.\n"
263			   "  Attemting to force I/O mapping\n", unit);
264#endif
265	    goto force_io;
266	  } else {
267	    dpt->v_membase = (volatile u_int8_t *)(vaddr + 0x10);
268	    dpt->p_membase = (volatile u_int8_t *)(paddr + 0x10);
269#ifdef DPT_DEBUG_PCI
270	    printf("dpt%d: Mapped registers to MEMORY space, "
271			   "starting at %x/%x\n",
272			   dpt->unit, dpt->v_membase, dpt->p_membase);
273#endif
274	  }
275    }
276
277#else /* !DPT_ALLOW_MEMIO */
278    data = pci_conf_read(config_id, PCI_MAP_REG_START);
279    if ((command & PCI_COMMAND_IO_ENABLE) == 0 ) {
280	  printf("dpt%d: Registers cannot be I/O mapped :-(\n", unit);
281	  free(dpt, M_DEVBUF);
282	  return;
283    } else {
284	  if ( pci_map_port(config_id, PCI_MAP_REG_START, &io_base) == 0 ) {
285#ifdef DPT_DEBUG_ERROR
286	    printf("dpt%d: Failed to map registers as I/O :-(\n", unit);
287#endif
288	    free(dpt, M_DEVBUF);
289	    return;
290	  } else {
291	    dpt->io_base = io_base + 0x10;
292#ifdef DPT_DEBUG_PCI
293	    printf("dpt%d: Mapped registers to I/O space, starting at %x\n",
294			   dpt->unit, dpt->io_base);
295#endif
296	  }
297    }
298#endif /* !DPT_ALLOW_MEMIO */
299
300    if (pci_map_int(config_id, dpt_intr, (void *)dpt, &cam_imask) == 0) {
301#ifdef DPT_DEBUG_WARN
302	  printf("dpt%d: Failed to map interrupt :-(\n", unit);
303#endif
304	  free(dpt, M_DEVBUF);
305	  return;
306    }
307
308    /* If the DPT is mapped as an IDE controller, let it be IDE controller */
309    if (io_base == (ISA_PRIMARY_WD_ADDRESS)) {
310#ifdef DPT_DEBUG_WARN
311	  printf("dpt%d: Mapped as an IDE controller.  "
312			 "Disabling SCSI setup\n", unit);
313#endif
314	  free(dpt, M_DEVBUF);
315	  return;
316    } else {
317	  if ((config = dpt_get_conf(dpt, 0xc1, 7,
318								 sizeof(dpt_conf_t), 1)) == NULL) {
319#ifdef DPT_DEBUG_ERROR
320	    printf("dpt%d: Failed to get board configuration (%x)\n",
321			   unit, BaseRegister(dpt));
322#endif
323	    free(dpt, M_DEVBUF);
324	    return;
325	  }
326    }
327
328    dpt->max_id      = config->MAX_ID;
329    dpt->max_lun     = config->MAX_LUN;
330    dpt->irq         = config->IRQ;
331    dpt->channels    = config->MAX_CHAN;
332    dpt->dma_channel = (8 - config->DMA_channel) & 7;
333
334#ifdef DPT_DEBUG_SETUP
335    printf("dpt%d: max_id = %d, max_chan = %d, max_lun = %d\n",
336		   dpt->unit, dpt->max_id, dpt->channels, dpt->max_lun);
337#endif
338
339    if (result = dpt_setup(dpt, config)) {
340	  free(config, M_TEMP);
341	  free(dpt, M_DEVBUF);
342	  printf("dpt%d: dpt_setup failed (%d).  Driver Disabled :-(\n",
343			 dpt->unit, result);
344    } else {
345	  /* clean up the informational data, and display */
346	  char clean_vendor[9];
347	  char clean_model[17];
348	  char clean_firmware[5];
349	  char clean_protocol[5];
350	  char clean_other[7];
351
352	  int     ndx;
353
354	  strncpy(clean_other, dpt->board_data.otherData, 8);
355	  clean_other[6] = '\0';
356	  for (ndx = 5; ndx >= 0; ndx--) {
357	    if (clean_other[ndx] == ' ')
358		  clean_other[ndx] = '\0';
359	    else
360		  break;
361	  }
362	  strncpy(dpt->board_data.otherData, clean_other, 6);
363
364	  strncpy(clean_vendor, dpt->board_data.vendor, 8);
365	  clean_vendor[8] = '\0';
366	  for (ndx = 7; ndx >= 0; ndx--) {
367	    if (clean_vendor[ndx] == ' ')
368		  clean_vendor[ndx] = '\0';
369	    else
370		  break;
371	  }
372	  strncpy(dpt->board_data.vendor, clean_vendor, 8);
373
374	  strncpy(clean_model, dpt->board_data.modelNum, 16);
375	  clean_model[16] = '\0';
376	  for (ndx = 15; ndx >= 0; ndx--) {
377	    if (clean_model[ndx] == ' ')
378		  clean_model[ndx] = '\0';
379	    else
380		  break;
381	  }
382	  strncpy(dpt->board_data.modelNum, clean_model, 16);
383
384	  strncpy(clean_firmware, dpt->board_data.firmware, 4);
385	  clean_firmware[4] = '\0';
386	  for (ndx = 3; ndx >= 0; ndx--) {
387	    if (clean_firmware[ndx] == ' ')
388		  clean_firmware[ndx] = '\0';
389	    else
390		  break;
391	  }
392	  strncpy(dpt->board_data.firmware, clean_firmware, 4);
393
394	  strncpy(clean_protocol, dpt->board_data.protocol, 4);
395	  clean_protocol[4] = '\0';
396	  for (ndx = 3; ndx >= 0; ndx--) {
397	    if (clean_protocol[ndx] == ' ')
398		  clean_protocol[ndx] = '\0';
399	    else
400		  break;
401	  }
402	  strncpy(dpt->board_data.protocol, clean_protocol, 4);
403
404	  dpt_detect_cache(dpt);
405
406	  printf("dpt%d: %s type %x, model %s firmware %s, Protocol %s \n"
407			 "      on port %x with %s cache.  LED = %s\n",
408			 dpt->unit, clean_vendor, dpt->board_data.deviceType,
409			 clean_model, clean_firmware, clean_protocol, dpt->io_base,
410			 (dpt->cache_type == DPT_NO_CACHE)
411			 ? "Disabled"
412			 : (dpt->cache_type == DPT_CACHE_WRITETHROUGH)
413			 ? "Write-Through"
414			 : "Write-Back",
415			 i2bin(dpt_blinking_led(dpt), 8));
416	  printf("dpt%d: Enabled Options:\n", dpt->unit);
417#ifdef DPT_LOST_IRQ
418	  printf("      Recover Lost Interrupts\n");
419#endif
420#ifdef DPT_VERIFY_HINTR
421	  printf("      Verify Lost Transactions\n");
422#endif
423#ifdef DPT_RESTRICTED_FREELIST
424	  printf("      Restrict the Freelist Size\n");
425#endif
426#ifdef DPT_MEASURE_PERFORMANCE
427	  printf("      Collect Metrics\n");
428#endif
429#ifdef DPT_FREELIST_IS_STACK
430	  printf("      Optimize CPU Cache\n");
431#endif
432#ifdef DPT_HANDLE_TIMEOUTS
433	  printf("      Handle Timeouts\n");
434#endif
435#ifdef DPT_ALLOW_MEMIO
436	  printf("      Allow I/O to be Memeory Mapped\n");
437#endif
438#ifdef DPT_HINTR_CHECK_SOFTC
439	  printf("      Validate SoftC at Interrupt\n");
440#endif
441
442	  /* register shutdown handlers */
443	  result = at_shutdown((bootlist_fn)dpt_shutdown, (void *)dpt,
444						   SHUTDOWN_POST_SYNC);
445	  switch ( result ) {
446	  case 0:
447#ifdef DPT_DEBUG_SHUTDOWN
448		printf("dpt%d: Shutdown handler registered\n", dpt->unit);
449#endif
450		break;
451	  default:
452#ifdef DPT_DEBUG_WARN
453		printf("dpt%d: Failed to register shutdown handler (%d)\n",
454			   dpt->unit, result);
455#endif
456		break;
457	  }
458
459	  /* Attach SCSI devices */
460	  dpt_attach(dpt);
461	  ++dpt_controllers_present;
462
463	  /*
464	   * Now we create the DEVFS entry.
465	   * This would be normally done from dpt_control.c,
466	   * But since it appears to be called before we do here,
467	   * We never get the entries made.
468       */
469#ifdef DEVFS
470	  (void) devfs_add_devswf(&dpt_cdevsw, dpt->unit, DV_CHR,
471				   			  UID_ROOT, GID_WHEEL, 0600,
472				   			  "dpt%d", dpt->unit);
473	  (void) devfs_add_devswf(&dpt_cdevsw, dpt->unit | SCSI_CONTROL_MASK,
474							  DV_CHR,
475							  UID_ROOT, GID_WHEEL, 0600,
476							  "dpt%d.ctl", dpt->unit);
477#endif
478    }
479}
480
481static int
482dpt_pci_shutdown(int foo, int bar)
483{
484#ifdef DPT_DEBUG_WARN
485    printf("dpt_pci_shutdown(%x, %x)\n", foo, bar);
486#endif
487    return (0);
488}
489
490/* End of the DPT PCI part of the driver */
491
492/*
493 * Hello emacs, these are the
494 * Local Variables:
495 *  c-indent-level:               8
496 *  c-continued-statement-offset: 8
497 *  c-continued-brace-offset:     0
498 *  c-brace-offset:              -8
499 *  c-brace-imaginary-offset:     0
500 *  c-argdecl-indent:             8
501 *  c-label-offset:              -8
502 *  c++-hanging-braces:           1
503 *  c++-access-specifier-offset: -8
504 *  c++-empty-arglist-indent:     8
505 *  c++-friend-offset:            0
506 * End:
507 */
508