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 */
38
39#include <sys/cdefs.h>
40__FBSDID("$FreeBSD$");
41
42/* All bugs are subject to removal without further notice */
43
44/*
45 * offe 01/07/95
46 *
47 * This version of the driver _still_ doesn't implement scatter/gather for the
48 * WD7000-FASST2. This is due to the fact that my controller doesn't seem to
49 * support it. That, and the lack of documentation makes it impossible for me
50 * to implement it. What I've done instead is allocated a local buffer,
51 * contiguous buffer big enough to handle the requests. I haven't seen any
52 * read/write bigger than 64k, so I allocate a buffer of 64+16k. The data
53 * that needs to be DMA'd to/from the controller is copied to/from that
54 * buffer before/after the command is sent to the card.
55 *
56 * SB 03/30/00
57 *
58 * An intermediate buffer is needed anyway to make sure that the buffer is
59 * located under 16MB, otherwise it's out of reach of ISA cards. I've added
60 * optimizations to allocate space in buffer in fragments.
61 */
62
63/*
64 * Jumpers: (see The Ref(TM) for more info)
65 * W1/W2 - interrupt selection:
66 *  W1 (1-2) IRQ3, (3-4) IRQ4, (5-6) IRQ5, (7-8) IRQ7, (9-10) IRQ9
67 *  W2 (21-22) IRQ10, (19-20) IRQ11, (17-18) IRQ12, (15-16) IRQ14, (13-14) IRQ15
68 *
69 * W2 - DRQ/DACK selection, DRQ and DACK must be the same:
70 *  (5-6) DRQ5 (11-12) DACK5
71 *  (3-4) DRQ6 (9-10) DACK6
72 *  (1-2) DRQ7 (7-8) DACK7
73 *
74 * W3 - I/O address selection: open pair of pins (OFF) means 1, jumpered (ON) means 0
75 *  pair (1-2) is bit 3, ..., pair (9-10) is bit 7. All the other bits are equal
76 *  to the value 0x300. In bitwise representation that would be:
77 *   0 0 1 1 (9-10) (7-8) (5-6) (3-4) (1-2) 0 0 0
78 *  For example, address 0x3C0, bitwise 1111000000 will be represented as:
79 *   (9-10) OFF, (7-8) OFF, (5-6) ON, (3-4) ON, (1-2) ON
80 *
81 * W4 - BIOS address: open pair of pins (OFF) means 1, jumpered (ON) means 0
82 *  pair (1-2) is bit 13, ..., pair (7-8) is bit 16. All the other bits are
83 *  equal to the value 0xC0000. In bitwise representation that would be:
84 *   1 1 0 (7-8) (5-6) (3-4) (1-2) 0 0000 0000 0000
85 *  For example, address 0xD8000 will be represented as:
86 *   (7-8) OFF, (5-6) OFF, (3-4) ON, (1-2) ON
87 *
88 * W98 (on newer cards) - BIOS enabled; on older cards just remove the BIOS
89 * chip to disable it
90 * W99 (on newer cards) - ROM size (1-2) OFF, (3-4) ON
91 *
92 * W5 - terminator power
93 *  ON - host supplies term. power
94 *  OFF - target supplies term. power
95 *
96 * W6, W9 - floppy support (a bit cryptic):
97 *  W6 ON, W9 ON - disabled
98 *  W6 OFF, W9 ON - enabled with HardCard only
99 *  W6 OFF, W9 OFF - enabled with no hardCard or Combo
100 *
101 * Default: I/O 0x350, IRQ15, DMA6
102 */
103
104/*
105 * debugging levels:
106 * 0 - disabled
107 * 1 - print debugging messages
108 * 2 - collect  debugging messages in an internal log buffer which can be
109 *     printed later by calling wds_printlog from DDB
110 *
111 * Both kind of logs are heavy and interact significantly with the timing
112 * of commands, so the observed problems may become invisible if debug
113 * logging is enabled.
114 *
115 * The light-weight logging facility may be enabled by defining
116 * WDS_ENABLE_SMALLOG as 1. It has very little overhead and allows observing
117 * the traces of various race conditions without affectiong them but the log is
118 * quite terse. The small log can be printer from DDB by calling
119 * wds_printsmallog.
120 */
121#ifndef WDS_DEBUG
122#define WDS_DEBUG 0
123#endif
124
125#ifndef WDS_ENABLE_SMALLOG
126#define WDS_ENABLE_SMALLOG 0
127#endif
128
129#include <sys/types.h>
130#include <sys/param.h>
131#include <sys/systm.h>
132#include <sys/errno.h>
133#include <sys/kernel.h>
134#include <sys/assym.h>
135#include <sys/malloc.h>
136
137#include <sys/bio.h>
138#include <sys/buf.h>
139
140#include <cam/cam.h>
141#include <cam/cam_ccb.h>
142#include <cam/cam_sim.h>
143#include <cam/cam_xpt_sim.h>
144#include <cam/cam_debug.h>
145#include <cam/scsi/scsi_all.h>
146#include <cam/scsi/scsi_message.h>
147
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);
404MODULE_DEPEND(wds, isa, 1, 1, 1);
405MODULE_DEPEND(wds, cam, 1, 1, 1);
406
407#if WDS_ENABLE_SMALLOG==1
408#define SMALLOGSIZ	512
409static char	 wds_smallog[SMALLOGSIZ];
410static char	*wds_smallogp = wds_smallog;
411static char	 wds_smallogover = 0;
412
413static __inline void
414smallog(char c)
415{
416	*wds_smallogp = c;
417	if (++wds_smallogp == &wds_smallog[SMALLOGSIZ]) {
418		wds_smallogp = wds_smallog;
419		wds_smallogover = 1;
420	}
421}
422
423#define smallog2(a, b)	(smallog(a), smallog(b))
424#define smallog3(a, b, c)	(smallog(a), smallog(b), smallog(c))
425#define smallog4(a, b, c, d)	(smallog(a),smallog(b),smallog(c),smallog(d))
426
427void
428wds_printsmallog(void)
429{
430	int	 i;
431	char	*p;
432
433	printf("wds: ");
434	p = wds_smallogover ? wds_smallogp : wds_smallog;
435	i = 0;
436	do {
437		printf("%c", *p);
438		if (++p == &wds_smallog[SMALLOGSIZ])
439			p = wds_smallog;
440		if (++i == 70) {
441			i = 0;
442			printf("\nwds: ");
443		}
444	} while (p != wds_smallogp);
445	printf("\n");
446}
447#else
448#define smallog(a)
449#define smallog2(a, b)
450#define smallog3(a, b, c)
451#define smallog4(a, b, c, d)
452#endif				/* SMALLOG */
453
454static int
455wds_probe(device_t dev)
456{
457	struct	wds *wp;
458	int	error = 0;
459	int	irq;
460
461	/* No pnp support */
462	if (isa_get_vendorid(dev))
463		return (ENXIO);
464
465	wp = (struct wds *) device_get_softc(dev);
466	wp->unit = device_get_unit(dev);
467	wp->dev = dev;
468
469	wp->addr = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/);
470	if (wp->addr == 0 || wp->addr <0x300
471	 || wp->addr > 0x3f8 || wp->addr & 0x7) {
472		device_printf(dev, "invalid port address 0x%x\n", wp->addr);
473		return (ENXIO);
474	}
475
476	if (bus_set_resource(dev, SYS_RES_IOPORT, 0, wp->addr, WDS_NPORTS) < 0)
477		return (ENXIO);
478
479	/* get the DRQ */
480	wp->drq = bus_get_resource_start(dev, SYS_RES_DRQ, 0 /*rid*/);
481	if (wp->drq < 5 || wp->drq > 7) {
482		device_printf(dev, "invalid DRQ %d\n", wp->drq);
483		return (ENXIO);
484	}
485
486	/* get the IRQ */
487	irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0 /*rid*/);
488	if (irq < 3) {
489		device_printf(dev, "invalid IRQ %d\n", irq);
490		return (ENXIO);
491	}
492
493	wp->port_rid = 0;
494	wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT,  &wp->port_rid,
495				        /*start*/ 0, /*end*/ ~0,
496					/*count*/ 0, RF_ACTIVE);
497	if (wp->port_r == NULL)
498		return (ENXIO);
499
500	error = wds_preinit(wp);
501
502	/*
503	 * We cannot hold resources between probe and
504	 * attach as we may never be attached.
505	 */
506	wds_free_resources(wp);
507
508	return (error);
509}
510
511static int
512wds_attach(device_t dev)
513{
514	struct	wds *wp;
515	struct	cam_devq *devq;
516	struct	cam_sim *sim;
517	struct	cam_path *pathp;
518	int	i;
519	int	error = 0;
520
521	wp = (struct wds *)device_get_softc(dev);
522
523	wp->port_rid = 0;
524	wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT,  &wp->port_rid,
525					/*start*/ 0, /*end*/ ~0,
526					/*count*/ 0, RF_ACTIVE);
527	if (wp->port_r == NULL)
528		return (ENXIO);
529
530	/* We must now release resources on error. */
531
532	wp->drq_rid = 0;
533	wp->drq_r = bus_alloc_resource(dev, SYS_RES_DRQ,  &wp->drq_rid,
534				       /*start*/ 0, /*end*/ ~0,
535				       /*count*/ 0, RF_ACTIVE);
536	if (wp->drq_r == NULL)
537		goto bad;
538
539	wp->intr_rid = 0;
540	wp->intr_r = bus_alloc_resource(dev, SYS_RES_IRQ,  &wp->intr_rid,
541					/*start*/ 0, /*end*/ ~0,
542					/*count*/ 0, RF_ACTIVE);
543	if (wp->intr_r == NULL)
544		goto bad;
545	error = bus_setup_intr(dev, wp->intr_r, INTR_TYPE_CAM | INTR_ENTROPY,
546			       NULL, (driver_intr_t *)wds_intr, (void *)wp,
547			       &wp->intr_cookie);
548	if (error)
549		goto bad;
550
551	/* now create the memory buffer */
552	error = bus_dma_tag_create(bus_get_dma_tag(dev), /*alignment*/4,
553				   /*boundary*/0,
554				   /*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
555				   /*highaddr*/ BUS_SPACE_MAXADDR,
556				   /*filter*/ NULL, /*filterarg*/ NULL,
557				   /*maxsize*/ sizeof(* wp->dx),
558				   /*nsegments*/ 1,
559				   /*maxsegsz*/ sizeof(* wp->dx), /*flags*/ 0,
560				   /*lockfunc*/busdma_lock_mutex,
561				   /*lockarg*/&Giant,
562				   &wp->bustag);
563	if (error)
564		goto bad;
565
566	error = bus_dmamem_alloc(wp->bustag, (void **)&wp->dx,
567				 /*flags*/ 0, &wp->busmap);
568	if (error)
569		goto bad;
570
571	bus_dmamap_load(wp->bustag, wp->busmap, (void *)wp->dx,
572			sizeof(* wp->dx), wds_alloc_callback,
573			(void *)&wp->dx_p, /*flags*/0);
574
575	/* initialize the wds_req structures on this unit */
576	for(i=0; i<MAXSIMUL; i++)  {
577		wp->dx->req[i].id = i;
578		wp->wdsr_free |= 1<<i;
579	}
580
581	/* initialize the memory buffer allocation for this unit */
582	if (BUFSIZ / FRAGSIZ > 32) {
583		fragsiz = (BUFSIZ / 32) & ~0x01; /* keep it word-aligned */
584		device_printf(dev, "data buffer fragment size too small.  "
585			      "BUFSIZE / FRAGSIZE must be <= 32\n");
586	} else
587		fragsiz = FRAGSIZ & ~0x01; /* keep it word-aligned */
588
589	wp->data_free = 0;
590	nfrags = 0;
591	for (i = fragsiz; i <= BUFSIZ; i += fragsiz) {
592		nfrags++;
593		wp->data_free = (wp->data_free << 1) | 1;
594	}
595
596	/* complete the hardware initialization */
597	if (wds_init(wp) != 0)
598		goto bad;
599
600	if (wds_getvers(wp) == -1)
601		device_printf(dev, "getvers failed\n");
602	device_printf(dev, "using %d bytes / %d frags for dma buffer\n",
603		      BUFSIZ, nfrags);
604
605	devq = cam_simq_alloc(MAXSIMUL);
606	if (devq == NULL)
607		goto bad;
608
609	sim = cam_sim_alloc(wds_action, wds_poll, "wds", (void *) wp,
610			    wp->unit, &Giant, 1, 1, devq);
611	if (sim == NULL) {
612		cam_simq_free(devq);
613		goto bad;
614	}
615	wp->sim = sim;
616
617	if (xpt_bus_register(sim, dev, 0) != CAM_SUCCESS) {
618		cam_sim_free(sim, /* free_devq */ TRUE);
619		goto bad;
620	}
621	if (xpt_create_path(&pathp, /* periph */ NULL,
622			    cam_sim_path(sim), CAM_TARGET_WILDCARD,
623			    CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
624		xpt_bus_deregister(cam_sim_path(sim));
625		cam_sim_free(sim, /* free_devq */ TRUE);
626		goto bad;
627	}
628	wp->path = pathp;
629
630	return (0);
631
632bad:
633	wds_free_resources(wp);
634	if (error)
635		return (error);
636	else /* exact error is unknown */
637		return (ENXIO);
638}
639
640/* callback to save the physical address */
641static void
642wds_alloc_callback(void *arg, bus_dma_segment_t *seg,  int nseg, int error)
643{
644	*(bus_addr_t *)arg = seg[0].ds_addr;
645}
646
647static void
648wds_free_resources(struct wds *wp)
649{
650	/* check every resource and free if not zero */
651
652	/* interrupt handler */
653	if (wp->intr_r) {
654		bus_teardown_intr(wp->dev, wp->intr_r, wp->intr_cookie);
655		bus_release_resource(wp->dev, SYS_RES_IRQ, wp->intr_rid,
656				     wp->intr_r);
657		wp->intr_r = 0;
658	}
659
660	/* all kinds of memory maps we could have allocated */
661	if (wp->dx_p) {
662		bus_dmamap_unload(wp->bustag, wp->busmap);
663		wp->dx_p = 0;
664	}
665	if (wp->dx) { /* wp->busmap may be legitimately equal to 0 */
666		/* the map will also be freed */
667		bus_dmamem_free(wp->bustag, wp->dx, wp->busmap);
668		wp->dx = 0;
669	}
670	if (wp->bustag) {
671		bus_dma_tag_destroy(wp->bustag);
672		wp->bustag = 0;
673	}
674	/* release all the bus resources */
675	if (wp->drq_r) {
676		bus_release_resource(wp->dev, SYS_RES_DRQ,
677				     wp->drq_rid, wp->drq_r);
678		wp->drq_r = 0;
679	}
680	if (wp->port_r) {
681		bus_release_resource(wp->dev, SYS_RES_IOPORT,
682				     wp->port_rid, wp->port_r);
683		wp->port_r = 0;
684	}
685}
686
687/* allocate contiguous fragments from the buffer */
688static u_int32_t
689frag_alloc(struct wds *wp, int size, u_int8_t **res, u_int32_t *maskp)
690{
691	int	i;
692	u_int32_t	mask;
693	u_int32_t	free;
694
695	if (size > fragsiz * nfrags)
696		return (CAM_REQ_TOO_BIG);
697
698	mask = 1;		/* always allocate at least 1 fragment */
699	for (i = fragsiz; i < size; i += fragsiz)
700		mask = (mask << 1) | 1;
701
702	free = wp->data_free;
703	if(free != 0) {
704		i = ffs(free)-1; /* ffs counts bits from 1 */
705		for (mask <<= i; i < nfrags; i++) {
706			if ((free & mask) == mask) {
707				wp->data_free &= ~mask;	/* mark frags as busy */
708				*maskp = mask;
709				*res = &wp->dx->data[fragsiz * i];
710				DBG(DBX "wds%d: allocated buffer mask=0x%x\n",
711					wp->unit, mask);
712				return (CAM_REQ_CMP);
713			}
714			if (mask & 0x80000000)
715				break;
716
717			mask <<= 1;
718		}
719	}
720	return (CAM_REQUEUE_REQ);	/* no free memory now, try later */
721}
722
723static void
724frag_free(struct wds *wp, u_int32_t mask)
725{
726	wp->data_free |= mask;	/* mark frags as free */
727	DBG(DBX "wds%d: freed buffer mask=0x%x\n", wp->unit, mask);
728}
729
730static struct wds_req *
731wdsr_alloc(struct wds *wp)
732{
733	struct	wds_req *r;
734	int	x;
735	int	i;
736
737	r = NULL;
738	x = splcam();
739
740	/* anyway most of the time only 1 or 2 commands will
741	 * be active because SCSI disconnect is not supported
742	 * by hardware, so the search should be fast enough
743	 */
744	i = ffs(wp->wdsr_free) - 1;
745	if(i < 0) {
746		splx(x);
747		return (NULL);
748	}
749	wp->wdsr_free &= ~ (1<<i);
750	r = &wp->dx->req[i];
751	r->flags = 0;	/* reset all flags */
752	r->ombn = i;		/* luckily we have one omb per wdsr */
753	wp->dx->ombs[i].stat = 1;
754
755	r->mask = 0;
756	splx(x);
757	smallog3('r', i + '0', r->ombn + '0');
758	return (r);
759}
760
761static void
762wds_intr(struct wds *wp)
763{
764	struct	 wds_req *rp;
765	struct	 wds_mb *in;
766	u_int8_t stat;
767	u_int8_t c;
768	int	 addr = wp->addr;
769
770	DBG(DBX "wds%d: interrupt [\n", wp->unit);
771	smallog('[');
772
773	if (inb(addr + WDS_STAT) & WDS_IRQ) {
774		c = inb(addr + WDS_IRQSTAT);
775		if ((c & WDSI_MASK) == WDSI_MSVC) {
776			c = c & ~WDSI_MASK;
777			in = &wp->dx->imbs[c];
778
779			rp = cmdtovirt(wp, scsi_3btoul(in->addr));
780			stat = in->stat;
781
782			if (rp != NULL)
783				wds_done(wp, rp, stat);
784			else
785				device_printf(wp->dev,
786					      "got weird command address %p"
787					      "from controller\n", rp);
788
789			in->stat = 0;
790		} else
791			device_printf(wp->dev,
792				      "weird interrupt, irqstat=0x%x\n", c);
793		outb(addr + WDS_IRQACK, 0);
794	} else {
795		smallog('?');
796	}
797	smallog(']');
798	DBG(DBX "wds%d: ]\n", wp->unit);
799}
800
801static void
802wds_done(struct wds *wp, struct wds_req *r, u_int8_t stat)
803{
804	struct	ccb_hdr *ccb_h;
805	struct	ccb_scsiio *csio;
806	int	status;
807
808	smallog('d');
809
810	if (r->flags & WR_DONE) {
811		device_printf(wp->dev,
812				"request %d reported done twice\n", r->id);
813		smallog2('x', r->id + '0');
814		return;
815	}
816
817	smallog(r->id + '0');
818	ccb_h = &r->ccb->ccb_h;
819	csio = &r->ccb->csio;
820	status = CAM_REQ_CMP_ERR;
821
822	DBG(DBX "wds%d: %s stat=0x%x c->stat=0x%x c->venderr=0x%x\n", wp->unit,
823	    r->flags & WR_SENSE ? "(sense)" : "",
824		stat, r->cmd.stat, r->cmd.venderr);
825
826	if (r->flags & WR_SENSE) {
827		if (stat == ICMB_OK || (stat == ICMB_OKERR && r->cmd.stat == 0)) {
828			DBG(DBX "wds%d: sense 0x%x\n", wp->unit, r->buf[0]);
829			/* it has the same size now but for future */
830			bcopy(r->buf, &csio->sense_data,
831			      sizeof(struct scsi_sense_data) > csio->sense_len ?
832			      csio->sense_len : sizeof(struct scsi_sense_data));
833			if (sizeof(struct scsi_sense_data) >= csio->sense_len)
834				csio->sense_resid = 0;
835			else
836				csio->sense_resid =
837					csio->sense_len
838				      - sizeof(struct scsi_sense_data);
839			status = CAM_AUTOSNS_VALID | CAM_SCSI_STATUS_ERROR;
840		} else {
841			status = CAM_AUTOSENSE_FAIL;
842		}
843	} else {
844		switch (stat) {
845		case ICMB_OK:
846			if (ccb_h) {
847				csio->resid = 0;
848				csio->scsi_status = r->cmd.stat;
849				status = CAM_REQ_CMP;
850			}
851			break;
852		case ICMB_OKERR:
853			if (ccb_h) {
854				csio->scsi_status = r->cmd.stat;
855				if (r->cmd.stat) {
856					if (ccb_h->flags & CAM_DIS_AUTOSENSE)
857						status = CAM_SCSI_STATUS_ERROR;
858					else {
859						if ( wds_runsense(wp, r) == CAM_REQ_CMP )
860							return;
861						/* in case of error continue with freeing of CCB */
862					}
863				} else {
864					csio->resid = 0;
865					status = CAM_REQ_CMP;
866				}
867			}
868			break;
869		case ICMB_ETIME:
870			if (ccb_h)
871				status = CAM_SEL_TIMEOUT;
872			break;
873		case ICMB_ERESET:
874		case ICMB_ETARCMD:
875		case ICMB_ERESEL:
876		case ICMB_ESEL:
877		case ICMB_EABORT:
878		case ICMB_ESRESET:
879		case ICMB_EHRESET:
880			if (ccb_h)
881				status = CAM_REQ_CMP_ERR;
882			break;
883		}
884
885		if (ccb_h && (ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN) {
886			/* we accept only virtual addresses in wds_action() */
887			bcopy(r->buf, csio->data_ptr, csio->dxfer_len);
888		}
889	}
890
891	r->flags |= WR_DONE;
892	wp->dx->ombs[r->ombn].stat = 0;
893
894	if (ccb_h) {
895		wdsr_ccb_done(wp, r, r->ccb, status);
896		smallog3('-', ccb_h->target_id + '0', ccb_h->target_lun + '0');
897	} else {
898		frag_free(wp, r->mask);
899		if (wp->want_wdsr) {
900			wp->want_wdsr = 0;
901			xpt_release_simq(wp->sim, /* run queue */ 1);
902		}
903		wp->wdsr_free |= (1 << r->id);
904	}
905
906	DBG(DBX "wds%d: request %p done\n", wp->unit, r);
907}
908
909/* command returned bad status, request sense */
910
911static int
912wds_runsense(struct wds *wp, struct wds_req *r)
913{
914	u_int8_t          c;
915	struct	ccb_hdr *ccb_h;
916
917	ccb_h = &r->ccb->ccb_h;
918
919	r->flags |= WR_SENSE;
920	scsi_ulto3b(WDSTOPHYS(wp, &r->cmd),
921	 wp->dx->ombs[r->ombn].addr);
922	bzero(&r->cmd, sizeof r->cmd);
923	r->cmd.cmd = WDSX_SCSICMD;
924	r->cmd.targ = (ccb_h->target_id << 5) |
925		ccb_h->target_lun;
926
927	scsi_ulto3b(0, r->cmd.next);
928
929	r->cmd.scb[0] = REQUEST_SENSE;
930	r->cmd.scb[1] = ccb_h->target_lun << 5;
931	r->cmd.scb[4] = sizeof(struct scsi_sense_data);
932	r->cmd.scb[5] = 0;
933	scsi_ulto3b(WDSTOPHYS(wp, r->buf), r->cmd.data);
934	scsi_ulto3b(sizeof(struct scsi_sense_data), r->cmd.len);
935	r->cmd.write = 0x80;
936
937	outb(wp->addr + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
938
939	wp->dx->ombs[r->ombn].stat = 1;
940	c = WDSC_MSTART(r->ombn);
941
942	if (wds_cmd(wp->addr, &c, sizeof c) != 0) {
943		device_printf(wp->dev, "unable to start outgoing sense mbox\n");
944		wp->dx->ombs[r->ombn].stat = 0;
945		wdsr_ccb_done(wp, r, r->ccb, CAM_AUTOSENSE_FAIL);
946		return CAM_AUTOSENSE_FAIL;
947	} else {
948		DBG(DBX "wds%d: enqueued status cmd 0x%x, r=%p\n",
949			wp->unit, r->cmd.scb[0] & 0xFF, r);
950		/* don't free CCB yet */
951		smallog3('*', ccb_h->target_id + '0',
952			 ccb_h->target_lun + '0');
953		return CAM_REQ_CMP;
954	}
955}
956
957static int
958wds_getvers(struct wds *wp)
959{
960	struct	 wds_req *r;
961	int	 base;
962	u_int8_t c;
963	int	 i;
964
965	base = wp->addr;
966
967	r = wdsr_alloc(wp);
968	if (!r) {
969		device_printf(wp->dev, "no request slot available!\n");
970		return (-1);
971	}
972	r->flags &= ~WR_DONE;
973
974	r->ccb = NULL;
975
976	scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr);
977
978	bzero(&r->cmd, sizeof r->cmd);
979	r->cmd.cmd = WDSX_GETFIRMREV;
980
981	outb(base + WDS_HCR, WDSH_DRQEN);
982
983	c = WDSC_MSTART(r->ombn);
984	if (wds_cmd(base, (u_int8_t *) & c, sizeof c)) {
985		device_printf(wp->dev, "version request failed\n");
986		wp->wdsr_free |= (1 << r->id);
987		wp->dx->ombs[r->ombn].stat = 0;
988		return (-1);
989	}
990	while (1) {
991		i = 0;
992		while ((inb(base + WDS_STAT) & WDS_IRQ) == 0) {
993			DELAY(9000);
994			if (++i == 100) {
995				device_printf(wp->dev, "getvers timeout\n");
996				return (-1);
997			}
998		}
999		wds_intr(wp);
1000		if (r->flags & WR_DONE) {
1001			device_printf(wp->dev, "firmware version %d.%02d\n",
1002			       r->cmd.targ, r->cmd.scb[0]);
1003			wp->wdsr_free |= (1 << r->id);
1004			return (0);
1005		}
1006	}
1007}
1008
1009static void
1010wdsr_ccb_done(struct wds *wp, struct wds_req *r,
1011	      union ccb *ccb, u_int32_t status)
1012{
1013	ccb->ccb_h.ccb_wdsr = 0;
1014
1015	if (r != NULL) {
1016		/* To implement timeouts we would need to know how to abort the
1017		 * command on controller, and this is a great mystery.
1018		 * So for now we just pass the responsibility for timeouts
1019		 * to the controlles itself, it does that reasonably good.
1020		 */
1021		/* untimeout(_timeout, (caddr_t) hcb, ccb->ccb_h.timeout_ch); */
1022		/* we're about to free a hcb, so the shortage has ended */
1023		frag_free(wp, r->mask);
1024		if (wp->want_wdsr && status != CAM_REQUEUE_REQ) {
1025			wp->want_wdsr = 0;
1026			status |= CAM_RELEASE_SIMQ;
1027			smallog('R');
1028		}
1029		wp->wdsr_free |= (1 << r->id);
1030	}
1031	ccb->ccb_h.status =
1032	    status | (ccb->ccb_h.status & ~(CAM_STATUS_MASK | CAM_SIM_QUEUED));
1033	xpt_done(ccb);
1034}
1035
1036static void
1037wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio)
1038{
1039	int	 unit = cam_sim_unit(sim);
1040	struct	 wds *wp;
1041	struct	 ccb_hdr *ccb_h;
1042	struct	 wds_req *r;
1043	int	 base;
1044	u_int8_t c;
1045	int	 error;
1046	int	 n;
1047
1048	wp = (struct wds *)cam_sim_softc(sim);
1049	ccb_h = &csio->ccb_h;
1050
1051	DBG(DBX "wds%d: cmd TARG=%d LUN=%d\n", unit, ccb_h->target_id,
1052	    ccb_h->target_lun);
1053
1054	if (ccb_h->target_id > 7 || ccb_h->target_id == WDS_HBA_ID) {
1055		ccb_h->status = CAM_TID_INVALID;
1056		xpt_done((union ccb *) csio);
1057		return;
1058	}
1059	if (ccb_h->target_lun > 7) {
1060		ccb_h->status = CAM_LUN_INVALID;
1061		xpt_done((union ccb *) csio);
1062		return;
1063	}
1064	if (csio->dxfer_len > BUFSIZ) {
1065		ccb_h->status = CAM_REQ_TOO_BIG;
1066		xpt_done((union ccb *) csio);
1067		return;
1068	}
1069	if ((ccb_h->flags & CAM_DATA_MASK) != CAM_DATA_VADDR) {
1070		/* don't support these */
1071		ccb_h->status = CAM_REQ_INVALID;
1072		xpt_done((union ccb *) csio);
1073		return;
1074	}
1075	base = wp->addr;
1076
1077	/*
1078	 * this check is mostly for debugging purposes,
1079	 * "can't happen" normally.
1080	 */
1081	if(wp->want_wdsr) {
1082		DBG(DBX "wds%d: someone already waits for buffer\n", unit);
1083		smallog('b');
1084		n = xpt_freeze_simq(sim, /* count */ 1);
1085		smallog('0'+n);
1086		ccb_h->status = CAM_REQUEUE_REQ;
1087		xpt_done((union ccb *) csio);
1088		return;
1089	}
1090
1091	r = wdsr_alloc(wp);
1092	if (r == NULL) {
1093		device_printf(wp->dev, "no request slot available!\n");
1094		wp->want_wdsr = 1;
1095		n = xpt_freeze_simq(sim, /* count */ 1);
1096		smallog2('f', '0'+n);
1097		ccb_h->status = CAM_REQUEUE_REQ;
1098		xpt_done((union ccb *) csio);
1099		return;
1100	}
1101
1102	ccb_h->ccb_wdsr = (void *) r;
1103	r->ccb = (union ccb *) csio;
1104
1105	switch (error = frag_alloc(wp, csio->dxfer_len, &r->buf, &r->mask)) {
1106	case CAM_REQ_CMP:
1107		break;
1108	case CAM_REQUEUE_REQ:
1109		DBG(DBX "wds%d: no data buffer available\n", unit);
1110		wp->want_wdsr = 1;
1111		n = xpt_freeze_simq(sim, /* count */ 1);
1112		smallog2('f', '0'+n);
1113		wdsr_ccb_done(wp, r, r->ccb, CAM_REQUEUE_REQ);
1114		return;
1115	default:
1116		DBG(DBX "wds%d: request is too big\n", unit);
1117		wdsr_ccb_done(wp, r, r->ccb, error);
1118		break;
1119	}
1120
1121	ccb_h->status |= CAM_SIM_QUEUED;
1122	r->flags &= ~WR_DONE;
1123
1124	scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr);
1125
1126	bzero(&r->cmd, sizeof r->cmd);
1127	r->cmd.cmd = WDSX_SCSICMD;
1128	r->cmd.targ = (ccb_h->target_id << 5) | ccb_h->target_lun;
1129
1130	if (ccb_h->flags & CAM_CDB_POINTER)
1131		bcopy(csio->cdb_io.cdb_ptr, &r->cmd.scb,
1132		      csio->cdb_len < 12 ? csio->cdb_len : 12);
1133	else
1134		bcopy(csio->cdb_io.cdb_bytes, &r->cmd.scb,
1135		      csio->cdb_len < 12 ? csio->cdb_len : 12);
1136
1137	scsi_ulto3b(csio->dxfer_len, r->cmd.len);
1138
1139	if (csio->dxfer_len > 0
1140	 && (ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_OUT) {
1141		/* we already rejected physical or scattered addresses */
1142		bcopy(csio->data_ptr, r->buf, csio->dxfer_len);
1143	}
1144	scsi_ulto3b(csio->dxfer_len ? WDSTOPHYS(wp, r->buf) : 0, r->cmd.data);
1145
1146	if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN)
1147		r->cmd.write = 0x80;
1148	else
1149		r->cmd.write = 0x00;
1150
1151	scsi_ulto3b(0, r->cmd.next);
1152
1153	outb(base + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
1154
1155	c = WDSC_MSTART(r->ombn);
1156
1157	if (wds_cmd(base, &c, sizeof c) != 0) {
1158		device_printf(wp->dev, "unable to start outgoing mbox\n");
1159		wp->dx->ombs[r->ombn].stat = 0;
1160		wdsr_ccb_done(wp, r, r->ccb, CAM_RESRC_UNAVAIL);
1161		return;
1162	}
1163	DBG(DBX "wds%d: enqueued cmd 0x%x, r=%p\n", unit,
1164	    r->cmd.scb[0] & 0xFF, r);
1165
1166	smallog3('+', ccb_h->target_id + '0', ccb_h->target_lun + '0');
1167}
1168
1169static void
1170wds_action(struct cam_sim * sim, union ccb * ccb)
1171{
1172	int	unit = cam_sim_unit(sim);
1173	int	s;
1174
1175	DBG(DBX "wds%d: action 0x%x\n", unit, ccb->ccb_h.func_code);
1176	switch (ccb->ccb_h.func_code) {
1177	case XPT_SCSI_IO:
1178		s = splcam();
1179		DBG(DBX "wds%d: SCSI IO entered\n", unit);
1180		wds_scsi_io(sim, &ccb->csio);
1181		DBG(DBX "wds%d: SCSI IO returned\n", unit);
1182		splx(s);
1183		break;
1184	case XPT_RESET_BUS:
1185		/* how to do it right ? */
1186		printf("wds%d: reset\n", unit);
1187		ccb->ccb_h.status = CAM_REQ_CMP;
1188		xpt_done(ccb);
1189		break;
1190	case XPT_ABORT:
1191		ccb->ccb_h.status = CAM_UA_ABORT;
1192		xpt_done(ccb);
1193		break;
1194	case XPT_CALC_GEOMETRY:
1195	{
1196		struct	  ccb_calc_geometry *ccg;
1197		u_int32_t size_mb;
1198		u_int32_t secs_per_cylinder;
1199
1200		ccg = &ccb->ccg;
1201		size_mb = ccg->volume_size
1202			/ ((1024L * 1024L) / ccg->block_size);
1203
1204		ccg->heads = 64;
1205		ccg->secs_per_track = 16;
1206		secs_per_cylinder = ccg->heads * ccg->secs_per_track;
1207		ccg->cylinders = ccg->volume_size / secs_per_cylinder;
1208		ccb->ccb_h.status = CAM_REQ_CMP;
1209		xpt_done(ccb);
1210		break;
1211	}
1212	case XPT_PATH_INQ:	/* Path routing inquiry */
1213	{
1214		struct ccb_pathinq *cpi = &ccb->cpi;
1215
1216		cpi->version_num = 1;	/* XXX??? */
1217		cpi->hba_inquiry = 0;	/* nothing fancy */
1218		cpi->target_sprt = 0;
1219		cpi->hba_misc = 0;
1220		cpi->hba_eng_cnt = 0;
1221		cpi->max_target = 7;
1222		cpi->max_lun = 7;
1223		cpi->initiator_id = WDS_HBA_ID;
1224		cpi->hba_misc = 0;
1225		cpi->bus_id = cam_sim_bus(sim);
1226		cpi->base_transfer_speed = 3300;
1227		strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
1228		strncpy(cpi->hba_vid, "WD/FDC", HBA_IDLEN);
1229		strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
1230		cpi->unit_number = cam_sim_unit(sim);
1231		cpi->ccb_h.status = CAM_REQ_CMP;
1232		xpt_done(ccb);
1233		break;
1234	}
1235	default:
1236		ccb->ccb_h.status = CAM_REQ_INVALID;
1237		xpt_done(ccb);
1238		break;
1239	}
1240}
1241
1242static void
1243wds_poll(struct cam_sim * sim)
1244{
1245	wds_intr((struct wds *)cam_sim_softc(sim));
1246}
1247
1248/* part of initialization done in probe() */
1249/* returns 0 if OK, ENXIO if bad */
1250
1251static int
1252wds_preinit(struct wds *wp)
1253{
1254	int	base;
1255	int	i;
1256
1257	base = wp->addr;
1258
1259	/*
1260	 * Sending a command causes the CMDRDY bit to clear.
1261	 */
1262	outb(base + WDS_CMD, WDSC_NOOP);
1263	if (inb(base + WDS_STAT) & WDS_RDY)
1264		return (ENXIO);
1265
1266	/*
1267	 * the controller exists. reset and init.
1268	 */
1269	outb(base + WDS_HCR, WDSH_ASCRESET | WDSH_SCSIRESET);
1270	DELAY(30);
1271	outb(base + WDS_HCR, 0);
1272
1273	if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
1274		for (i = 0; i < 10; i++) {
1275			if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY)
1276				break;
1277			DELAY(40000);
1278		}
1279		if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY)
1280			/* probe timeout */
1281			return (ENXIO);
1282	}
1283
1284	return (0);
1285}
1286
1287/* part of initialization done in attach() */
1288/* returns 0 if OK, 1 if bad */
1289
1290static int
1291wds_init(struct wds *wp)
1292{
1293	struct	wds_setup init;
1294	int	base;
1295	int	i;
1296	struct	wds_cmd  wc;
1297
1298	base = wp->addr;
1299
1300	outb(base + WDS_HCR, WDSH_DRQEN);
1301
1302	isa_dmacascade(wp->drq);
1303
1304	if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
1305		for (i = 0; i < 10; i++) {
1306			if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY)
1307				break;
1308			DELAY(40000);
1309		}
1310		if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY)
1311			/* probe timeout */
1312			return (1);
1313	}
1314	bzero(&init, sizeof init);
1315	init.cmd = WDSC_INIT;
1316	init.scsi_id = WDS_HBA_ID;
1317	init.buson_t = 24;
1318	init.busoff_t = 48;
1319	scsi_ulto3b(WDSTOPHYS(wp, &wp->dx->ombs), init.mbaddr);
1320	init.xx = 0;
1321	init.nomb = WDS_NOMB;
1322	init.nimb = WDS_NIMB;
1323
1324	wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
1325	if (wds_cmd(base, (u_int8_t *) & init, sizeof init) != 0) {
1326		device_printf(wp->dev, "wds_cmd init failed\n");
1327		return (1);
1328	}
1329	wds_wait(base + WDS_STAT, WDS_INIT, WDS_INIT);
1330
1331	wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
1332
1333	bzero(&wc, sizeof wc);
1334	wc.cmd = WDSC_DISUNSOL;
1335	if (wds_cmd(base, (char *) &wc, sizeof wc) != 0) {
1336		device_printf(wp->dev, "wds_cmd init2 failed\n");
1337		return (1);
1338	}
1339	return (0);
1340}
1341
1342static int
1343wds_cmd(int base, u_int8_t * p, int l)
1344{
1345	int	s = splcam();
1346
1347	while (l--) {
1348		do {
1349			outb(base + WDS_CMD, *p);
1350			wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
1351		} while (inb(base + WDS_STAT) & WDS_REJ);
1352		p++;
1353	}
1354
1355	wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
1356
1357	splx(s);
1358
1359	return (0);
1360}
1361
1362static void
1363wds_wait(int reg, int mask, int val)
1364{
1365	while ((inb(reg) & mask) != val)
1366		;
1367}
1368
1369static struct wds_req *
1370cmdtovirt(struct wds *wp, u_int32_t phys)
1371{
1372	char	*a;
1373
1374	a = WDSTOVIRT(wp, (uintptr_t)phys);
1375	if( a < (char *)&wp->dx->req[0] || a>= (char *)&wp->dx->req[MAXSIMUL]) {
1376		device_printf(wp->dev, "weird phys address 0x%x\n", phys);
1377		return (NULL);
1378	}
1379	a -= (int)offsetof(struct wds_req, cmd); /* convert cmd to request */
1380	return ((struct wds_req *)a);
1381}
1382
1383/* for debugging, print out all the data about the status of devices */
1384void
1385wds_print(void)
1386{
1387	int	unit;
1388	int	i;
1389	struct	wds_req *r;
1390	struct	wds     *wp;
1391
1392	for (unit = 0; unit < devclass_get_maxunit(wds_devclass); unit++) {
1393		wp = (struct wds *) devclass_get_device(wds_devclass, unit);
1394		if (wp == NULL)
1395			continue;
1396		printf("wds%d: want_wdsr=0x%x stat=0x%x irq=%s irqstat=0x%x\n",
1397		       unit, wp->want_wdsr, inb(wp->addr + WDS_STAT) & 0xff,
1398		       (inb(wp->addr + WDS_STAT) & WDS_IRQ) ? "ready" : "no",
1399		       inb(wp->addr + WDS_IRQSTAT) & 0xff);
1400		for (i = 0; i < MAXSIMUL; i++) {
1401			r = &wp->dx->req[i];
1402			if( wp->wdsr_free & (1 << r->id) ) {
1403				printf("req=%d flg=0x%x ombn=%d ombstat=%d "
1404				       "mask=0x%x targ=%d lun=%d cmd=0x%x\n",
1405				       i, r->flags, r->ombn,
1406				       wp->dx->ombs[r->ombn].stat,
1407				       r->mask, r->cmd.targ >> 5,
1408				       r->cmd.targ & 7, r->cmd.scb[0]);
1409			}
1410		}
1411	}
1412}
1413
1414#if WDS_DEBUG == 2
1415/* create circular log buffer */
1416static char    *
1417wds_nextlog(void)
1418{
1419	int	n = logwrite;
1420
1421	if (++logwrite >= NLOGLINES)
1422		logwrite = 0;
1423	if (logread == logwrite)
1424		if (++logread >= NLOGLINES)
1425			logread = 0;
1426	return (wds_log[n]);
1427}
1428
1429void
1430wds_printlog(void)
1431{
1432	/* print the circular buffer */
1433	int	i;
1434
1435	for (i = logread; i != logwrite;) {
1436		printf("%s", wds_log[i]);
1437		if (i == NLOGLINES)
1438			i = 0;
1439		else
1440			i++;
1441	}
1442}
1443#endif /* WDS_DEBUG */
1444