wd7000.c revision 67708
1/*
2 * Copyright (c) 1994 Ludd, University of Lule}, Sweden.
3 * Copyright (c) 2000 Sergey A. Babkin
4 * All rights reserved.
5 *
6 * Written by Olof Johansson (offe@ludd.luth.se) 1995.
7 * Based on code written by Theo de Raadt (deraadt@fsa.ca).
8 * Resurrected, ported to CAM and generally cleaned up by Sergey Babkin
9 * <babkin@bellatlantic.net> or <babkin@users.sourceforge.net>.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *     This product includes software developed at Ludd, University of Lule}
22 *     and by the FreeBSD project.
23 * 4. The name of the author may not be used to endorse or promote products
24 *    derived from this software without specific prior written permission
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * $FreeBSD: head/sys/dev/wds/wd7000.c 67708 2000-10-27 11:45:49Z phk $
38 */
39
40/* All bugs are subject to removal without further notice */
41
42/*
43 * offe 01/07/95
44 *
45 * This version of the driver _still_ doesn't implement scatter/gather for the
46 * WD7000-FASST2. This is due to the fact that my controller doesn't seem to
47 * support it. That, and the lack of documentation makes it impossible for me
48 * to implement it. What I've done instead is allocated a local buffer,
49 * contiguous buffer big enough to handle the requests. I haven't seen any
50 * read/write bigger than 64k, so I allocate a buffer of 64+16k. The data
51 * that needs to be DMA'd to/from the controller is copied to/from that
52 * buffer before/after the command is sent to the card.
53 *
54 * SB 03/30/00
55 *
56 * An intermediate buffer is needed anyway to make sure that the buffer is
57 * located under 16MB, otherwise it's out of reach of ISA cards. I've added
58 * optimizations to allocate space in buffer in fragments.
59 */
60
61/*
62 * Jumpers: (see The Ref(TM) for more info)
63 * W1/W2 - interrupt selection:
64 *  W1 (1-2) IRQ3, (3-4) IRQ4, (5-6) IRQ5, (7-8) IRQ7, (9-10) IRQ9
65 *  W2 (21-22) IRQ10, (19-20) IRQ11, (17-18) IRQ12, (15-16) IRQ14, (13-14) IRQ15
66 *
67 * W2 - DRQ/DACK selection, DRQ and DACK must be the same:
68 *  (5-6) DRQ5 (11-12) DACK5
69 *  (3-4) DRQ6 (9-10) DACK6
70 *  (1-2) DRQ7 (7-8) DACK7
71 *
72 * W3 - I/O address selection: open pair of pins (OFF) means 1, jumpered (ON) means 0
73 *  pair (1-2) is bit 3, ..., pair (9-10) is bit 7. All the other bits are equal
74 *  to the value 0x300. In bitwise representation that would be:
75 *   0 0 1 1 (9-10) (7-8) (5-6) (3-4) (1-2) 0 0 0
76 *  For example, address 0x3C0, bitwise 1111000000 will be represented as:
77 *   (9-10) OFF, (7-8) OFF, (5-6) ON, (3-4) ON, (1-2) ON
78 *
79 * W4 - BIOS address: open pair of pins (OFF) means 1, jumpered (ON) means 0
80 *  pair (1-2) is bit 13, ..., pair (7-8) is bit 16. All the other bits are
81 *  equal to the value 0xC0000. In bitwise representation that would be:
82 *   1 1 0 (7-8) (5-6) (3-4) (1-2) 0 0000 0000 0000
83 *  For example, address 0xD8000 will be represented as:
84 *   (7-8) OFF, (5-6) OFF, (3-4) ON, (1-2) ON
85 *
86 * W98 (on newer cards) - BIOS enabled; on older cards just remove the BIOS
87 * chip to disable it
88 * W99 (on newer cards) - ROM size (1-2) OFF, (3-4) ON
89 *
90 * W5 - terminator power
91 *  ON - host supplies term. power
92 *  OFF - target supplies term. power
93 *
94 * W6, W9 - floppy support (a bit cryptic):
95 *  W6 ON, W9 ON - disabled
96 *  W6 OFF, W9 ON - enabled with HardCard only
97 *  W6 OFF, W9 OFF - enabled with no hardCard or Combo
98 *
99 * Default: I/O 0x350, IRQ15, DMA6
100 */
101
102/*
103 * debugging levels:
104 * 0 - disabled
105 * 1 - print debugging messages
106 * 2 - collect  debugging messages in an internal log buffer which can be
107 *     printed later by calling wds_printlog from DDB
108 *
109 * Both kind of logs are heavy and interact significantly with the timing
110 * of commands, so the observed problems may become invisible if debug
111 * logging is enabled.
112 *
113 * The light-weight logging facility may be enabled by defining
114 * WDS_ENABLE_SMALLOG as 1. It has very little overhead and allows observing
115 * the traces of various race conditions without affectiong them but the log is
116 * quite terse. The small log can be printer from DDB by calling
117 * wds_printsmallog.
118 */
119#ifndef WDS_DEBUG
120#define WDS_DEBUG 0
121#endif
122
123#ifndef WDS_ENABLE_SMALLOG
124#define WDS_ENABLE_SMALLOG 0
125#endif
126
127#include <sys/types.h>
128#include <sys/param.h>
129#include <sys/systm.h>
130#include <sys/errno.h>
131#include <sys/kernel.h>
132#include <sys/assym.h>
133
134#include <sys/bio.h>
135#include <sys/buf.h>
136#include <sys/proc.h>
137#include <sys/disklabel.h>
138
139#include <cam/cam.h>
140#include <cam/cam_ccb.h>
141#include <cam/cam_sim.h>
142#include <cam/cam_xpt_sim.h>
143#include <cam/cam_debug.h>
144#include <cam/scsi/scsi_all.h>
145#include <cam/scsi/scsi_message.h>
146
147#include <machine/clock.h>
148
149#include <vm/vm.h>
150#include <vm/vm_param.h>
151#include <vm/pmap.h>
152
153#include <sys/module.h>
154#include <sys/bus.h>
155#include <machine/bus.h>
156#include <machine/resource.h>
157#include <sys/rman.h>
158
159#include <isa/isavar.h>
160#include <isa/pnpvar.h>
161
162#define WDSTOPHYS(wp, a)	( ((u_long)a) - ((u_long)wp->dx) + ((u_long)wp->dx_p) )
163#define WDSTOVIRT(wp, a)	( ((char *)a) - ((char*)wp->dx_p) + ((char *)wp->dx) )
164
165/* 0x10000 (64k) should be enough. But just to be sure... */
166#define BUFSIZ 		0x12000
167/* buffer fragment size, no more than 32 frags per buffer */
168#define FRAGSIZ		0x1000
169
170
171/* WD7000 registers */
172#define WDS_STAT		0	/* read */
173#define WDS_IRQSTAT		1	/* read */
174
175#define WDS_CMD			0	/* write */
176#define WDS_IRQACK		1	/* write */
177#define WDS_HCR			2	/* write */
178
179#define WDS_NPORTS		4 /* number of ports used */
180
181/* WDS_STAT (read) defs */
182#define WDS_IRQ			0x80
183#define WDS_RDY			0x40
184#define WDS_REJ			0x20
185#define WDS_INIT		0x10
186
187/* WDS_IRQSTAT (read) defs */
188#define WDSI_MASK		0xc0
189#define WDSI_ERR		0x00
190#define WDSI_MFREE		0x80
191#define WDSI_MSVC		0xc0
192
193/* WDS_CMD (write) defs */
194#define WDSC_NOOP		0x00
195#define WDSC_INIT		0x01
196#define WDSC_DISUNSOL		0x02 /* disable unsolicited ints */
197#define WDSC_ENAUNSOL		0x03 /* enable unsolicited ints */
198#define WDSC_IRQMFREE		0x04 /* interrupt on free RQM */
199#define WDSC_SCSIRESETSOFT	0x05 /* soft reset */
200#define WDSC_SCSIRESETHARD	0x06 /* hard reset ack */
201#define WDSC_MSTART(m)		(0x80 + (m)) /* start mailbox */
202#define WDSC_MMSTART(m)		(0xc0 + (m)) /* start all mailboxes */
203
204/* WDS_HCR (write) defs */
205#define WDSH_IRQEN		0x08
206#define WDSH_DRQEN		0x04
207#define WDSH_SCSIRESET		0x02
208#define WDSH_ASCRESET		0x01
209
210struct wds_cmd {
211	u_int8_t	cmd;
212	u_int8_t	targ;
213	u_int8_t	scb[12];
214	u_int8_t	stat;
215	u_int8_t	venderr;
216	u_int8_t	len[3];
217	u_int8_t	data[3];
218	u_int8_t	next[3];
219	u_int8_t	write;
220	u_int8_t	xx[6];
221};
222
223struct wds_req {
224	struct	   wds_cmd cmd;
225	union	   ccb *ccb;
226	enum {
227		WR_DONE = 0x01,
228		WR_SENSE = 0x02
229	} flags;
230	u_int8_t  *buf;		/* address of linear data buffer */
231	u_int32_t  mask;	/* mask of allocated fragments */
232	u_int8_t	ombn;
233	u_int8_t	id;	/* number of request */
234};
235
236#define WDSX_SCSICMD		0x00
237#define WDSX_OPEN_RCVBUF	0x80
238#define WDSX_RCV_CMD		0x81
239#define WDSX_RCV_DATA		0x82
240#define WDSX_RCV_DATASTAT	0x83
241#define WDSX_SND_DATA		0x84
242#define WDSX_SND_DATASTAT	0x85
243#define WDSX_SND_CMDSTAT	0x86
244#define WDSX_READINIT		0x88
245#define WDSX_READSCSIID		0x89
246#define WDSX_SETUNSOLIRQMASK	0x8a
247#define WDSX_GETUNSOLIRQMASK	0x8b
248#define WDSX_GETFIRMREV		0x8c
249#define WDSX_EXECDIAG		0x8d
250#define WDSX_SETEXECPARM	0x8e
251#define WDSX_GETEXECPARM	0x8f
252
253struct wds_mb {
254	u_int8_t	stat;
255	u_int8_t	addr[3];
256};
257/* ICMB status value */
258#define ICMB_OK			0x01
259#define ICMB_OKERR		0x02
260#define ICMB_ETIME		0x04
261#define ICMB_ERESET		0x05
262#define ICMB_ETARCMD		0x06
263#define ICMB_ERESEL		0x80
264#define ICMB_ESEL		0x81
265#define ICMB_EABORT		0x82
266#define ICMB_ESRESET		0x83
267#define ICMB_EHRESET		0x84
268
269struct wds_setup {
270	u_int8_t	cmd;
271	u_int8_t	scsi_id;
272	u_int8_t	buson_t;
273	u_int8_t	busoff_t;
274	u_int8_t	xx;
275	u_int8_t	mbaddr[3];
276	u_int8_t	nomb;
277	u_int8_t	nimb;
278};
279
280/* the code depends on equality of these parameters */
281#define MAXSIMUL	8
282#define WDS_NOMB	MAXSIMUL
283#define WDS_NIMB	MAXSIMUL
284
285static int	fragsiz;
286static int	nfrags;
287
288/* structure for data exchange with controller */
289
290struct wdsdx {
291	struct wds_req	req[MAXSIMUL];
292	struct wds_mb	ombs[MAXSIMUL];
293	struct wds_mb	imbs[MAXSIMUL];
294	u_int8_t	data[BUFSIZ];
295};
296
297/* structure softc */
298
299struct wds {
300	device_t	 dev;
301	int		 unit;
302	int		 addr;
303	int		 drq;
304	struct cam_sim	*sim;	/* SIM descriptor for this card */
305	struct cam_path	*path;	/* wildcard path for this card */
306	char		 want_wdsr;	/* resource shortage flag */
307	u_int32_t	 data_free;
308	u_int32_t	 wdsr_free;
309	struct wdsdx	*dx;
310	struct wdsdx	*dx_p; /* physical address */
311	struct resource	*port_r;
312	int		 port_rid;
313	struct resource	*drq_r;
314	int		 drq_rid;
315	struct resource *intr_r;
316	int		 intr_rid;
317	void		*intr_cookie;
318	bus_dma_tag_t	 bustag;
319	bus_dmamap_t	 busmap;
320};
321
322#define ccb_wdsr	spriv_ptr1	/* for wds request */
323
324static int      wds_probe(device_t dev);
325static int      wds_attach(device_t dev);
326static void     wds_intr(struct wds *wp);
327
328static void     wds_action(struct cam_sim * sim, union ccb * ccb);
329static void     wds_poll(struct cam_sim * sim);
330
331static int      wds_preinit(struct wds *wp);
332static int      wds_init(struct wds *wp);
333
334static void     wds_alloc_callback(void *arg, bus_dma_segment_t *seg,
335	 int nseg, int error);
336static void     wds_free_resources(struct wds *wp);
337
338static struct wds_req *wdsr_alloc(struct wds *wp);
339
340static void     wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio);
341static void     wdsr_ccb_done(struct wds *wp, struct wds_req *r,
342			      union ccb *ccb, u_int32_t status);
343
344static void     wds_done(struct wds *wp, struct wds_req *r, u_int8_t stat);
345static int      wds_runsense(struct wds *wp, struct wds_req *r);
346static int      wds_getvers(struct wds *wp);
347
348static int      wds_cmd(int base, u_int8_t * p, int l);
349static void     wds_wait(int reg, int mask, int val);
350
351static struct wds_req *cmdtovirt(struct wds *wp, u_int32_t phys);
352
353static u_int32_t frag_alloc(struct wds *wp, int size, u_int8_t **res,
354			    u_int32_t *maskp);
355static void     frag_free(struct wds *wp, u_int32_t mask);
356
357void            wds_print(void);
358
359#if WDS_ENABLE_SMALLOG==1
360static __inline void   smallog(char c);
361void 	wds_printsmallog(void);
362#endif /* SMALLOG */
363
364/* SCSI ID of the adapter itself */
365#ifndef WDS_HBA_ID
366#define WDS_HBA_ID 7
367#endif
368
369#if WDS_DEBUG == 2
370#define LOGLINESIZ	81
371#define NLOGLINES	300
372#define DBX	wds_nextlog(), LOGLINESIZ,
373#define DBG	snprintf
374
375static char     wds_log[NLOGLINES][LOGLINESIZ];
376static int      logwrite = 0, logread = 0;
377static char    *wds_nextlog(void);
378void            wds_printlog(void);
379
380#elif WDS_DEBUG != 0
381#define DBX
382#define DBG	printf
383#else
384#define DBX
385#define DBG	if(0) printf
386#endif
387
388/* the table of supported bus methods */
389static device_method_t wds_isa_methods[] = {
390	DEVMETHOD(device_probe,		wds_probe),
391	DEVMETHOD(device_attach,	wds_attach),
392	{ 0, 0 }
393};
394
395static driver_t wds_isa_driver = {
396	"wds",
397	wds_isa_methods,
398	sizeof(struct wds),
399};
400
401static devclass_t wds_devclass;
402
403DRIVER_MODULE(wds, isa, wds_isa_driver, wds_devclass, 0, 0);
404
405#if WDS_ENABLE_SMALLOG==1
406#define SMALLOGSIZ	512
407static char	 wds_smallog[SMALLOGSIZ];
408static char	*wds_smallogp = wds_smallog;
409static char	 wds_smallogover = 0;
410
411static __inline void
412smallog(char c)
413{
414	*wds_smallogp = c;
415	if (++wds_smallogp == &wds_smallog[SMALLOGSIZ]) {
416		wds_smallogp = wds_smallog;
417		wds_smallogover = 1;
418	}
419}
420
421#define smallog2(a, b)	(smallog(a), smallog(b))
422#define smallog3(a, b, c)	(smallog(a), smallog(b), smallog(c))
423#define smallog4(a, b, c, d)	(smallog(a),smallog(b),smallog(c),smallog(d))
424
425void
426wds_printsmallog(void)
427{
428	int	 i;
429	char	*p;
430
431	printf("wds: ");
432	p = wds_smallogover ? wds_smallogp : wds_smallog;
433	i = 0;
434	do {
435		printf("%c", *p);
436		if (++p == &wds_smallog[SMALLOGSIZ])
437			p = wds_smallog;
438		if (++i == 70) {
439			i = 0;
440			printf("\nwds: ");
441		}
442	} while (p != wds_smallogp);
443	printf("\n");
444}
445#else
446#define smallog(a)
447#define smallog2(a, b)
448#define smallog3(a, b, c)
449#define smallog4(a, b, c, d)
450#endif				/* SMALLOG */
451
452static int
453wds_probe(device_t dev)
454{
455	struct	wds *wp;
456	int	error = 0;
457	int	irq;
458
459	/* No pnp support */
460	if (isa_get_vendorid(dev))
461		return (ENXIO);
462
463	wp = (struct wds *) device_get_softc(dev);
464	wp->unit = device_get_unit(dev);
465	wp->dev = dev;
466
467	wp->addr = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/);
468	if (wp->addr == 0 || wp->addr <0x300
469	 || wp->addr > 0x3f8 || wp->addr & 0x7) {
470		device_printf(dev, "invalid port address 0x%x\n", wp->addr);
471		return (ENXIO);
472	}
473
474	if (bus_set_resource(dev, SYS_RES_IOPORT, 0, wp->addr, WDS_NPORTS) < 0)
475		return (ENXIO);
476
477	/* get the DRQ */
478	wp->drq = bus_get_resource_start(dev, SYS_RES_DRQ, 0 /*rid*/);
479	if (wp->drq < 5 || wp->drq > 7) {
480		device_printf(dev, "invalid DRQ %d\n", wp->drq);
481		return (ENXIO);
482	}
483
484	/* get the IRQ */
485	irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0 /*rid*/);
486	if (irq < 3) {
487		device_printf(dev, "invalid IRQ %d\n", irq);
488		return (ENXIO);
489	}
490
491	wp->port_rid = 0;
492	wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT,  &wp->port_rid,
493				        /*start*/ 0, /*end*/ ~0,
494					/*count*/ 0, RF_ACTIVE);
495	if (wp->port_r == NULL)
496		return (ENXIO);
497
498	error = wds_preinit(wp);
499
500	/*
501	 * We cannot hold resources between probe and
502	 * attach as we may never be attached.
503	 */
504	wds_free_resources(wp);
505
506	return (error);
507}
508
509static int
510wds_attach(device_t dev)
511{
512	struct	wds *wp;
513	struct	cam_devq *devq;
514	struct	cam_sim *sim;
515	struct	cam_path *pathp;
516	int	i;
517	int	error = 0;
518
519	wp = (struct wds *)device_get_softc(dev);
520
521	wp->port_rid = 0;
522	wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT,  &wp->port_rid,
523					/*start*/ 0, /*end*/ ~0,
524					/*count*/ 0, RF_ACTIVE);
525	if (wp->port_r == NULL)
526		return (ENXIO);
527
528	/* We must now release resources on error. */
529
530	wp->drq_rid = 0;
531	wp->drq_r = bus_alloc_resource(dev, SYS_RES_DRQ,  &wp->drq_rid,
532				       /*start*/ 0, /*end*/ ~0,
533				       /*count*/ 0, RF_ACTIVE);
534	if (wp->drq_r == NULL)
535		goto bad;
536
537	wp->intr_rid = 0;
538	wp->intr_r = bus_alloc_resource(dev, SYS_RES_IRQ,  &wp->intr_rid,
539					/*start*/ 0, /*end*/ ~0,
540					/*count*/ 0, RF_ACTIVE);
541	if (wp->intr_r == NULL)
542		goto bad;
543	error = bus_setup_intr(dev, wp->intr_r, INTR_TYPE_CAM,
544			       (driver_intr_t *)wds_intr, (void *)wp,
545			       &wp->intr_cookie);
546	if (error)
547		goto bad;
548
549	/* now create the memory buffer */
550	error = bus_dma_tag_create(NULL, /*alignment*/4,
551				   /*boundary*/0,
552				   /*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
553				   /*highaddr*/ BUS_SPACE_MAXADDR,
554				   /*filter*/ NULL, /*filterarg*/ NULL,
555				   /*maxsize*/ sizeof(* wp->dx),
556				   /*nsegments*/ 1,
557				   /*maxsegsz*/ sizeof(* wp->dx), /*flags*/ 0,
558				   &wp->bustag);
559	if (error)
560		goto bad;
561
562	error = bus_dmamem_alloc(wp->bustag, (void **)&wp->dx,
563				 /*flags*/ 0, &wp->busmap);
564	if (error)
565		goto bad;
566
567	bus_dmamap_load(wp->bustag, wp->busmap, (void *)wp->dx,
568			sizeof(* wp->dx), wds_alloc_callback,
569			(void *)&wp->dx_p, /*flags*/0);
570
571	/* initialize the wds_req structures on this unit */
572	for(i=0; i<MAXSIMUL; i++)  {
573		wp->dx->req[i].id = i;
574		wp->wdsr_free |= 1<<i;
575	}
576
577	/* initialize the memory buffer allocation for this unit */
578	if (BUFSIZ / FRAGSIZ > 32) {
579		fragsiz = (BUFSIZ / 32) & ~0x01; /* keep it word-aligned */
580		device_printf(dev, "data buffer fragment size too small.  "
581			      "BUFSIZE / FRAGSIZE must be <= 32\n");
582	} else
583		fragsiz = FRAGSIZ & ~0x01; /* keep it word-aligned */
584
585	wp->data_free = 0;
586	nfrags = 0;
587	for (i = fragsiz; i <= BUFSIZ; i += fragsiz) {
588		nfrags++;
589		wp->data_free = (wp->data_free << 1) | 1;
590	}
591
592	/* complete the hardware initialization */
593	if (wds_init(wp) != 0)
594		goto bad;
595
596	if (wds_getvers(wp) == -1)
597		device_printf(dev, "getvers failed\n");
598	device_printf(dev, "using %d bytes / %d frags for dma buffer\n",
599		      BUFSIZ, nfrags);
600
601	devq = cam_simq_alloc(MAXSIMUL);
602	if (devq == NULL)
603		goto bad;
604
605	sim = cam_sim_alloc(wds_action, wds_poll, "wds", (void *) wp,
606			    wp->unit, 1, 1, devq);
607	if (sim == NULL) {
608		cam_simq_free(devq);
609		goto bad;
610	}
611	wp->sim = sim;
612
613	if (xpt_bus_register(sim, 0) != CAM_SUCCESS) {
614		cam_sim_free(sim, /* free_devq */ TRUE);
615		goto bad;
616	}
617	if (xpt_create_path(&pathp, /* periph */ NULL,
618			    cam_sim_path(sim), CAM_TARGET_WILDCARD,
619			    CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
620		xpt_bus_deregister(cam_sim_path(sim));
621		cam_sim_free(sim, /* free_devq */ TRUE);
622		goto bad;
623	}
624	wp->path = pathp;
625
626	return (0);
627
628bad:
629	wds_free_resources(wp);
630	if (error)
631		return (error);
632	else /* exact error is unknown */
633		return (ENXIO);
634}
635
636/* callback to save the physical address */
637static void
638wds_alloc_callback(void *arg, bus_dma_segment_t *seg,  int nseg, int error)
639{
640	*(bus_addr_t *)arg = seg[0].ds_addr;
641}
642
643static void
644wds_free_resources(struct wds *wp)
645{
646	/* check every resource and free if not zero */
647
648	/* interrupt handler */
649	if (wp->intr_r) {
650		bus_teardown_intr(wp->dev, wp->intr_r, wp->intr_cookie);
651		bus_release_resource(wp->dev, SYS_RES_IRQ, wp->intr_rid,
652				     wp->intr_r);
653		wp->intr_r = 0;
654	}
655
656	/* all kinds of memory maps we could have allocated */
657	if (wp->dx_p) {
658		bus_dmamap_unload(wp->bustag, wp->busmap);
659		wp->dx_p = 0;
660	}
661	if (wp->dx) { /* wp->busmap may be legitimately equal to 0 */
662		/* the map will also be freed */
663		bus_dmamem_free(wp->bustag, wp->dx, wp->busmap);
664		wp->dx = 0;
665	}
666	if (wp->bustag) {
667		bus_dma_tag_destroy(wp->bustag);
668		wp->bustag = 0;
669	}
670	/* release all the bus resources */
671	if (wp->drq_r) {
672		bus_release_resource(wp->dev, SYS_RES_DRQ,
673				     wp->drq_rid, wp->drq_r);
674		wp->drq_r = 0;
675	}
676	if (wp->port_r) {
677		bus_release_resource(wp->dev, SYS_RES_IOPORT,
678				     wp->port_rid, wp->port_r);
679		wp->port_r = 0;
680	}
681}
682
683/* allocate contiguous fragments from the buffer */
684static u_int32_t
685frag_alloc(struct wds *wp, int size, u_int8_t **res, u_int32_t *maskp)
686{
687	int	i;
688	u_int32_t	mask;
689	u_int32_t	free;
690
691	if (size > fragsiz * nfrags)
692		return (CAM_REQ_TOO_BIG);
693
694	mask = 1;		/* always allocate at least 1 fragment */
695	for (i = fragsiz; i < size; i += fragsiz)
696		mask = (mask << 1) | 1;
697
698	free = wp->data_free;
699	if(free != 0) {
700		i = ffs(free)-1; /* ffs counts bits from 1 */
701		for (mask <<= i; i < nfrags; i++) {
702			if ((free & mask) == mask) {
703				wp->data_free &= ~mask;	/* mark frags as busy */
704				*maskp = mask;
705				*res = &wp->dx->data[fragsiz * i];
706				DBG(DBX "wds%d: allocated buffer mask=0x%x\n",
707					wp->unit, mask);
708				return (CAM_REQ_CMP);
709			}
710			if (mask & 0x80000000)
711				break;
712
713			mask <<= 1;
714		}
715	}
716	return (CAM_REQUEUE_REQ);	/* no free memory now, try later */
717}
718
719static void
720frag_free(struct wds *wp, u_int32_t mask)
721{
722	wp->data_free |= mask;	/* mark frags as free */
723	DBG(DBX "wds%d: freed buffer mask=0x%x\n", wp->unit, mask);
724}
725
726static struct wds_req *
727wdsr_alloc(struct wds *wp)
728{
729	struct	wds_req *r;
730	int	x;
731	int	i;
732
733	r = NULL;
734	x = splcam();
735
736	/* anyway most of the time only 1 or 2 commands will
737	 * be active because SCSI disconnect is not supported
738	 * by hardware, so the search should be fast enough
739	 */
740	i = ffs(wp->wdsr_free) - 1;
741	if(i < 0) {
742		splx(x);
743		return (NULL);
744	}
745	wp->wdsr_free &= ~ (1<<i);
746	r = &wp->dx->req[i];
747	r->flags = 0;	/* reset all flags */
748	r->ombn = i;		/* luckily we have one omb per wdsr */
749	wp->dx->ombs[i].stat = 1;
750
751	r->mask = 0;
752	splx(x);
753	smallog3('r', i + '0', r->ombn + '0');
754	return (r);
755}
756
757static void
758wds_intr(struct wds *wp)
759{
760	struct	 wds_req *rp;
761	struct	 wds_mb *in;
762	u_int8_t stat;
763	u_int8_t c;
764	int	 addr = wp->addr;
765
766	DBG(DBX "wds%d: interrupt [\n", wp->unit);
767	smallog('[');
768
769	if (inb(addr + WDS_STAT) & WDS_IRQ) {
770		c = inb(addr + WDS_IRQSTAT);
771		if ((c & WDSI_MASK) == WDSI_MSVC) {
772			c = c & ~WDSI_MASK;
773			in = &wp->dx->imbs[c];
774
775			rp = cmdtovirt(wp, scsi_3btoul(in->addr));
776			stat = in->stat;
777
778			if (rp != NULL)
779				wds_done(wp, rp, stat);
780			else
781				device_printf(wp->dev,
782					      "got weird command address %p"
783					      "from controller\n", rp);
784
785			in->stat = 0;
786		} else
787			device_printf(wp->dev,
788				      "weird interrupt, irqstat=0x%x\n", c);
789		outb(addr + WDS_IRQACK, 0);
790	} else {
791		smallog('?');
792	}
793	smallog(']');
794	DBG(DBX "wds%d: ]\n", wp->unit);
795}
796
797static void
798wds_done(struct wds *wp, struct wds_req *r, u_int8_t stat)
799{
800	struct	ccb_hdr *ccb_h;
801	struct	ccb_scsiio *csio;
802	int	status;
803
804	smallog('d');
805
806	if (r->flags & WR_DONE) {
807		device_printf(wp->dev,
808				"request %d reported done twice\n", r->id);
809		smallog2('x', r->id + '0');
810		return;
811	}
812
813	smallog(r->id + '0');
814	ccb_h = &r->ccb->ccb_h;
815	csio = &r->ccb->csio;
816	status = CAM_REQ_CMP_ERR;
817
818	DBG(DBX "wds%d: %s stat=0x%x c->stat=0x%x c->venderr=0x%x\n", wp->unit,
819	    r->flags & WR_SENSE ? "(sense)" : "",
820		stat, r->cmd.stat, r->cmd.venderr);
821
822	if (r->flags & WR_SENSE) {
823		if (stat == ICMB_OK || (stat == ICMB_OKERR && r->cmd.stat == 0)) {
824			DBG(DBX "wds%d: sense 0x%x\n", wp->unit, r->buf[0]);
825			/* it has the same size now but for future */
826			bcopy(r->buf, &csio->sense_data,
827			      sizeof(struct scsi_sense_data) > csio->sense_len ?
828			      csio->sense_len : sizeof(struct scsi_sense_data));
829			if (sizeof(struct scsi_sense_data) >= csio->sense_len)
830				csio->sense_resid = 0;
831			else
832				csio->sense_resid =
833					csio->sense_len
834				      - sizeof(struct scsi_sense_data);
835			status = CAM_AUTOSNS_VALID | CAM_SCSI_STATUS_ERROR;
836		} else {
837			status = CAM_AUTOSENSE_FAIL;
838		}
839	} else {
840		switch (stat) {
841		case ICMB_OK:
842			if (ccb_h) {
843				csio->resid = 0;
844				csio->scsi_status = r->cmd.stat;
845				status = CAM_REQ_CMP;
846			}
847			break;
848		case ICMB_OKERR:
849			if (ccb_h) {
850				csio->scsi_status = r->cmd.stat;
851				if (r->cmd.stat) {
852					if (ccb_h->flags & CAM_DIS_AUTOSENSE)
853						status = CAM_SCSI_STATUS_ERROR;
854					else {
855						if ( wds_runsense(wp, r) == CAM_REQ_CMP )
856							return;
857						/* in case of error continue with freeing of CCB */
858					}
859				} else {
860					csio->resid = 0;
861					status = CAM_REQ_CMP;
862				}
863			}
864			break;
865		case ICMB_ETIME:
866			if (ccb_h)
867				status = CAM_SEL_TIMEOUT;
868			break;
869		case ICMB_ERESET:
870		case ICMB_ETARCMD:
871		case ICMB_ERESEL:
872		case ICMB_ESEL:
873		case ICMB_EABORT:
874		case ICMB_ESRESET:
875		case ICMB_EHRESET:
876			if (ccb_h)
877				status = CAM_REQ_CMP_ERR;
878			break;
879		}
880
881		if (ccb_h && (ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN) {
882			/* we accept only virtual addresses in wds_action() */
883			bcopy(r->buf, csio->data_ptr, csio->dxfer_len);
884		}
885	}
886
887	r->flags |= WR_DONE;
888	wp->dx->ombs[r->ombn].stat = 0;
889
890	if (ccb_h) {
891		wdsr_ccb_done(wp, r, r->ccb, status);
892		smallog3('-', ccb_h->target_id + '0', ccb_h->target_lun + '0');
893	} else {
894		frag_free(wp, r->mask);
895		if (wp->want_wdsr) {
896			wp->want_wdsr = 0;
897			xpt_release_simq(wp->sim, /* run queue */ 1);
898		}
899		wp->wdsr_free |= (1 << r->id);
900	}
901
902	DBG(DBX "wds%d: request 0x%x done\n", wp->unit, (u_int) r);
903}
904
905/* command returned bad status, request sense */
906
907static int
908wds_runsense(struct wds *wp, struct wds_req *r)
909{
910	u_int8_t          c;
911	struct	ccb_hdr *ccb_h;
912
913	ccb_h = &r->ccb->ccb_h;
914
915	r->flags |= WR_SENSE;
916	scsi_ulto3b(WDSTOPHYS(wp, &r->cmd),
917	 wp->dx->ombs[r->ombn].addr);
918	bzero(&r->cmd, sizeof r->cmd);
919	r->cmd.cmd = WDSX_SCSICMD;
920	r->cmd.targ = (ccb_h->target_id << 5) |
921		ccb_h->target_lun;
922
923	scsi_ulto3b(0, r->cmd.next);
924
925	r->cmd.scb[0] = REQUEST_SENSE;
926	r->cmd.scb[1] = ccb_h->target_lun << 5;
927	r->cmd.scb[4] = sizeof(struct scsi_sense_data);
928	r->cmd.scb[5] = 0;
929	scsi_ulto3b(WDSTOPHYS(wp, r->buf), r->cmd.data);
930	scsi_ulto3b(sizeof(struct scsi_sense_data), r->cmd.len);
931	r->cmd.write = 0x80;
932
933	outb(wp->addr + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
934
935	wp->dx->ombs[r->ombn].stat = 1;
936	c = WDSC_MSTART(r->ombn);
937
938	if (wds_cmd(wp->addr, &c, sizeof c) != 0) {
939		device_printf(wp->dev, "unable to start outgoing sense mbox\n");
940		wp->dx->ombs[r->ombn].stat = 0;
941		wdsr_ccb_done(wp, r, r->ccb, CAM_AUTOSENSE_FAIL);
942		return CAM_AUTOSENSE_FAIL;
943	} else {
944		DBG(DBX "wds%d: enqueued status cmd 0x%x, r=0x%x\n",
945			wp->unit, r->cmd.scb[0] & 0xFF, (u_int) r);
946		/* don't free CCB yet */
947		smallog3('*', ccb_h->target_id + '0',
948			 ccb_h->target_lun + '0');
949		return CAM_REQ_CMP;
950	}
951}
952
953static int
954wds_getvers(struct wds *wp)
955{
956	struct	 wds_req *r;
957	int	 base;
958	u_int8_t c;
959	int	 i;
960
961	base = wp->addr;
962
963	r = wdsr_alloc(wp);
964	if (!r) {
965		device_printf(wp->dev, "no request slot available!\n");
966		return (-1);
967	}
968	r->flags &= ~WR_DONE;
969
970	r->ccb = NULL;
971
972	scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr);
973
974	bzero(&r->cmd, sizeof r->cmd);
975	r->cmd.cmd = WDSX_GETFIRMREV;
976
977	outb(base + WDS_HCR, WDSH_DRQEN);
978
979	c = WDSC_MSTART(r->ombn);
980	if (wds_cmd(base, (u_int8_t *) & c, sizeof c)) {
981		device_printf(wp->dev, "version request failed\n");
982		wp->wdsr_free |= (1 << r->id);
983		wp->dx->ombs[r->ombn].stat = 0;
984		return (-1);
985	}
986	while (1) {
987		i = 0;
988		while ((inb(base + WDS_STAT) & WDS_IRQ) == 0) {
989			DELAY(9000);
990			if (++i == 100) {
991				device_printf(wp->dev, "getvers timeout\n");
992				return (-1);
993			}
994		}
995		wds_intr(wp);
996		if (r->flags & WR_DONE) {
997			device_printf(wp->dev, "firmware version %d.%02d\n",
998			       r->cmd.targ, r->cmd.scb[0]);
999			wp->wdsr_free |= (1 << r->id);
1000			return (0);
1001		}
1002	}
1003}
1004
1005static void
1006wdsr_ccb_done(struct wds *wp, struct wds_req *r,
1007	      union ccb *ccb, u_int32_t status)
1008{
1009	ccb->ccb_h.ccb_wdsr = 0;
1010
1011	if (r != NULL) {
1012		/* To implement timeouts we would need to know how to abort the
1013		 * command on controller, and this is a great mystery.
1014		 * So for now we just pass the responsibility for timeouts
1015		 * to the controlles itself, it does that reasonably good.
1016		 */
1017		/* untimeout(_timeout, (caddr_t) hcb, ccb->ccb_h.timeout_ch); */
1018		/* we're about to free a hcb, so the shortage has ended */
1019		frag_free(wp, r->mask);
1020		if (wp->want_wdsr && status != CAM_REQUEUE_REQ) {
1021			wp->want_wdsr = 0;
1022			status |= CAM_RELEASE_SIMQ;
1023			smallog('R');
1024		}
1025		wp->wdsr_free |= (1 << r->id);
1026	}
1027	ccb->ccb_h.status =
1028	    status | (ccb->ccb_h.status & ~(CAM_STATUS_MASK | CAM_SIM_QUEUED));
1029	xpt_done(ccb);
1030}
1031
1032static void
1033wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio)
1034{
1035	int	 unit = cam_sim_unit(sim);
1036	struct	 wds *wp;
1037	struct	 ccb_hdr *ccb_h;
1038	struct	 wds_req *r;
1039	int	 base;
1040	u_int8_t c;
1041	int	 error;
1042	int	 n;
1043
1044	wp = (struct wds *)cam_sim_softc(sim);
1045	ccb_h = &csio->ccb_h;
1046
1047	DBG(DBX "wds%d: cmd TARG=%d LUN=%d\n", unit, ccb_h->target_id,
1048	    ccb_h->target_lun);
1049
1050	if (ccb_h->target_id > 7 || ccb_h->target_id == WDS_HBA_ID) {
1051		ccb_h->status = CAM_TID_INVALID;
1052		xpt_done((union ccb *) csio);
1053		return;
1054	}
1055	if (ccb_h->target_lun > 7) {
1056		ccb_h->status = CAM_LUN_INVALID;
1057		xpt_done((union ccb *) csio);
1058		return;
1059	}
1060	if (csio->dxfer_len > BUFSIZ) {
1061		ccb_h->status = CAM_REQ_TOO_BIG;
1062		xpt_done((union ccb *) csio);
1063		return;
1064	}
1065	if (ccb_h->flags & (CAM_CDB_PHYS | CAM_SCATTER_VALID | CAM_DATA_PHYS)) {
1066		/* don't support these */
1067		ccb_h->status = CAM_REQ_INVALID;
1068		xpt_done((union ccb *) csio);
1069		return;
1070	}
1071	base = wp->addr;
1072
1073	/*
1074	 * this check is mostly for debugging purposes,
1075	 * "can't happen" normally.
1076	 */
1077	if(wp->want_wdsr) {
1078		DBG(DBX "wds%d: someone already waits for buffer\n", unit);
1079		smallog('b');
1080		n = xpt_freeze_simq(sim, /* count */ 1);
1081		smallog('0'+n);
1082		ccb_h->status = CAM_REQUEUE_REQ;
1083		xpt_done((union ccb *) csio);
1084		return;
1085	}
1086
1087	r = wdsr_alloc(wp);
1088	if (r == NULL) {
1089		device_printf(wp->dev, "no request slot available!\n");
1090		wp->want_wdsr = 1;
1091		n = xpt_freeze_simq(sim, /* count */ 1);
1092		smallog2('f', '0'+n);
1093		ccb_h->status = CAM_REQUEUE_REQ;
1094		xpt_done((union ccb *) csio);
1095		return;
1096	}
1097
1098	ccb_h->ccb_wdsr = (void *) r;
1099	r->ccb = (union ccb *) csio;
1100
1101	switch (error = frag_alloc(wp, csio->dxfer_len, &r->buf, &r->mask)) {
1102	case CAM_REQ_CMP:
1103		break;
1104	case CAM_REQUEUE_REQ:
1105		DBG(DBX "wds%d: no data buffer available\n", unit);
1106		wp->want_wdsr = 1;
1107		n = xpt_freeze_simq(sim, /* count */ 1);
1108		smallog2('f', '0'+n);
1109		wdsr_ccb_done(wp, r, r->ccb, CAM_REQUEUE_REQ);
1110		return;
1111	default:
1112		DBG(DBX "wds%d: request is too big\n", unit);
1113		wdsr_ccb_done(wp, r, r->ccb, error);
1114		break;
1115	}
1116
1117	ccb_h->status |= CAM_SIM_QUEUED;
1118	r->flags &= ~WR_DONE;
1119
1120	scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr);
1121
1122	bzero(&r->cmd, sizeof r->cmd);
1123	r->cmd.cmd = WDSX_SCSICMD;
1124	r->cmd.targ = (ccb_h->target_id << 5) | ccb_h->target_lun;
1125
1126	if (ccb_h->flags & CAM_CDB_POINTER)
1127		bcopy(csio->cdb_io.cdb_ptr, &r->cmd.scb,
1128		      csio->cdb_len < 12 ? csio->cdb_len : 12);
1129	else
1130		bcopy(csio->cdb_io.cdb_bytes, &r->cmd.scb,
1131		      csio->cdb_len < 12 ? csio->cdb_len : 12);
1132
1133	scsi_ulto3b(csio->dxfer_len, r->cmd.len);
1134
1135	if (csio->dxfer_len > 0
1136	 && (ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_OUT) {
1137		/* we already rejected physical or scattered addresses */
1138		bcopy(csio->data_ptr, r->buf, csio->dxfer_len);
1139	}
1140	scsi_ulto3b(csio->dxfer_len ? WDSTOPHYS(wp, r->buf) : 0, r->cmd.data);
1141
1142	if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN)
1143		r->cmd.write = 0x80;
1144	else
1145		r->cmd.write = 0x00;
1146
1147	scsi_ulto3b(0, r->cmd.next);
1148
1149	outb(base + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
1150
1151	c = WDSC_MSTART(r->ombn);
1152
1153	if (wds_cmd(base, &c, sizeof c) != 0) {
1154		device_printf(wp->dev, "unable to start outgoing mbox\n");
1155		wp->dx->ombs[r->ombn].stat = 0;
1156		wdsr_ccb_done(wp, r, r->ccb, CAM_RESRC_UNAVAIL);
1157		return;
1158	}
1159	DBG(DBX "wds%d: enqueued cmd 0x%x, r=0x%x\n", unit,
1160	    r->cmd.scb[0] & 0xFF, (u_int) r);
1161
1162	smallog3('+', ccb_h->target_id + '0', ccb_h->target_lun + '0');
1163}
1164
1165static void
1166wds_action(struct cam_sim * sim, union ccb * ccb)
1167{
1168	int	unit = cam_sim_unit(sim);
1169	int	s;
1170
1171	DBG(DBX "wds%d: action 0x%x\n", unit, ccb->ccb_h.func_code);
1172	switch (ccb->ccb_h.func_code) {
1173	case XPT_SCSI_IO:
1174		s = splcam();
1175		DBG(DBX "wds%d: SCSI IO entered\n", unit);
1176		wds_scsi_io(sim, &ccb->csio);
1177		DBG(DBX "wds%d: SCSI IO returned\n", unit);
1178		splx(s);
1179		break;
1180	case XPT_RESET_BUS:
1181		/* how to do it right ? */
1182		printf("wds%d: reset\n", unit);
1183		ccb->ccb_h.status = CAM_REQ_CMP;
1184		xpt_done(ccb);
1185		break;
1186	case XPT_ABORT:
1187		ccb->ccb_h.status = CAM_UA_ABORT;
1188		xpt_done(ccb);
1189		break;
1190	case XPT_CALC_GEOMETRY:
1191	{
1192		struct	  ccb_calc_geometry *ccg;
1193		u_int32_t size_mb;
1194		u_int32_t secs_per_cylinder;
1195
1196		ccg = &ccb->ccg;
1197		size_mb = ccg->volume_size
1198			/ ((1024L * 1024L) / ccg->block_size);
1199
1200		ccg->heads = 64;
1201		ccg->secs_per_track = 16;
1202		secs_per_cylinder = ccg->heads * ccg->secs_per_track;
1203		ccg->cylinders = ccg->volume_size / secs_per_cylinder;
1204		ccb->ccb_h.status = CAM_REQ_CMP;
1205		xpt_done(ccb);
1206		break;
1207	}
1208	case XPT_PATH_INQ:	/* Path routing inquiry */
1209	{
1210		struct ccb_pathinq *cpi = &ccb->cpi;
1211
1212		cpi->version_num = 1;	/* XXX??? */
1213		cpi->hba_inquiry = 0;	/* nothing fancy */
1214		cpi->target_sprt = 0;
1215		cpi->hba_misc = 0;
1216		cpi->hba_eng_cnt = 0;
1217		cpi->max_target = 7;
1218		cpi->max_lun = 7;
1219		cpi->initiator_id = WDS_HBA_ID;
1220		cpi->hba_misc = 0;
1221		cpi->bus_id = cam_sim_bus(sim);
1222		cpi->base_transfer_speed = 3300;
1223		strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
1224		strncpy(cpi->hba_vid, "WD/FDC", HBA_IDLEN);
1225		strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
1226		cpi->unit_number = cam_sim_unit(sim);
1227		cpi->ccb_h.status = CAM_REQ_CMP;
1228		xpt_done(ccb);
1229		break;
1230	}
1231	default:
1232		ccb->ccb_h.status = CAM_REQ_INVALID;
1233		xpt_done(ccb);
1234		break;
1235	}
1236}
1237
1238static void
1239wds_poll(struct cam_sim * sim)
1240{
1241	wds_intr((struct wds *)cam_sim_softc(sim));
1242}
1243
1244/* part of initialization done in probe() */
1245/* returns 0 if OK, ENXIO if bad */
1246
1247static int
1248wds_preinit(struct wds *wp)
1249{
1250	int	base;
1251	int	i;
1252
1253	base = wp->addr;
1254
1255	/*
1256	 * Sending a command causes the CMDRDY bit to clear.
1257	 */
1258	outb(base + WDS_CMD, WDSC_NOOP);
1259	if (inb(base + WDS_STAT) & WDS_RDY)
1260		return (ENXIO);
1261
1262	/*
1263	 * the controller exists. reset and init.
1264	 */
1265	outb(base + WDS_HCR, WDSH_ASCRESET | WDSH_SCSIRESET);
1266	DELAY(30);
1267	outb(base + WDS_HCR, 0);
1268
1269	if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
1270		for (i = 0; i < 10; i++) {
1271			if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY)
1272				break;
1273			DELAY(40000);
1274		}
1275		if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY)
1276			/* probe timeout */
1277			return (ENXIO);
1278	}
1279
1280	return (0);
1281}
1282
1283/* part of initialization done in attach() */
1284/* returns 0 if OK, 1 if bad */
1285
1286static int
1287wds_init(struct wds *wp)
1288{
1289	struct	wds_setup init;
1290	int	base;
1291	int	i;
1292	struct	wds_cmd  wc;
1293
1294	base = wp->addr;
1295
1296	outb(base + WDS_HCR, WDSH_DRQEN);
1297
1298	isa_dmacascade(wp->drq);
1299
1300	if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
1301		for (i = 0; i < 10; i++) {
1302			if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY)
1303				break;
1304			DELAY(40000);
1305		}
1306		if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY)
1307			/* probe timeout */
1308			return (1);
1309	}
1310	bzero(&init, sizeof init);
1311	init.cmd = WDSC_INIT;
1312	init.scsi_id = WDS_HBA_ID;
1313	init.buson_t = 24;
1314	init.busoff_t = 48;
1315	scsi_ulto3b(WDSTOPHYS(wp, &wp->dx->ombs), init.mbaddr);
1316	init.xx = 0;
1317	init.nomb = WDS_NOMB;
1318	init.nimb = WDS_NIMB;
1319
1320	wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
1321	if (wds_cmd(base, (u_int8_t *) & init, sizeof init) != 0) {
1322		device_printf(wp->dev, "wds_cmd init failed\n");
1323		return (1);
1324	}
1325	wds_wait(base + WDS_STAT, WDS_INIT, WDS_INIT);
1326
1327	wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
1328
1329	bzero(&wc, sizeof wc);
1330	wc.cmd = WDSC_DISUNSOL;
1331	if (wds_cmd(base, (char *) &wc, sizeof wc) != 0) {
1332		device_printf(wp->dev, "wds_cmd init2 failed\n");
1333		return (1);
1334	}
1335	return (0);
1336}
1337
1338static int
1339wds_cmd(int base, u_int8_t * p, int l)
1340{
1341	int	s = splcam();
1342
1343	while (l--) {
1344		do {
1345			outb(base + WDS_CMD, *p);
1346			wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
1347		} while (inb(base + WDS_STAT) & WDS_REJ);
1348		p++;
1349	}
1350
1351	wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
1352
1353	splx(s);
1354
1355	return (0);
1356}
1357
1358static void
1359wds_wait(int reg, int mask, int val)
1360{
1361	while ((inb(reg) & mask) != val)
1362		;
1363}
1364
1365static struct wds_req *
1366cmdtovirt(struct wds *wp, u_int32_t phys)
1367{
1368	char	*a;
1369
1370	a = WDSTOVIRT(wp, phys);
1371	if( a < (char *)&wp->dx->req[0] || a>= (char *)&wp->dx->req[MAXSIMUL]) {
1372		device_printf(wp->dev, "weird phys address 0x%x\n", phys);
1373		return (NULL);
1374	}
1375	a -= (int)offsetof(struct wds_req, cmd); /* convert cmd to request */
1376	return ((struct wds_req *)a);
1377}
1378
1379/* for debugging, print out all the data about the status of devices */
1380void
1381wds_print(void)
1382{
1383	int	unit;
1384	int	i;
1385	struct	wds_req *r;
1386	struct	wds     *wp;
1387
1388	for (unit = 0; unit < devclass_get_maxunit(wds_devclass); unit++) {
1389		wp = (struct wds *) devclass_get_device(wds_devclass, unit);
1390		if (wp == NULL)
1391			continue;
1392		printf("wds%d: want_wdsr=0x%x stat=0x%x irq=%s irqstat=0x%x\n",
1393		       unit, wp->want_wdsr, inb(wp->addr + WDS_STAT) & 0xff,
1394		       (inb(wp->addr + WDS_STAT) & WDS_IRQ) ? "ready" : "no",
1395		       inb(wp->addr + WDS_IRQSTAT) & 0xff);
1396		for (i = 0; i < MAXSIMUL; i++) {
1397			r = &wp->dx->req[i];
1398			if( wp->wdsr_free & (1 << r->id) ) {
1399				printf("req=%d flg=0x%x ombn=%d ombstat=%d "
1400				       "mask=0x%x targ=%d lun=%d cmd=0x%x\n",
1401				       i, r->flags, r->ombn,
1402				       wp->dx->ombs[r->ombn].stat,
1403				       r->mask, r->cmd.targ >> 5,
1404				       r->cmd.targ & 7, r->cmd.scb[0]);
1405			}
1406		}
1407	}
1408}
1409
1410#if WDS_DEBUG == 2
1411/* create circular log buffer */
1412static char    *
1413wds_nextlog(void)
1414{
1415	int	n = logwrite;
1416
1417	if (++logwrite >= NLOGLINES)
1418		logwrite = 0;
1419	if (logread == logwrite)
1420		if (++logread >= NLOGLINES)
1421			logread = 0;
1422	return (wds_log[n]);
1423}
1424
1425void
1426wds_printlog(void)
1427{
1428	/* print the circular buffer */
1429	int	i;
1430
1431	for (i = logread; i != logwrite;) {
1432		printf("%s", wds_log[i]);
1433		if (i == NLOGLINES)
1434			i = 0;
1435		else
1436			i++;
1437	}
1438}
1439#endif /* WDS_DEBUG */
1440