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