1139749Simp/*-
267482Sbabkin * Copyright (c) 1994 Ludd, University of Lule}, Sweden.
367482Sbabkin * Copyright (c) 2000 Sergey A. Babkin
467482Sbabkin * All rights reserved.
567482Sbabkin *
667482Sbabkin * Written by Olof Johansson (offe@ludd.luth.se) 1995.
767482Sbabkin * Based on code written by Theo de Raadt (deraadt@fsa.ca).
867482Sbabkin * Resurrected, ported to CAM and generally cleaned up by Sergey Babkin
967482Sbabkin * <babkin@bellatlantic.net> or <babkin@users.sourceforge.net>.
1067482Sbabkin *
1167482Sbabkin * Redistribution and use in source and binary forms, with or without
1267482Sbabkin * modification, are permitted provided that the following conditions
1367482Sbabkin * are met:
1467482Sbabkin * 1. Redistributions of source code must retain the above copyright
1567482Sbabkin *    notice, this list of conditions and the following disclaimer.
1667482Sbabkin * 2. Redistributions in binary form must reproduce the above copyright
1767482Sbabkin *    notice, this list of conditions and the following disclaimer in the
1867482Sbabkin *    documentation and/or other materials provided with the distribution.
1967482Sbabkin * 3. All advertising materials mentioning features or use of this software
2067482Sbabkin *    must display the following acknowledgement:
2167482Sbabkin *     This product includes software developed at Ludd, University of Lule}
2267482Sbabkin *     and by the FreeBSD project.
2367482Sbabkin * 4. The name of the author may not be used to endorse or promote products
2467482Sbabkin *    derived from this software without specific prior written permission
2567482Sbabkin *
2667482Sbabkin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2767482Sbabkin * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2867482Sbabkin * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2967482Sbabkin * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
3067482Sbabkin * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3167482Sbabkin * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3267482Sbabkin * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3367482Sbabkin * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3467482Sbabkin * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3567482Sbabkin * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3667482Sbabkin *
3767482Sbabkin */
3867482Sbabkin
39119418Sobrien#include <sys/cdefs.h>
40119418Sobrien__FBSDID("$FreeBSD$");
41119418Sobrien
4267482Sbabkin/* All bugs are subject to removal without further notice */
4367482Sbabkin
4467482Sbabkin/*
4567482Sbabkin * offe 01/07/95
4667482Sbabkin *
4767482Sbabkin * This version of the driver _still_ doesn't implement scatter/gather for the
4867482Sbabkin * WD7000-FASST2. This is due to the fact that my controller doesn't seem to
4967482Sbabkin * support it. That, and the lack of documentation makes it impossible for me
5067482Sbabkin * to implement it. What I've done instead is allocated a local buffer,
5167482Sbabkin * contiguous buffer big enough to handle the requests. I haven't seen any
5267482Sbabkin * read/write bigger than 64k, so I allocate a buffer of 64+16k. The data
5367482Sbabkin * that needs to be DMA'd to/from the controller is copied to/from that
5467482Sbabkin * buffer before/after the command is sent to the card.
5567482Sbabkin *
5667482Sbabkin * SB 03/30/00
5767482Sbabkin *
5867482Sbabkin * An intermediate buffer is needed anyway to make sure that the buffer is
5967482Sbabkin * located under 16MB, otherwise it's out of reach of ISA cards. I've added
6067482Sbabkin * optimizations to allocate space in buffer in fragments.
6167482Sbabkin */
6267482Sbabkin
6367482Sbabkin/*
6467482Sbabkin * Jumpers: (see The Ref(TM) for more info)
6567482Sbabkin * W1/W2 - interrupt selection:
6667482Sbabkin *  W1 (1-2) IRQ3, (3-4) IRQ4, (5-6) IRQ5, (7-8) IRQ7, (9-10) IRQ9
6767482Sbabkin *  W2 (21-22) IRQ10, (19-20) IRQ11, (17-18) IRQ12, (15-16) IRQ14, (13-14) IRQ15
6867482Sbabkin *
6967482Sbabkin * W2 - DRQ/DACK selection, DRQ and DACK must be the same:
7067482Sbabkin *  (5-6) DRQ5 (11-12) DACK5
7167482Sbabkin *  (3-4) DRQ6 (9-10) DACK6
7267482Sbabkin *  (1-2) DRQ7 (7-8) DACK7
7367482Sbabkin *
7467482Sbabkin * W3 - I/O address selection: open pair of pins (OFF) means 1, jumpered (ON) means 0
7567482Sbabkin *  pair (1-2) is bit 3, ..., pair (9-10) is bit 7. All the other bits are equal
7667482Sbabkin *  to the value 0x300. In bitwise representation that would be:
7767482Sbabkin *   0 0 1 1 (9-10) (7-8) (5-6) (3-4) (1-2) 0 0 0
7867482Sbabkin *  For example, address 0x3C0, bitwise 1111000000 will be represented as:
7967482Sbabkin *   (9-10) OFF, (7-8) OFF, (5-6) ON, (3-4) ON, (1-2) ON
8067482Sbabkin *
8167482Sbabkin * W4 - BIOS address: open pair of pins (OFF) means 1, jumpered (ON) means 0
8267482Sbabkin *  pair (1-2) is bit 13, ..., pair (7-8) is bit 16. All the other bits are
8367482Sbabkin *  equal to the value 0xC0000. In bitwise representation that would be:
8467482Sbabkin *   1 1 0 (7-8) (5-6) (3-4) (1-2) 0 0000 0000 0000
8567482Sbabkin *  For example, address 0xD8000 will be represented as:
8667482Sbabkin *   (7-8) OFF, (5-6) OFF, (3-4) ON, (1-2) ON
8767482Sbabkin *
8867482Sbabkin * W98 (on newer cards) - BIOS enabled; on older cards just remove the BIOS
8967482Sbabkin * chip to disable it
9067482Sbabkin * W99 (on newer cards) - ROM size (1-2) OFF, (3-4) ON
9167482Sbabkin *
9267482Sbabkin * W5 - terminator power
9367482Sbabkin *  ON - host supplies term. power
9467482Sbabkin *  OFF - target supplies term. power
9567482Sbabkin *
9667482Sbabkin * W6, W9 - floppy support (a bit cryptic):
9767482Sbabkin *  W6 ON, W9 ON - disabled
9867482Sbabkin *  W6 OFF, W9 ON - enabled with HardCard only
9967482Sbabkin *  W6 OFF, W9 OFF - enabled with no hardCard or Combo
10067482Sbabkin *
10167482Sbabkin * Default: I/O 0x350, IRQ15, DMA6
10267482Sbabkin */
10367482Sbabkin
10467482Sbabkin/*
10567482Sbabkin * debugging levels:
10667482Sbabkin * 0 - disabled
10767482Sbabkin * 1 - print debugging messages
10867482Sbabkin * 2 - collect  debugging messages in an internal log buffer which can be
10967482Sbabkin *     printed later by calling wds_printlog from DDB
11067482Sbabkin *
11167482Sbabkin * Both kind of logs are heavy and interact significantly with the timing
11267482Sbabkin * of commands, so the observed problems may become invisible if debug
11367482Sbabkin * logging is enabled.
11467482Sbabkin *
11567482Sbabkin * The light-weight logging facility may be enabled by defining
11667482Sbabkin * WDS_ENABLE_SMALLOG as 1. It has very little overhead and allows observing
11767482Sbabkin * the traces of various race conditions without affectiong them but the log is
11867482Sbabkin * quite terse. The small log can be printer from DDB by calling
11967482Sbabkin * wds_printsmallog.
12067482Sbabkin */
12167482Sbabkin#ifndef WDS_DEBUG
12267482Sbabkin#define WDS_DEBUG 0
12367482Sbabkin#endif
12467482Sbabkin
12567482Sbabkin#ifndef WDS_ENABLE_SMALLOG
12667482Sbabkin#define WDS_ENABLE_SMALLOG 0
12767482Sbabkin#endif
12867482Sbabkin
12967482Sbabkin#include <sys/types.h>
13067482Sbabkin#include <sys/param.h>
13167482Sbabkin#include <sys/systm.h>
13267482Sbabkin#include <sys/errno.h>
13367482Sbabkin#include <sys/kernel.h>
13467482Sbabkin#include <sys/assym.h>
135168752Sscottl#include <sys/malloc.h>
13667482Sbabkin
13767482Sbabkin#include <sys/bio.h>
13867482Sbabkin#include <sys/buf.h>
13967482Sbabkin
14067482Sbabkin#include <cam/cam.h>
14167482Sbabkin#include <cam/cam_ccb.h>
14267482Sbabkin#include <cam/cam_sim.h>
14367482Sbabkin#include <cam/cam_xpt_sim.h>
14467482Sbabkin#include <cam/cam_debug.h>
14567482Sbabkin#include <cam/scsi/scsi_all.h>
14667482Sbabkin#include <cam/scsi/scsi_message.h>
14767482Sbabkin
14867482Sbabkin
14967482Sbabkin#include <vm/vm.h>
15067482Sbabkin#include <vm/vm_param.h>
15167482Sbabkin#include <vm/pmap.h>
15267482Sbabkin
15367482Sbabkin#include <sys/module.h>
15467482Sbabkin#include <sys/bus.h>
15567482Sbabkin#include <machine/bus.h>
15667482Sbabkin#include <machine/resource.h>
15767482Sbabkin#include <sys/rman.h>
15867482Sbabkin
15967482Sbabkin#include <isa/isavar.h>
16067482Sbabkin#include <isa/pnpvar.h>
16167482Sbabkin
16267482Sbabkin#define WDSTOPHYS(wp, a)	( ((u_long)a) - ((u_long)wp->dx) + ((u_long)wp->dx_p) )
16367482Sbabkin#define WDSTOVIRT(wp, a)	( ((char *)a) - ((char*)wp->dx_p) + ((char *)wp->dx) )
16467482Sbabkin
16567482Sbabkin/* 0x10000 (64k) should be enough. But just to be sure... */
16667482Sbabkin#define BUFSIZ 		0x12000
16767482Sbabkin/* buffer fragment size, no more than 32 frags per buffer */
16867482Sbabkin#define FRAGSIZ		0x1000
16967482Sbabkin
17067482Sbabkin
17167482Sbabkin/* WD7000 registers */
17267482Sbabkin#define WDS_STAT		0	/* read */
17367482Sbabkin#define WDS_IRQSTAT		1	/* read */
17467482Sbabkin
17567482Sbabkin#define WDS_CMD			0	/* write */
17667482Sbabkin#define WDS_IRQACK		1	/* write */
17767482Sbabkin#define WDS_HCR			2	/* write */
17867482Sbabkin
17967482Sbabkin#define WDS_NPORTS		4 /* number of ports used */
18067482Sbabkin
18167482Sbabkin/* WDS_STAT (read) defs */
18267482Sbabkin#define WDS_IRQ			0x80
18367482Sbabkin#define WDS_RDY			0x40
18467482Sbabkin#define WDS_REJ			0x20
18567482Sbabkin#define WDS_INIT		0x10
18667482Sbabkin
18767482Sbabkin/* WDS_IRQSTAT (read) defs */
18867482Sbabkin#define WDSI_MASK		0xc0
18967482Sbabkin#define WDSI_ERR		0x00
19067482Sbabkin#define WDSI_MFREE		0x80
19167482Sbabkin#define WDSI_MSVC		0xc0
19267482Sbabkin
19367482Sbabkin/* WDS_CMD (write) defs */
19467482Sbabkin#define WDSC_NOOP		0x00
19567482Sbabkin#define WDSC_INIT		0x01
19667482Sbabkin#define WDSC_DISUNSOL		0x02 /* disable unsolicited ints */
19767482Sbabkin#define WDSC_ENAUNSOL		0x03 /* enable unsolicited ints */
19867482Sbabkin#define WDSC_IRQMFREE		0x04 /* interrupt on free RQM */
19967482Sbabkin#define WDSC_SCSIRESETSOFT	0x05 /* soft reset */
20067482Sbabkin#define WDSC_SCSIRESETHARD	0x06 /* hard reset ack */
20167482Sbabkin#define WDSC_MSTART(m)		(0x80 + (m)) /* start mailbox */
20267482Sbabkin#define WDSC_MMSTART(m)		(0xc0 + (m)) /* start all mailboxes */
20367482Sbabkin
20467482Sbabkin/* WDS_HCR (write) defs */
20567482Sbabkin#define WDSH_IRQEN		0x08
20667482Sbabkin#define WDSH_DRQEN		0x04
20767482Sbabkin#define WDSH_SCSIRESET		0x02
20867482Sbabkin#define WDSH_ASCRESET		0x01
20967482Sbabkin
21067482Sbabkinstruct wds_cmd {
21167482Sbabkin	u_int8_t	cmd;
21267482Sbabkin	u_int8_t	targ;
21367482Sbabkin	u_int8_t	scb[12];
21467482Sbabkin	u_int8_t	stat;
21567482Sbabkin	u_int8_t	venderr;
21667482Sbabkin	u_int8_t	len[3];
21767482Sbabkin	u_int8_t	data[3];
21867482Sbabkin	u_int8_t	next[3];
21967482Sbabkin	u_int8_t	write;
22067482Sbabkin	u_int8_t	xx[6];
22167482Sbabkin};
22267482Sbabkin
22367482Sbabkinstruct wds_req {
22467482Sbabkin	struct	   wds_cmd cmd;
22567482Sbabkin	union	   ccb *ccb;
22667482Sbabkin	enum {
22767482Sbabkin		WR_DONE = 0x01,
22867482Sbabkin		WR_SENSE = 0x02
22967482Sbabkin	} flags;
23067482Sbabkin	u_int8_t  *buf;		/* address of linear data buffer */
23167482Sbabkin	u_int32_t  mask;	/* mask of allocated fragments */
23267482Sbabkin	u_int8_t	ombn;
23367482Sbabkin	u_int8_t	id;	/* number of request */
23467482Sbabkin};
23567482Sbabkin
23667482Sbabkin#define WDSX_SCSICMD		0x00
23767482Sbabkin#define WDSX_OPEN_RCVBUF	0x80
23867482Sbabkin#define WDSX_RCV_CMD		0x81
23967482Sbabkin#define WDSX_RCV_DATA		0x82
24067482Sbabkin#define WDSX_RCV_DATASTAT	0x83
24167482Sbabkin#define WDSX_SND_DATA		0x84
24267482Sbabkin#define WDSX_SND_DATASTAT	0x85
24367482Sbabkin#define WDSX_SND_CMDSTAT	0x86
24467482Sbabkin#define WDSX_READINIT		0x88
24567482Sbabkin#define WDSX_READSCSIID		0x89
24667482Sbabkin#define WDSX_SETUNSOLIRQMASK	0x8a
24767482Sbabkin#define WDSX_GETUNSOLIRQMASK	0x8b
24867482Sbabkin#define WDSX_GETFIRMREV		0x8c
24967482Sbabkin#define WDSX_EXECDIAG		0x8d
25067482Sbabkin#define WDSX_SETEXECPARM	0x8e
25167482Sbabkin#define WDSX_GETEXECPARM	0x8f
25267482Sbabkin
25367482Sbabkinstruct wds_mb {
25467482Sbabkin	u_int8_t	stat;
25567482Sbabkin	u_int8_t	addr[3];
25667482Sbabkin};
25767482Sbabkin/* ICMB status value */
25867482Sbabkin#define ICMB_OK			0x01
25967482Sbabkin#define ICMB_OKERR		0x02
26067482Sbabkin#define ICMB_ETIME		0x04
26167482Sbabkin#define ICMB_ERESET		0x05
26267482Sbabkin#define ICMB_ETARCMD		0x06
26367482Sbabkin#define ICMB_ERESEL		0x80
26467482Sbabkin#define ICMB_ESEL		0x81
26567482Sbabkin#define ICMB_EABORT		0x82
26667482Sbabkin#define ICMB_ESRESET		0x83
26767482Sbabkin#define ICMB_EHRESET		0x84
26867482Sbabkin
26967482Sbabkinstruct wds_setup {
27067482Sbabkin	u_int8_t	cmd;
27167482Sbabkin	u_int8_t	scsi_id;
27267482Sbabkin	u_int8_t	buson_t;
27367482Sbabkin	u_int8_t	busoff_t;
27467482Sbabkin	u_int8_t	xx;
27567482Sbabkin	u_int8_t	mbaddr[3];
27667482Sbabkin	u_int8_t	nomb;
27767482Sbabkin	u_int8_t	nimb;
27867482Sbabkin};
27967482Sbabkin
28067482Sbabkin/* the code depends on equality of these parameters */
28167482Sbabkin#define MAXSIMUL	8
28267482Sbabkin#define WDS_NOMB	MAXSIMUL
28367482Sbabkin#define WDS_NIMB	MAXSIMUL
28467482Sbabkin
28567482Sbabkinstatic int	fragsiz;
28667482Sbabkinstatic int	nfrags;
28767482Sbabkin
28867482Sbabkin/* structure for data exchange with controller */
28967482Sbabkin
29067482Sbabkinstruct wdsdx {
29167482Sbabkin	struct wds_req	req[MAXSIMUL];
29267482Sbabkin	struct wds_mb	ombs[MAXSIMUL];
29367482Sbabkin	struct wds_mb	imbs[MAXSIMUL];
29467482Sbabkin	u_int8_t	data[BUFSIZ];
29567482Sbabkin};
29667482Sbabkin
29767482Sbabkin/* structure softc */
29867482Sbabkin
29967482Sbabkinstruct wds {
30067482Sbabkin	device_t	 dev;
30167482Sbabkin	int		 unit;
30267482Sbabkin	int		 addr;
30367482Sbabkin	int		 drq;
30467482Sbabkin	struct cam_sim	*sim;	/* SIM descriptor for this card */
30567482Sbabkin	struct cam_path	*path;	/* wildcard path for this card */
30667482Sbabkin	char		 want_wdsr;	/* resource shortage flag */
30767482Sbabkin	u_int32_t	 data_free;
30867482Sbabkin	u_int32_t	 wdsr_free;
30967482Sbabkin	struct wdsdx	*dx;
31067482Sbabkin	struct wdsdx	*dx_p; /* physical address */
31167482Sbabkin	struct resource	*port_r;
31267482Sbabkin	int		 port_rid;
31367482Sbabkin	struct resource	*drq_r;
31467482Sbabkin	int		 drq_rid;
31567482Sbabkin	struct resource *intr_r;
31667482Sbabkin	int		 intr_rid;
31767482Sbabkin	void		*intr_cookie;
31867482Sbabkin	bus_dma_tag_t	 bustag;
31967482Sbabkin	bus_dmamap_t	 busmap;
32067482Sbabkin};
32167482Sbabkin
32267482Sbabkin#define ccb_wdsr	spriv_ptr1	/* for wds request */
32367482Sbabkin
32467482Sbabkinstatic int      wds_probe(device_t dev);
32567482Sbabkinstatic int      wds_attach(device_t dev);
32667482Sbabkinstatic void     wds_intr(struct wds *wp);
32767482Sbabkin
32867482Sbabkinstatic void     wds_action(struct cam_sim * sim, union ccb * ccb);
32967482Sbabkinstatic void     wds_poll(struct cam_sim * sim);
33067482Sbabkin
33167482Sbabkinstatic int      wds_preinit(struct wds *wp);
33267482Sbabkinstatic int      wds_init(struct wds *wp);
33367482Sbabkin
33467482Sbabkinstatic void     wds_alloc_callback(void *arg, bus_dma_segment_t *seg,
33567482Sbabkin	 int nseg, int error);
33667482Sbabkinstatic void     wds_free_resources(struct wds *wp);
33767482Sbabkin
33867482Sbabkinstatic struct wds_req *wdsr_alloc(struct wds *wp);
33967482Sbabkin
34067482Sbabkinstatic void     wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio);
34167482Sbabkinstatic void     wdsr_ccb_done(struct wds *wp, struct wds_req *r,
34267482Sbabkin			      union ccb *ccb, u_int32_t status);
34367482Sbabkin
34467482Sbabkinstatic void     wds_done(struct wds *wp, struct wds_req *r, u_int8_t stat);
34567482Sbabkinstatic int      wds_runsense(struct wds *wp, struct wds_req *r);
34667482Sbabkinstatic int      wds_getvers(struct wds *wp);
34767482Sbabkin
34867482Sbabkinstatic int      wds_cmd(int base, u_int8_t * p, int l);
34967482Sbabkinstatic void     wds_wait(int reg, int mask, int val);
35067482Sbabkin
35167482Sbabkinstatic struct wds_req *cmdtovirt(struct wds *wp, u_int32_t phys);
35267482Sbabkin
35367482Sbabkinstatic u_int32_t frag_alloc(struct wds *wp, int size, u_int8_t **res,
35467482Sbabkin			    u_int32_t *maskp);
35567482Sbabkinstatic void     frag_free(struct wds *wp, u_int32_t mask);
35667482Sbabkin
35767482Sbabkinvoid            wds_print(void);
35867482Sbabkin
35967482Sbabkin#if WDS_ENABLE_SMALLOG==1
36067482Sbabkinstatic __inline void   smallog(char c);
36167482Sbabkinvoid 	wds_printsmallog(void);
36267482Sbabkin#endif /* SMALLOG */
36367482Sbabkin
36467482Sbabkin/* SCSI ID of the adapter itself */
36567482Sbabkin#ifndef WDS_HBA_ID
36667482Sbabkin#define WDS_HBA_ID 7
36767482Sbabkin#endif
36867482Sbabkin
36967482Sbabkin#if WDS_DEBUG == 2
37067482Sbabkin#define LOGLINESIZ	81
37167482Sbabkin#define NLOGLINES	300
37267482Sbabkin#define DBX	wds_nextlog(), LOGLINESIZ,
37367482Sbabkin#define DBG	snprintf
37467482Sbabkin
37567482Sbabkinstatic char     wds_log[NLOGLINES][LOGLINESIZ];
37667482Sbabkinstatic int      logwrite = 0, logread = 0;
37767482Sbabkinstatic char    *wds_nextlog(void);
37867482Sbabkinvoid            wds_printlog(void);
37967482Sbabkin
38067482Sbabkin#elif WDS_DEBUG != 0
38167482Sbabkin#define DBX
38267482Sbabkin#define DBG	printf
38367482Sbabkin#else
38467482Sbabkin#define DBX
38567482Sbabkin#define DBG	if(0) printf
38667482Sbabkin#endif
38767482Sbabkin
38867482Sbabkin/* the table of supported bus methods */
38967482Sbabkinstatic device_method_t wds_isa_methods[] = {
39067482Sbabkin	DEVMETHOD(device_probe,		wds_probe),
39167482Sbabkin	DEVMETHOD(device_attach,	wds_attach),
39267482Sbabkin	{ 0, 0 }
39367482Sbabkin};
39467482Sbabkin
39567482Sbabkinstatic driver_t wds_isa_driver = {
39667482Sbabkin	"wds",
39767482Sbabkin	wds_isa_methods,
39867482Sbabkin	sizeof(struct wds),
39967482Sbabkin};
40067482Sbabkin
40167482Sbabkinstatic devclass_t wds_devclass;
40267482Sbabkin
40367482SbabkinDRIVER_MODULE(wds, isa, wds_isa_driver, wds_devclass, 0, 0);
404165102SmjacobMODULE_DEPEND(wds, isa, 1, 1, 1);
405165102SmjacobMODULE_DEPEND(wds, cam, 1, 1, 1);
40667482Sbabkin
40767482Sbabkin#if WDS_ENABLE_SMALLOG==1
40867482Sbabkin#define SMALLOGSIZ	512
40967482Sbabkinstatic char	 wds_smallog[SMALLOGSIZ];
41067482Sbabkinstatic char	*wds_smallogp = wds_smallog;
41167482Sbabkinstatic char	 wds_smallogover = 0;
41267482Sbabkin
41367482Sbabkinstatic __inline void
41467482Sbabkinsmallog(char c)
41567482Sbabkin{
41667482Sbabkin	*wds_smallogp = c;
41767482Sbabkin	if (++wds_smallogp == &wds_smallog[SMALLOGSIZ]) {
41867482Sbabkin		wds_smallogp = wds_smallog;
41967482Sbabkin		wds_smallogover = 1;
42067482Sbabkin	}
42167482Sbabkin}
42267482Sbabkin
42367482Sbabkin#define smallog2(a, b)	(smallog(a), smallog(b))
42467482Sbabkin#define smallog3(a, b, c)	(smallog(a), smallog(b), smallog(c))
42567482Sbabkin#define smallog4(a, b, c, d)	(smallog(a),smallog(b),smallog(c),smallog(d))
42667482Sbabkin
42767482Sbabkinvoid
42867482Sbabkinwds_printsmallog(void)
42967482Sbabkin{
43067482Sbabkin	int	 i;
43167482Sbabkin	char	*p;
43267482Sbabkin
43367482Sbabkin	printf("wds: ");
43467482Sbabkin	p = wds_smallogover ? wds_smallogp : wds_smallog;
43567482Sbabkin	i = 0;
43667482Sbabkin	do {
43767482Sbabkin		printf("%c", *p);
43867482Sbabkin		if (++p == &wds_smallog[SMALLOGSIZ])
43967482Sbabkin			p = wds_smallog;
44067482Sbabkin		if (++i == 70) {
44167482Sbabkin			i = 0;
44267482Sbabkin			printf("\nwds: ");
44367482Sbabkin		}
44467482Sbabkin	} while (p != wds_smallogp);
44567482Sbabkin	printf("\n");
44667482Sbabkin}
44767482Sbabkin#else
44867482Sbabkin#define smallog(a)
44967482Sbabkin#define smallog2(a, b)
45067482Sbabkin#define smallog3(a, b, c)
45167482Sbabkin#define smallog4(a, b, c, d)
45267482Sbabkin#endif				/* SMALLOG */
45367482Sbabkin
45467482Sbabkinstatic int
45567482Sbabkinwds_probe(device_t dev)
45667482Sbabkin{
45767482Sbabkin	struct	wds *wp;
45867482Sbabkin	int	error = 0;
45967482Sbabkin	int	irq;
46067482Sbabkin
46167482Sbabkin	/* No pnp support */
46267482Sbabkin	if (isa_get_vendorid(dev))
46367482Sbabkin		return (ENXIO);
46467482Sbabkin
46567482Sbabkin	wp = (struct wds *) device_get_softc(dev);
46667482Sbabkin	wp->unit = device_get_unit(dev);
46767482Sbabkin	wp->dev = dev;
46867482Sbabkin
46967482Sbabkin	wp->addr = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/);
47067482Sbabkin	if (wp->addr == 0 || wp->addr <0x300
47167482Sbabkin	 || wp->addr > 0x3f8 || wp->addr & 0x7) {
47267482Sbabkin		device_printf(dev, "invalid port address 0x%x\n", wp->addr);
47367482Sbabkin		return (ENXIO);
47467482Sbabkin	}
47567482Sbabkin
47667482Sbabkin	if (bus_set_resource(dev, SYS_RES_IOPORT, 0, wp->addr, WDS_NPORTS) < 0)
47767482Sbabkin		return (ENXIO);
47867482Sbabkin
47967482Sbabkin	/* get the DRQ */
48067482Sbabkin	wp->drq = bus_get_resource_start(dev, SYS_RES_DRQ, 0 /*rid*/);
48167482Sbabkin	if (wp->drq < 5 || wp->drq > 7) {
48267482Sbabkin		device_printf(dev, "invalid DRQ %d\n", wp->drq);
48367482Sbabkin		return (ENXIO);
48467482Sbabkin	}
48567482Sbabkin
48667482Sbabkin	/* get the IRQ */
48767482Sbabkin	irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0 /*rid*/);
48867482Sbabkin	if (irq < 3) {
48967482Sbabkin		device_printf(dev, "invalid IRQ %d\n", irq);
49067482Sbabkin		return (ENXIO);
49167482Sbabkin	}
49267482Sbabkin
49367482Sbabkin	wp->port_rid = 0;
49467482Sbabkin	wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT,  &wp->port_rid,
49567482Sbabkin				        /*start*/ 0, /*end*/ ~0,
49667482Sbabkin					/*count*/ 0, RF_ACTIVE);
49767482Sbabkin	if (wp->port_r == NULL)
49867482Sbabkin		return (ENXIO);
49967482Sbabkin
50067482Sbabkin	error = wds_preinit(wp);
50167482Sbabkin
50267482Sbabkin	/*
50367482Sbabkin	 * We cannot hold resources between probe and
50467482Sbabkin	 * attach as we may never be attached.
50567482Sbabkin	 */
50667482Sbabkin	wds_free_resources(wp);
50767482Sbabkin
50867482Sbabkin	return (error);
50967482Sbabkin}
51067482Sbabkin
51167482Sbabkinstatic int
51267482Sbabkinwds_attach(device_t dev)
51367482Sbabkin{
51467482Sbabkin	struct	wds *wp;
51567482Sbabkin	struct	cam_devq *devq;
51667482Sbabkin	struct	cam_sim *sim;
51767482Sbabkin	struct	cam_path *pathp;
51867482Sbabkin	int	i;
51967482Sbabkin	int	error = 0;
52067482Sbabkin
52167482Sbabkin	wp = (struct wds *)device_get_softc(dev);
52267482Sbabkin
52367482Sbabkin	wp->port_rid = 0;
52467482Sbabkin	wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT,  &wp->port_rid,
52567482Sbabkin					/*start*/ 0, /*end*/ ~0,
52667482Sbabkin					/*count*/ 0, RF_ACTIVE);
52767482Sbabkin	if (wp->port_r == NULL)
52867482Sbabkin		return (ENXIO);
52967482Sbabkin
53067482Sbabkin	/* We must now release resources on error. */
53167482Sbabkin
53267482Sbabkin	wp->drq_rid = 0;
53367482Sbabkin	wp->drq_r = bus_alloc_resource(dev, SYS_RES_DRQ,  &wp->drq_rid,
53467482Sbabkin				       /*start*/ 0, /*end*/ ~0,
53567482Sbabkin				       /*count*/ 0, RF_ACTIVE);
53667482Sbabkin	if (wp->drq_r == NULL)
53767482Sbabkin		goto bad;
53867482Sbabkin
53967482Sbabkin	wp->intr_rid = 0;
54067482Sbabkin	wp->intr_r = bus_alloc_resource(dev, SYS_RES_IRQ,  &wp->intr_rid,
54167482Sbabkin					/*start*/ 0, /*end*/ ~0,
54267482Sbabkin					/*count*/ 0, RF_ACTIVE);
54367482Sbabkin	if (wp->intr_r == NULL)
54467482Sbabkin		goto bad;
54573280Smarkm	error = bus_setup_intr(dev, wp->intr_r, INTR_TYPE_CAM | INTR_ENTROPY,
546166914Simp			       NULL, (driver_intr_t *)wds_intr, (void *)wp,
54767482Sbabkin			       &wp->intr_cookie);
54867482Sbabkin	if (error)
54967482Sbabkin		goto bad;
55067482Sbabkin
55167482Sbabkin	/* now create the memory buffer */
552233024Sscottl	error = bus_dma_tag_create(bus_get_dma_tag(dev), /*alignment*/4,
55367482Sbabkin				   /*boundary*/0,
55467482Sbabkin				   /*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
55567482Sbabkin				   /*highaddr*/ BUS_SPACE_MAXADDR,
55667482Sbabkin				   /*filter*/ NULL, /*filterarg*/ NULL,
55767482Sbabkin				   /*maxsize*/ sizeof(* wp->dx),
55867482Sbabkin				   /*nsegments*/ 1,
55967482Sbabkin				   /*maxsegsz*/ sizeof(* wp->dx), /*flags*/ 0,
560117126Sscottl				   /*lockfunc*/busdma_lock_mutex,
561117126Sscottl				   /*lockarg*/&Giant,
56267482Sbabkin				   &wp->bustag);
56367482Sbabkin	if (error)
56467482Sbabkin		goto bad;
56567482Sbabkin
56667482Sbabkin	error = bus_dmamem_alloc(wp->bustag, (void **)&wp->dx,
56767482Sbabkin				 /*flags*/ 0, &wp->busmap);
56867482Sbabkin	if (error)
56967482Sbabkin		goto bad;
57067482Sbabkin
57167482Sbabkin	bus_dmamap_load(wp->bustag, wp->busmap, (void *)wp->dx,
57267482Sbabkin			sizeof(* wp->dx), wds_alloc_callback,
57367482Sbabkin			(void *)&wp->dx_p, /*flags*/0);
57467482Sbabkin
57567482Sbabkin	/* initialize the wds_req structures on this unit */
57667482Sbabkin	for(i=0; i<MAXSIMUL; i++)  {
57767482Sbabkin		wp->dx->req[i].id = i;
57867482Sbabkin		wp->wdsr_free |= 1<<i;
57967482Sbabkin	}
58067482Sbabkin
58167482Sbabkin	/* initialize the memory buffer allocation for this unit */
58267482Sbabkin	if (BUFSIZ / FRAGSIZ > 32) {
58367482Sbabkin		fragsiz = (BUFSIZ / 32) & ~0x01; /* keep it word-aligned */
58467482Sbabkin		device_printf(dev, "data buffer fragment size too small.  "
58567482Sbabkin			      "BUFSIZE / FRAGSIZE must be <= 32\n");
58667482Sbabkin	} else
58767482Sbabkin		fragsiz = FRAGSIZ & ~0x01; /* keep it word-aligned */
58867482Sbabkin
58967482Sbabkin	wp->data_free = 0;
59067482Sbabkin	nfrags = 0;
59167482Sbabkin	for (i = fragsiz; i <= BUFSIZ; i += fragsiz) {
59267482Sbabkin		nfrags++;
59367482Sbabkin		wp->data_free = (wp->data_free << 1) | 1;
59467482Sbabkin	}
59567482Sbabkin
59667482Sbabkin	/* complete the hardware initialization */
59767482Sbabkin	if (wds_init(wp) != 0)
59867482Sbabkin		goto bad;
59967482Sbabkin
60067482Sbabkin	if (wds_getvers(wp) == -1)
60167482Sbabkin		device_printf(dev, "getvers failed\n");
60267482Sbabkin	device_printf(dev, "using %d bytes / %d frags for dma buffer\n",
60367482Sbabkin		      BUFSIZ, nfrags);
60467482Sbabkin
60567482Sbabkin	devq = cam_simq_alloc(MAXSIMUL);
60667482Sbabkin	if (devq == NULL)
60767482Sbabkin		goto bad;
60867482Sbabkin
60967482Sbabkin	sim = cam_sim_alloc(wds_action, wds_poll, "wds", (void *) wp,
610168752Sscottl			    wp->unit, &Giant, 1, 1, devq);
61167482Sbabkin	if (sim == NULL) {
61267482Sbabkin		cam_simq_free(devq);
61367482Sbabkin		goto bad;
61467482Sbabkin	}
61567482Sbabkin	wp->sim = sim;
61667482Sbabkin
617170872Sscottl	if (xpt_bus_register(sim, dev, 0) != CAM_SUCCESS) {
61867482Sbabkin		cam_sim_free(sim, /* free_devq */ TRUE);
61967482Sbabkin		goto bad;
62067482Sbabkin	}
62167482Sbabkin	if (xpt_create_path(&pathp, /* periph */ NULL,
62267482Sbabkin			    cam_sim_path(sim), CAM_TARGET_WILDCARD,
62367482Sbabkin			    CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
62467482Sbabkin		xpt_bus_deregister(cam_sim_path(sim));
62567482Sbabkin		cam_sim_free(sim, /* free_devq */ TRUE);
62667482Sbabkin		goto bad;
62767482Sbabkin	}
62867482Sbabkin	wp->path = pathp;
62967482Sbabkin
63067482Sbabkin	return (0);
63167482Sbabkin
63267482Sbabkinbad:
63367482Sbabkin	wds_free_resources(wp);
63467482Sbabkin	if (error)
63567482Sbabkin		return (error);
63667482Sbabkin	else /* exact error is unknown */
63767482Sbabkin		return (ENXIO);
63867482Sbabkin}
63967482Sbabkin
64067482Sbabkin/* callback to save the physical address */
64167482Sbabkinstatic void
64267482Sbabkinwds_alloc_callback(void *arg, bus_dma_segment_t *seg,  int nseg, int error)
64367482Sbabkin{
64467482Sbabkin	*(bus_addr_t *)arg = seg[0].ds_addr;
64567482Sbabkin}
64667482Sbabkin
64767482Sbabkinstatic void
64867482Sbabkinwds_free_resources(struct wds *wp)
64967482Sbabkin{
65067482Sbabkin	/* check every resource and free if not zero */
65167482Sbabkin
65267482Sbabkin	/* interrupt handler */
65367482Sbabkin	if (wp->intr_r) {
65467482Sbabkin		bus_teardown_intr(wp->dev, wp->intr_r, wp->intr_cookie);
65567482Sbabkin		bus_release_resource(wp->dev, SYS_RES_IRQ, wp->intr_rid,
65667482Sbabkin				     wp->intr_r);
65767482Sbabkin		wp->intr_r = 0;
65867482Sbabkin	}
65967482Sbabkin
66067482Sbabkin	/* all kinds of memory maps we could have allocated */
66167482Sbabkin	if (wp->dx_p) {
66267482Sbabkin		bus_dmamap_unload(wp->bustag, wp->busmap);
66367482Sbabkin		wp->dx_p = 0;
66467482Sbabkin	}
66567482Sbabkin	if (wp->dx) { /* wp->busmap may be legitimately equal to 0 */
66667482Sbabkin		/* the map will also be freed */
66767482Sbabkin		bus_dmamem_free(wp->bustag, wp->dx, wp->busmap);
66867482Sbabkin		wp->dx = 0;
66967482Sbabkin	}
67067482Sbabkin	if (wp->bustag) {
67167482Sbabkin		bus_dma_tag_destroy(wp->bustag);
67267482Sbabkin		wp->bustag = 0;
67367482Sbabkin	}
67467482Sbabkin	/* release all the bus resources */
67567482Sbabkin	if (wp->drq_r) {
67667482Sbabkin		bus_release_resource(wp->dev, SYS_RES_DRQ,
67767482Sbabkin				     wp->drq_rid, wp->drq_r);
67867482Sbabkin		wp->drq_r = 0;
67967482Sbabkin	}
68067482Sbabkin	if (wp->port_r) {
68167482Sbabkin		bus_release_resource(wp->dev, SYS_RES_IOPORT,
68267482Sbabkin				     wp->port_rid, wp->port_r);
68367482Sbabkin		wp->port_r = 0;
68467482Sbabkin	}
68567482Sbabkin}
68667482Sbabkin
68767482Sbabkin/* allocate contiguous fragments from the buffer */
68867482Sbabkinstatic u_int32_t
68967482Sbabkinfrag_alloc(struct wds *wp, int size, u_int8_t **res, u_int32_t *maskp)
69067482Sbabkin{
69167482Sbabkin	int	i;
69267482Sbabkin	u_int32_t	mask;
69367482Sbabkin	u_int32_t	free;
69467482Sbabkin
69567482Sbabkin	if (size > fragsiz * nfrags)
69667482Sbabkin		return (CAM_REQ_TOO_BIG);
69767482Sbabkin
69867482Sbabkin	mask = 1;		/* always allocate at least 1 fragment */
69967482Sbabkin	for (i = fragsiz; i < size; i += fragsiz)
70067482Sbabkin		mask = (mask << 1) | 1;
70167482Sbabkin
70267482Sbabkin	free = wp->data_free;
70367482Sbabkin	if(free != 0) {
70467482Sbabkin		i = ffs(free)-1; /* ffs counts bits from 1 */
70567482Sbabkin		for (mask <<= i; i < nfrags; i++) {
70667482Sbabkin			if ((free & mask) == mask) {
70767482Sbabkin				wp->data_free &= ~mask;	/* mark frags as busy */
70867482Sbabkin				*maskp = mask;
70967482Sbabkin				*res = &wp->dx->data[fragsiz * i];
71067482Sbabkin				DBG(DBX "wds%d: allocated buffer mask=0x%x\n",
71167482Sbabkin					wp->unit, mask);
71267482Sbabkin				return (CAM_REQ_CMP);
71367482Sbabkin			}
71467482Sbabkin			if (mask & 0x80000000)
71567482Sbabkin				break;
71667482Sbabkin
71767482Sbabkin			mask <<= 1;
71867482Sbabkin		}
71967482Sbabkin	}
72067482Sbabkin	return (CAM_REQUEUE_REQ);	/* no free memory now, try later */
72167482Sbabkin}
72267482Sbabkin
72367482Sbabkinstatic void
72467482Sbabkinfrag_free(struct wds *wp, u_int32_t mask)
72567482Sbabkin{
72667482Sbabkin	wp->data_free |= mask;	/* mark frags as free */
72767482Sbabkin	DBG(DBX "wds%d: freed buffer mask=0x%x\n", wp->unit, mask);
72867482Sbabkin}
72967482Sbabkin
73067482Sbabkinstatic struct wds_req *
73167482Sbabkinwdsr_alloc(struct wds *wp)
73267482Sbabkin{
73367482Sbabkin	struct	wds_req *r;
73467482Sbabkin	int	x;
73567482Sbabkin	int	i;
73667482Sbabkin
73767482Sbabkin	r = NULL;
73867482Sbabkin	x = splcam();
73967482Sbabkin
74067482Sbabkin	/* anyway most of the time only 1 or 2 commands will
74167482Sbabkin	 * be active because SCSI disconnect is not supported
74267482Sbabkin	 * by hardware, so the search should be fast enough
74367482Sbabkin	 */
74467482Sbabkin	i = ffs(wp->wdsr_free) - 1;
74567482Sbabkin	if(i < 0) {
74667482Sbabkin		splx(x);
74767482Sbabkin		return (NULL);
74867482Sbabkin	}
74967482Sbabkin	wp->wdsr_free &= ~ (1<<i);
75067482Sbabkin	r = &wp->dx->req[i];
75167482Sbabkin	r->flags = 0;	/* reset all flags */
75267482Sbabkin	r->ombn = i;		/* luckily we have one omb per wdsr */
75367482Sbabkin	wp->dx->ombs[i].stat = 1;
75467482Sbabkin
75567482Sbabkin	r->mask = 0;
75667482Sbabkin	splx(x);
75767482Sbabkin	smallog3('r', i + '0', r->ombn + '0');
75867482Sbabkin	return (r);
75967482Sbabkin}
76067482Sbabkin
76167482Sbabkinstatic void
76267482Sbabkinwds_intr(struct wds *wp)
76367482Sbabkin{
76467482Sbabkin	struct	 wds_req *rp;
76567482Sbabkin	struct	 wds_mb *in;
76667482Sbabkin	u_int8_t stat;
76767482Sbabkin	u_int8_t c;
76867482Sbabkin	int	 addr = wp->addr;
76967482Sbabkin
77067482Sbabkin	DBG(DBX "wds%d: interrupt [\n", wp->unit);
77167482Sbabkin	smallog('[');
77267482Sbabkin
77367482Sbabkin	if (inb(addr + WDS_STAT) & WDS_IRQ) {
77467482Sbabkin		c = inb(addr + WDS_IRQSTAT);
77567482Sbabkin		if ((c & WDSI_MASK) == WDSI_MSVC) {
77667482Sbabkin			c = c & ~WDSI_MASK;
77767482Sbabkin			in = &wp->dx->imbs[c];
77867482Sbabkin
77967482Sbabkin			rp = cmdtovirt(wp, scsi_3btoul(in->addr));
78067482Sbabkin			stat = in->stat;
78167482Sbabkin
78267482Sbabkin			if (rp != NULL)
78367482Sbabkin				wds_done(wp, rp, stat);
78467482Sbabkin			else
78567482Sbabkin				device_printf(wp->dev,
78667482Sbabkin					      "got weird command address %p"
78767482Sbabkin					      "from controller\n", rp);
78867482Sbabkin
78967482Sbabkin			in->stat = 0;
79067482Sbabkin		} else
79167482Sbabkin			device_printf(wp->dev,
79267482Sbabkin				      "weird interrupt, irqstat=0x%x\n", c);
79367482Sbabkin		outb(addr + WDS_IRQACK, 0);
79467482Sbabkin	} else {
79567482Sbabkin		smallog('?');
79667482Sbabkin	}
79767482Sbabkin	smallog(']');
79867482Sbabkin	DBG(DBX "wds%d: ]\n", wp->unit);
79967482Sbabkin}
80067482Sbabkin
80167482Sbabkinstatic void
80267482Sbabkinwds_done(struct wds *wp, struct wds_req *r, u_int8_t stat)
80367482Sbabkin{
80467482Sbabkin	struct	ccb_hdr *ccb_h;
80567482Sbabkin	struct	ccb_scsiio *csio;
80667482Sbabkin	int	status;
80767482Sbabkin
80867482Sbabkin	smallog('d');
80967482Sbabkin
81067482Sbabkin	if (r->flags & WR_DONE) {
81167482Sbabkin		device_printf(wp->dev,
81267482Sbabkin				"request %d reported done twice\n", r->id);
81367482Sbabkin		smallog2('x', r->id + '0');
81467482Sbabkin		return;
81567482Sbabkin	}
81667482Sbabkin
81767482Sbabkin	smallog(r->id + '0');
81867482Sbabkin	ccb_h = &r->ccb->ccb_h;
81967482Sbabkin	csio = &r->ccb->csio;
82067482Sbabkin	status = CAM_REQ_CMP_ERR;
82167482Sbabkin
82267482Sbabkin	DBG(DBX "wds%d: %s stat=0x%x c->stat=0x%x c->venderr=0x%x\n", wp->unit,
82367482Sbabkin	    r->flags & WR_SENSE ? "(sense)" : "",
82467482Sbabkin		stat, r->cmd.stat, r->cmd.venderr);
82567482Sbabkin
82667482Sbabkin	if (r->flags & WR_SENSE) {
82767482Sbabkin		if (stat == ICMB_OK || (stat == ICMB_OKERR && r->cmd.stat == 0)) {
82867482Sbabkin			DBG(DBX "wds%d: sense 0x%x\n", wp->unit, r->buf[0]);
82967482Sbabkin			/* it has the same size now but for future */
83067482Sbabkin			bcopy(r->buf, &csio->sense_data,
83167482Sbabkin			      sizeof(struct scsi_sense_data) > csio->sense_len ?
83267482Sbabkin			      csio->sense_len : sizeof(struct scsi_sense_data));
83367482Sbabkin			if (sizeof(struct scsi_sense_data) >= csio->sense_len)
83467482Sbabkin				csio->sense_resid = 0;
83567482Sbabkin			else
83667482Sbabkin				csio->sense_resid =
83767482Sbabkin					csio->sense_len
83867482Sbabkin				      - sizeof(struct scsi_sense_data);
83967482Sbabkin			status = CAM_AUTOSNS_VALID | CAM_SCSI_STATUS_ERROR;
84067482Sbabkin		} else {
84167482Sbabkin			status = CAM_AUTOSENSE_FAIL;
84267482Sbabkin		}
84367482Sbabkin	} else {
84467482Sbabkin		switch (stat) {
84567482Sbabkin		case ICMB_OK:
84667482Sbabkin			if (ccb_h) {
84767482Sbabkin				csio->resid = 0;
84867482Sbabkin				csio->scsi_status = r->cmd.stat;
84967482Sbabkin				status = CAM_REQ_CMP;
85067482Sbabkin			}
85167482Sbabkin			break;
85267482Sbabkin		case ICMB_OKERR:
85367482Sbabkin			if (ccb_h) {
85467482Sbabkin				csio->scsi_status = r->cmd.stat;
85567482Sbabkin				if (r->cmd.stat) {
85667482Sbabkin					if (ccb_h->flags & CAM_DIS_AUTOSENSE)
85767482Sbabkin						status = CAM_SCSI_STATUS_ERROR;
85867482Sbabkin					else {
85967482Sbabkin						if ( wds_runsense(wp, r) == CAM_REQ_CMP )
86067482Sbabkin							return;
86167482Sbabkin						/* in case of error continue with freeing of CCB */
86267482Sbabkin					}
86367482Sbabkin				} else {
86467482Sbabkin					csio->resid = 0;
86567482Sbabkin					status = CAM_REQ_CMP;
86667482Sbabkin				}
86767482Sbabkin			}
86867482Sbabkin			break;
86967482Sbabkin		case ICMB_ETIME:
87067482Sbabkin			if (ccb_h)
87167482Sbabkin				status = CAM_SEL_TIMEOUT;
87267482Sbabkin			break;
87367482Sbabkin		case ICMB_ERESET:
87467482Sbabkin		case ICMB_ETARCMD:
87567482Sbabkin		case ICMB_ERESEL:
87667482Sbabkin		case ICMB_ESEL:
87767482Sbabkin		case ICMB_EABORT:
87867482Sbabkin		case ICMB_ESRESET:
87967482Sbabkin		case ICMB_EHRESET:
88067482Sbabkin			if (ccb_h)
88167482Sbabkin				status = CAM_REQ_CMP_ERR;
88267482Sbabkin			break;
88367482Sbabkin		}
88467482Sbabkin
88567482Sbabkin		if (ccb_h && (ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN) {
88667482Sbabkin			/* we accept only virtual addresses in wds_action() */
88767482Sbabkin			bcopy(r->buf, csio->data_ptr, csio->dxfer_len);
88867482Sbabkin		}
88967482Sbabkin	}
89067482Sbabkin
89167482Sbabkin	r->flags |= WR_DONE;
89267482Sbabkin	wp->dx->ombs[r->ombn].stat = 0;
89367482Sbabkin
89467482Sbabkin	if (ccb_h) {
89567482Sbabkin		wdsr_ccb_done(wp, r, r->ccb, status);
89667482Sbabkin		smallog3('-', ccb_h->target_id + '0', ccb_h->target_lun + '0');
89767482Sbabkin	} else {
89867482Sbabkin		frag_free(wp, r->mask);
89967482Sbabkin		if (wp->want_wdsr) {
90067482Sbabkin			wp->want_wdsr = 0;
90167482Sbabkin			xpt_release_simq(wp->sim, /* run queue */ 1);
90267482Sbabkin		}
90367482Sbabkin		wp->wdsr_free |= (1 << r->id);
90467482Sbabkin	}
90567482Sbabkin
906106592Sjhb	DBG(DBX "wds%d: request %p done\n", wp->unit, r);
90767482Sbabkin}
90867482Sbabkin
90967482Sbabkin/* command returned bad status, request sense */
91067482Sbabkin
91167482Sbabkinstatic int
91267482Sbabkinwds_runsense(struct wds *wp, struct wds_req *r)
91367482Sbabkin{
91467482Sbabkin	u_int8_t          c;
91567482Sbabkin	struct	ccb_hdr *ccb_h;
91667482Sbabkin
91767482Sbabkin	ccb_h = &r->ccb->ccb_h;
91867482Sbabkin
91967482Sbabkin	r->flags |= WR_SENSE;
92067482Sbabkin	scsi_ulto3b(WDSTOPHYS(wp, &r->cmd),
92167482Sbabkin	 wp->dx->ombs[r->ombn].addr);
92267482Sbabkin	bzero(&r->cmd, sizeof r->cmd);
92367482Sbabkin	r->cmd.cmd = WDSX_SCSICMD;
92467482Sbabkin	r->cmd.targ = (ccb_h->target_id << 5) |
92567482Sbabkin		ccb_h->target_lun;
92667482Sbabkin
92767482Sbabkin	scsi_ulto3b(0, r->cmd.next);
92867482Sbabkin
92967482Sbabkin	r->cmd.scb[0] = REQUEST_SENSE;
93067482Sbabkin	r->cmd.scb[1] = ccb_h->target_lun << 5;
93167482Sbabkin	r->cmd.scb[4] = sizeof(struct scsi_sense_data);
93267482Sbabkin	r->cmd.scb[5] = 0;
93367482Sbabkin	scsi_ulto3b(WDSTOPHYS(wp, r->buf), r->cmd.data);
93467482Sbabkin	scsi_ulto3b(sizeof(struct scsi_sense_data), r->cmd.len);
93567482Sbabkin	r->cmd.write = 0x80;
93667482Sbabkin
93767482Sbabkin	outb(wp->addr + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
93867482Sbabkin
93967482Sbabkin	wp->dx->ombs[r->ombn].stat = 1;
94067482Sbabkin	c = WDSC_MSTART(r->ombn);
94167482Sbabkin
94267482Sbabkin	if (wds_cmd(wp->addr, &c, sizeof c) != 0) {
94367482Sbabkin		device_printf(wp->dev, "unable to start outgoing sense mbox\n");
94467482Sbabkin		wp->dx->ombs[r->ombn].stat = 0;
94567482Sbabkin		wdsr_ccb_done(wp, r, r->ccb, CAM_AUTOSENSE_FAIL);
94667482Sbabkin		return CAM_AUTOSENSE_FAIL;
94767482Sbabkin	} else {
948106592Sjhb		DBG(DBX "wds%d: enqueued status cmd 0x%x, r=%p\n",
949106592Sjhb			wp->unit, r->cmd.scb[0] & 0xFF, r);
95067482Sbabkin		/* don't free CCB yet */
95167482Sbabkin		smallog3('*', ccb_h->target_id + '0',
95267482Sbabkin			 ccb_h->target_lun + '0');
95367482Sbabkin		return CAM_REQ_CMP;
95467482Sbabkin	}
95567482Sbabkin}
95667482Sbabkin
95767482Sbabkinstatic int
95867482Sbabkinwds_getvers(struct wds *wp)
95967482Sbabkin{
96067482Sbabkin	struct	 wds_req *r;
96167482Sbabkin	int	 base;
96267482Sbabkin	u_int8_t c;
96367482Sbabkin	int	 i;
96467482Sbabkin
96567482Sbabkin	base = wp->addr;
96667482Sbabkin
96767482Sbabkin	r = wdsr_alloc(wp);
96867482Sbabkin	if (!r) {
96967482Sbabkin		device_printf(wp->dev, "no request slot available!\n");
97067482Sbabkin		return (-1);
97167482Sbabkin	}
97267482Sbabkin	r->flags &= ~WR_DONE;
97367482Sbabkin
97467482Sbabkin	r->ccb = NULL;
97567482Sbabkin
97667482Sbabkin	scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr);
97767482Sbabkin
97867482Sbabkin	bzero(&r->cmd, sizeof r->cmd);
97967482Sbabkin	r->cmd.cmd = WDSX_GETFIRMREV;
98067482Sbabkin
98167482Sbabkin	outb(base + WDS_HCR, WDSH_DRQEN);
98267482Sbabkin
98367482Sbabkin	c = WDSC_MSTART(r->ombn);
98467482Sbabkin	if (wds_cmd(base, (u_int8_t *) & c, sizeof c)) {
98567482Sbabkin		device_printf(wp->dev, "version request failed\n");
98667482Sbabkin		wp->wdsr_free |= (1 << r->id);
98767482Sbabkin		wp->dx->ombs[r->ombn].stat = 0;
98867482Sbabkin		return (-1);
98967482Sbabkin	}
99067482Sbabkin	while (1) {
99167482Sbabkin		i = 0;
99267482Sbabkin		while ((inb(base + WDS_STAT) & WDS_IRQ) == 0) {
99367482Sbabkin			DELAY(9000);
99467482Sbabkin			if (++i == 100) {
99567482Sbabkin				device_printf(wp->dev, "getvers timeout\n");
99667482Sbabkin				return (-1);
99767482Sbabkin			}
99867482Sbabkin		}
99967482Sbabkin		wds_intr(wp);
100067482Sbabkin		if (r->flags & WR_DONE) {
100167482Sbabkin			device_printf(wp->dev, "firmware version %d.%02d\n",
100267482Sbabkin			       r->cmd.targ, r->cmd.scb[0]);
100367482Sbabkin			wp->wdsr_free |= (1 << r->id);
100467482Sbabkin			return (0);
100567482Sbabkin		}
100667482Sbabkin	}
100767482Sbabkin}
100867482Sbabkin
100967482Sbabkinstatic void
101067482Sbabkinwdsr_ccb_done(struct wds *wp, struct wds_req *r,
101167482Sbabkin	      union ccb *ccb, u_int32_t status)
101267482Sbabkin{
101367482Sbabkin	ccb->ccb_h.ccb_wdsr = 0;
101467482Sbabkin
101567482Sbabkin	if (r != NULL) {
101667482Sbabkin		/* To implement timeouts we would need to know how to abort the
101767482Sbabkin		 * command on controller, and this is a great mystery.
101867482Sbabkin		 * So for now we just pass the responsibility for timeouts
101967482Sbabkin		 * to the controlles itself, it does that reasonably good.
102067482Sbabkin		 */
102167482Sbabkin		/* untimeout(_timeout, (caddr_t) hcb, ccb->ccb_h.timeout_ch); */
102267482Sbabkin		/* we're about to free a hcb, so the shortage has ended */
102367482Sbabkin		frag_free(wp, r->mask);
102467482Sbabkin		if (wp->want_wdsr && status != CAM_REQUEUE_REQ) {
102567482Sbabkin			wp->want_wdsr = 0;
102667482Sbabkin			status |= CAM_RELEASE_SIMQ;
102767482Sbabkin			smallog('R');
102867482Sbabkin		}
102967482Sbabkin		wp->wdsr_free |= (1 << r->id);
103067482Sbabkin	}
103167482Sbabkin	ccb->ccb_h.status =
103267482Sbabkin	    status | (ccb->ccb_h.status & ~(CAM_STATUS_MASK | CAM_SIM_QUEUED));
103367482Sbabkin	xpt_done(ccb);
103467482Sbabkin}
103567482Sbabkin
103667482Sbabkinstatic void
103767482Sbabkinwds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio)
103867482Sbabkin{
103967482Sbabkin	int	 unit = cam_sim_unit(sim);
104067482Sbabkin	struct	 wds *wp;
104167482Sbabkin	struct	 ccb_hdr *ccb_h;
104267482Sbabkin	struct	 wds_req *r;
104367482Sbabkin	int	 base;
104467482Sbabkin	u_int8_t c;
104567482Sbabkin	int	 error;
104667482Sbabkin	int	 n;
104767482Sbabkin
104867482Sbabkin	wp = (struct wds *)cam_sim_softc(sim);
104967482Sbabkin	ccb_h = &csio->ccb_h;
105067482Sbabkin
105167482Sbabkin	DBG(DBX "wds%d: cmd TARG=%d LUN=%d\n", unit, ccb_h->target_id,
105267482Sbabkin	    ccb_h->target_lun);
105367482Sbabkin
105467482Sbabkin	if (ccb_h->target_id > 7 || ccb_h->target_id == WDS_HBA_ID) {
105567482Sbabkin		ccb_h->status = CAM_TID_INVALID;
105667482Sbabkin		xpt_done((union ccb *) csio);
105767482Sbabkin		return;
105867482Sbabkin	}
105967482Sbabkin	if (ccb_h->target_lun > 7) {
106067482Sbabkin		ccb_h->status = CAM_LUN_INVALID;
106167482Sbabkin		xpt_done((union ccb *) csio);
106267482Sbabkin		return;
106367482Sbabkin	}
106467482Sbabkin	if (csio->dxfer_len > BUFSIZ) {
106567482Sbabkin		ccb_h->status = CAM_REQ_TOO_BIG;
106667482Sbabkin		xpt_done((union ccb *) csio);
106767482Sbabkin		return;
106867482Sbabkin	}
1069251874Sscottl	if ((ccb_h->flags & CAM_DATA_MASK) != CAM_DATA_VADDR) {
107067482Sbabkin		/* don't support these */
107167482Sbabkin		ccb_h->status = CAM_REQ_INVALID;
107267482Sbabkin		xpt_done((union ccb *) csio);
107367482Sbabkin		return;
107467482Sbabkin	}
107567482Sbabkin	base = wp->addr;
107667482Sbabkin
107767482Sbabkin	/*
107867482Sbabkin	 * this check is mostly for debugging purposes,
107967482Sbabkin	 * "can't happen" normally.
108067482Sbabkin	 */
108167482Sbabkin	if(wp->want_wdsr) {
108267482Sbabkin		DBG(DBX "wds%d: someone already waits for buffer\n", unit);
108367482Sbabkin		smallog('b');
108467482Sbabkin		n = xpt_freeze_simq(sim, /* count */ 1);
108567482Sbabkin		smallog('0'+n);
108667482Sbabkin		ccb_h->status = CAM_REQUEUE_REQ;
108767482Sbabkin		xpt_done((union ccb *) csio);
108867482Sbabkin		return;
108967482Sbabkin	}
109067482Sbabkin
109167482Sbabkin	r = wdsr_alloc(wp);
109267482Sbabkin	if (r == NULL) {
109367482Sbabkin		device_printf(wp->dev, "no request slot available!\n");
109467482Sbabkin		wp->want_wdsr = 1;
109567482Sbabkin		n = xpt_freeze_simq(sim, /* count */ 1);
109667482Sbabkin		smallog2('f', '0'+n);
109767482Sbabkin		ccb_h->status = CAM_REQUEUE_REQ;
109867482Sbabkin		xpt_done((union ccb *) csio);
109967482Sbabkin		return;
110067482Sbabkin	}
110167482Sbabkin
110267482Sbabkin	ccb_h->ccb_wdsr = (void *) r;
110367482Sbabkin	r->ccb = (union ccb *) csio;
110467482Sbabkin
110567482Sbabkin	switch (error = frag_alloc(wp, csio->dxfer_len, &r->buf, &r->mask)) {
110667482Sbabkin	case CAM_REQ_CMP:
110767482Sbabkin		break;
110867482Sbabkin	case CAM_REQUEUE_REQ:
110967482Sbabkin		DBG(DBX "wds%d: no data buffer available\n", unit);
111067482Sbabkin		wp->want_wdsr = 1;
111167482Sbabkin		n = xpt_freeze_simq(sim, /* count */ 1);
111267482Sbabkin		smallog2('f', '0'+n);
111367482Sbabkin		wdsr_ccb_done(wp, r, r->ccb, CAM_REQUEUE_REQ);
111467482Sbabkin		return;
111567482Sbabkin	default:
111667482Sbabkin		DBG(DBX "wds%d: request is too big\n", unit);
111767482Sbabkin		wdsr_ccb_done(wp, r, r->ccb, error);
111867482Sbabkin		break;
111967482Sbabkin	}
112067482Sbabkin
112167482Sbabkin	ccb_h->status |= CAM_SIM_QUEUED;
112267482Sbabkin	r->flags &= ~WR_DONE;
112367482Sbabkin
112467482Sbabkin	scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr);
112567482Sbabkin
112667482Sbabkin	bzero(&r->cmd, sizeof r->cmd);
112767482Sbabkin	r->cmd.cmd = WDSX_SCSICMD;
112867482Sbabkin	r->cmd.targ = (ccb_h->target_id << 5) | ccb_h->target_lun;
112967482Sbabkin
113067482Sbabkin	if (ccb_h->flags & CAM_CDB_POINTER)
113167482Sbabkin		bcopy(csio->cdb_io.cdb_ptr, &r->cmd.scb,
113267482Sbabkin		      csio->cdb_len < 12 ? csio->cdb_len : 12);
113367482Sbabkin	else
113467482Sbabkin		bcopy(csio->cdb_io.cdb_bytes, &r->cmd.scb,
113567482Sbabkin		      csio->cdb_len < 12 ? csio->cdb_len : 12);
113667482Sbabkin
113767482Sbabkin	scsi_ulto3b(csio->dxfer_len, r->cmd.len);
113867482Sbabkin
113967482Sbabkin	if (csio->dxfer_len > 0
114067482Sbabkin	 && (ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_OUT) {
114167482Sbabkin		/* we already rejected physical or scattered addresses */
114267482Sbabkin		bcopy(csio->data_ptr, r->buf, csio->dxfer_len);
114367482Sbabkin	}
114467482Sbabkin	scsi_ulto3b(csio->dxfer_len ? WDSTOPHYS(wp, r->buf) : 0, r->cmd.data);
114567482Sbabkin
114667482Sbabkin	if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN)
114767482Sbabkin		r->cmd.write = 0x80;
114867482Sbabkin	else
114967482Sbabkin		r->cmd.write = 0x00;
115067482Sbabkin
115167482Sbabkin	scsi_ulto3b(0, r->cmd.next);
115267482Sbabkin
115367482Sbabkin	outb(base + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
115467482Sbabkin
115567482Sbabkin	c = WDSC_MSTART(r->ombn);
115667482Sbabkin
115767482Sbabkin	if (wds_cmd(base, &c, sizeof c) != 0) {
115867482Sbabkin		device_printf(wp->dev, "unable to start outgoing mbox\n");
115967482Sbabkin		wp->dx->ombs[r->ombn].stat = 0;
116067482Sbabkin		wdsr_ccb_done(wp, r, r->ccb, CAM_RESRC_UNAVAIL);
116167482Sbabkin		return;
116267482Sbabkin	}
1163106592Sjhb	DBG(DBX "wds%d: enqueued cmd 0x%x, r=%p\n", unit,
1164106592Sjhb	    r->cmd.scb[0] & 0xFF, r);
116567482Sbabkin
116667482Sbabkin	smallog3('+', ccb_h->target_id + '0', ccb_h->target_lun + '0');
116767482Sbabkin}
116867482Sbabkin
116967482Sbabkinstatic void
117067482Sbabkinwds_action(struct cam_sim * sim, union ccb * ccb)
117167482Sbabkin{
117267482Sbabkin	int	unit = cam_sim_unit(sim);
117367482Sbabkin	int	s;
117467482Sbabkin
117567482Sbabkin	DBG(DBX "wds%d: action 0x%x\n", unit, ccb->ccb_h.func_code);
117667482Sbabkin	switch (ccb->ccb_h.func_code) {
117767482Sbabkin	case XPT_SCSI_IO:
117867482Sbabkin		s = splcam();
117967482Sbabkin		DBG(DBX "wds%d: SCSI IO entered\n", unit);
118067482Sbabkin		wds_scsi_io(sim, &ccb->csio);
118167482Sbabkin		DBG(DBX "wds%d: SCSI IO returned\n", unit);
118267482Sbabkin		splx(s);
118367482Sbabkin		break;
118467482Sbabkin	case XPT_RESET_BUS:
118567482Sbabkin		/* how to do it right ? */
118667482Sbabkin		printf("wds%d: reset\n", unit);
118767482Sbabkin		ccb->ccb_h.status = CAM_REQ_CMP;
118867482Sbabkin		xpt_done(ccb);
118967482Sbabkin		break;
119067482Sbabkin	case XPT_ABORT:
119167482Sbabkin		ccb->ccb_h.status = CAM_UA_ABORT;
119267482Sbabkin		xpt_done(ccb);
119367482Sbabkin		break;
119467482Sbabkin	case XPT_CALC_GEOMETRY:
119567482Sbabkin	{
119667482Sbabkin		struct	  ccb_calc_geometry *ccg;
119767482Sbabkin		u_int32_t size_mb;
119867482Sbabkin		u_int32_t secs_per_cylinder;
119967482Sbabkin
120067482Sbabkin		ccg = &ccb->ccg;
120167482Sbabkin		size_mb = ccg->volume_size
120267482Sbabkin			/ ((1024L * 1024L) / ccg->block_size);
120367482Sbabkin
120467482Sbabkin		ccg->heads = 64;
120567482Sbabkin		ccg->secs_per_track = 16;
120667482Sbabkin		secs_per_cylinder = ccg->heads * ccg->secs_per_track;
120767482Sbabkin		ccg->cylinders = ccg->volume_size / secs_per_cylinder;
120867482Sbabkin		ccb->ccb_h.status = CAM_REQ_CMP;
120967482Sbabkin		xpt_done(ccb);
121067482Sbabkin		break;
121167482Sbabkin	}
121267482Sbabkin	case XPT_PATH_INQ:	/* Path routing inquiry */
121367482Sbabkin	{
121467482Sbabkin		struct ccb_pathinq *cpi = &ccb->cpi;
121567482Sbabkin
121667482Sbabkin		cpi->version_num = 1;	/* XXX??? */
121767482Sbabkin		cpi->hba_inquiry = 0;	/* nothing fancy */
121867482Sbabkin		cpi->target_sprt = 0;
121967482Sbabkin		cpi->hba_misc = 0;
122067482Sbabkin		cpi->hba_eng_cnt = 0;
122167482Sbabkin		cpi->max_target = 7;
122267482Sbabkin		cpi->max_lun = 7;
122367482Sbabkin		cpi->initiator_id = WDS_HBA_ID;
122467482Sbabkin		cpi->hba_misc = 0;
122567482Sbabkin		cpi->bus_id = cam_sim_bus(sim);
122667482Sbabkin		cpi->base_transfer_speed = 3300;
122767482Sbabkin		strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
122867482Sbabkin		strncpy(cpi->hba_vid, "WD/FDC", HBA_IDLEN);
122967482Sbabkin		strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
123067482Sbabkin		cpi->unit_number = cam_sim_unit(sim);
123167482Sbabkin		cpi->ccb_h.status = CAM_REQ_CMP;
123267482Sbabkin		xpt_done(ccb);
123367482Sbabkin		break;
123467482Sbabkin	}
123567482Sbabkin	default:
123667482Sbabkin		ccb->ccb_h.status = CAM_REQ_INVALID;
123767482Sbabkin		xpt_done(ccb);
123867482Sbabkin		break;
123967482Sbabkin	}
124067482Sbabkin}
124167482Sbabkin
124267482Sbabkinstatic void
124367482Sbabkinwds_poll(struct cam_sim * sim)
124467482Sbabkin{
124567482Sbabkin	wds_intr((struct wds *)cam_sim_softc(sim));
124667482Sbabkin}
124767482Sbabkin
124867482Sbabkin/* part of initialization done in probe() */
124967482Sbabkin/* returns 0 if OK, ENXIO if bad */
125067482Sbabkin
125167482Sbabkinstatic int
125267482Sbabkinwds_preinit(struct wds *wp)
125367482Sbabkin{
125467482Sbabkin	int	base;
125567482Sbabkin	int	i;
125667482Sbabkin
125767482Sbabkin	base = wp->addr;
125867482Sbabkin
125967482Sbabkin	/*
126067482Sbabkin	 * Sending a command causes the CMDRDY bit to clear.
126167482Sbabkin	 */
126267482Sbabkin	outb(base + WDS_CMD, WDSC_NOOP);
126367482Sbabkin	if (inb(base + WDS_STAT) & WDS_RDY)
126467482Sbabkin		return (ENXIO);
126567482Sbabkin
126667482Sbabkin	/*
126767482Sbabkin	 * the controller exists. reset and init.
126867482Sbabkin	 */
126967482Sbabkin	outb(base + WDS_HCR, WDSH_ASCRESET | WDSH_SCSIRESET);
127067482Sbabkin	DELAY(30);
127167482Sbabkin	outb(base + WDS_HCR, 0);
127267482Sbabkin
127367482Sbabkin	if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
127467482Sbabkin		for (i = 0; i < 10; i++) {
127567482Sbabkin			if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY)
127667482Sbabkin				break;
127767482Sbabkin			DELAY(40000);
127867482Sbabkin		}
127967482Sbabkin		if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY)
128067482Sbabkin			/* probe timeout */
128167482Sbabkin			return (ENXIO);
128267482Sbabkin	}
128367482Sbabkin
128467482Sbabkin	return (0);
128567482Sbabkin}
128667482Sbabkin
128767482Sbabkin/* part of initialization done in attach() */
128867482Sbabkin/* returns 0 if OK, 1 if bad */
128967482Sbabkin
129067482Sbabkinstatic int
129167482Sbabkinwds_init(struct wds *wp)
129267482Sbabkin{
129367482Sbabkin	struct	wds_setup init;
129467482Sbabkin	int	base;
129567482Sbabkin	int	i;
129667482Sbabkin	struct	wds_cmd  wc;
129767482Sbabkin
129867482Sbabkin	base = wp->addr;
129967482Sbabkin
130067482Sbabkin	outb(base + WDS_HCR, WDSH_DRQEN);
130167482Sbabkin
130267482Sbabkin	isa_dmacascade(wp->drq);
130367482Sbabkin
130467482Sbabkin	if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
130567482Sbabkin		for (i = 0; i < 10; i++) {
130667482Sbabkin			if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY)
130767482Sbabkin				break;
130867482Sbabkin			DELAY(40000);
130967482Sbabkin		}
131067482Sbabkin		if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY)
131167482Sbabkin			/* probe timeout */
131267482Sbabkin			return (1);
131367482Sbabkin	}
131467482Sbabkin	bzero(&init, sizeof init);
131567482Sbabkin	init.cmd = WDSC_INIT;
131667482Sbabkin	init.scsi_id = WDS_HBA_ID;
131767482Sbabkin	init.buson_t = 24;
131867482Sbabkin	init.busoff_t = 48;
131967482Sbabkin	scsi_ulto3b(WDSTOPHYS(wp, &wp->dx->ombs), init.mbaddr);
132067482Sbabkin	init.xx = 0;
132167482Sbabkin	init.nomb = WDS_NOMB;
132267482Sbabkin	init.nimb = WDS_NIMB;
132367482Sbabkin
132467482Sbabkin	wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
132567482Sbabkin	if (wds_cmd(base, (u_int8_t *) & init, sizeof init) != 0) {
132667482Sbabkin		device_printf(wp->dev, "wds_cmd init failed\n");
132767482Sbabkin		return (1);
132867482Sbabkin	}
132967482Sbabkin	wds_wait(base + WDS_STAT, WDS_INIT, WDS_INIT);
133067482Sbabkin
133167482Sbabkin	wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
133267482Sbabkin
133367482Sbabkin	bzero(&wc, sizeof wc);
133467482Sbabkin	wc.cmd = WDSC_DISUNSOL;
133567482Sbabkin	if (wds_cmd(base, (char *) &wc, sizeof wc) != 0) {
133667482Sbabkin		device_printf(wp->dev, "wds_cmd init2 failed\n");
133767482Sbabkin		return (1);
133867482Sbabkin	}
133967482Sbabkin	return (0);
134067482Sbabkin}
134167482Sbabkin
134267482Sbabkinstatic int
134367482Sbabkinwds_cmd(int base, u_int8_t * p, int l)
134467482Sbabkin{
134567482Sbabkin	int	s = splcam();
134667482Sbabkin
134767482Sbabkin	while (l--) {
134867482Sbabkin		do {
134967482Sbabkin			outb(base + WDS_CMD, *p);
135067482Sbabkin			wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
135167482Sbabkin		} while (inb(base + WDS_STAT) & WDS_REJ);
135267482Sbabkin		p++;
135367482Sbabkin	}
135467482Sbabkin
135567482Sbabkin	wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
135667482Sbabkin
135767482Sbabkin	splx(s);
135867482Sbabkin
135967482Sbabkin	return (0);
136067482Sbabkin}
136167482Sbabkin
136267482Sbabkinstatic void
136367482Sbabkinwds_wait(int reg, int mask, int val)
136467482Sbabkin{
136567482Sbabkin	while ((inb(reg) & mask) != val)
136667482Sbabkin		;
136767482Sbabkin}
136867482Sbabkin
136967482Sbabkinstatic struct wds_req *
137067482Sbabkincmdtovirt(struct wds *wp, u_int32_t phys)
137167482Sbabkin{
137267482Sbabkin	char	*a;
137367482Sbabkin
1374106592Sjhb	a = WDSTOVIRT(wp, (uintptr_t)phys);
137567482Sbabkin	if( a < (char *)&wp->dx->req[0] || a>= (char *)&wp->dx->req[MAXSIMUL]) {
137667482Sbabkin		device_printf(wp->dev, "weird phys address 0x%x\n", phys);
137767482Sbabkin		return (NULL);
137867482Sbabkin	}
137967482Sbabkin	a -= (int)offsetof(struct wds_req, cmd); /* convert cmd to request */
138067482Sbabkin	return ((struct wds_req *)a);
138167482Sbabkin}
138267482Sbabkin
138367482Sbabkin/* for debugging, print out all the data about the status of devices */
138467482Sbabkinvoid
138567482Sbabkinwds_print(void)
138667482Sbabkin{
138767482Sbabkin	int	unit;
138867482Sbabkin	int	i;
138967482Sbabkin	struct	wds_req *r;
139067482Sbabkin	struct	wds     *wp;
139167482Sbabkin
139267482Sbabkin	for (unit = 0; unit < devclass_get_maxunit(wds_devclass); unit++) {
139367482Sbabkin		wp = (struct wds *) devclass_get_device(wds_devclass, unit);
139467482Sbabkin		if (wp == NULL)
139567482Sbabkin			continue;
139667482Sbabkin		printf("wds%d: want_wdsr=0x%x stat=0x%x irq=%s irqstat=0x%x\n",
139767482Sbabkin		       unit, wp->want_wdsr, inb(wp->addr + WDS_STAT) & 0xff,
139867482Sbabkin		       (inb(wp->addr + WDS_STAT) & WDS_IRQ) ? "ready" : "no",
139967482Sbabkin		       inb(wp->addr + WDS_IRQSTAT) & 0xff);
140067482Sbabkin		for (i = 0; i < MAXSIMUL; i++) {
140167482Sbabkin			r = &wp->dx->req[i];
140267482Sbabkin			if( wp->wdsr_free & (1 << r->id) ) {
140367482Sbabkin				printf("req=%d flg=0x%x ombn=%d ombstat=%d "
140467482Sbabkin				       "mask=0x%x targ=%d lun=%d cmd=0x%x\n",
140567482Sbabkin				       i, r->flags, r->ombn,
140667482Sbabkin				       wp->dx->ombs[r->ombn].stat,
140767482Sbabkin				       r->mask, r->cmd.targ >> 5,
140867482Sbabkin				       r->cmd.targ & 7, r->cmd.scb[0]);
140967482Sbabkin			}
141067482Sbabkin		}
141167482Sbabkin	}
141267482Sbabkin}
141367482Sbabkin
141467482Sbabkin#if WDS_DEBUG == 2
141567482Sbabkin/* create circular log buffer */
141667482Sbabkinstatic char    *
141767482Sbabkinwds_nextlog(void)
141867482Sbabkin{
141967482Sbabkin	int	n = logwrite;
142067482Sbabkin
142167482Sbabkin	if (++logwrite >= NLOGLINES)
142267482Sbabkin		logwrite = 0;
142367482Sbabkin	if (logread == logwrite)
142467482Sbabkin		if (++logread >= NLOGLINES)
142567482Sbabkin			logread = 0;
142667482Sbabkin	return (wds_log[n]);
142767482Sbabkin}
142867482Sbabkin
142967482Sbabkinvoid
143067482Sbabkinwds_printlog(void)
143167482Sbabkin{
143267482Sbabkin	/* print the circular buffer */
143367482Sbabkin	int	i;
143467482Sbabkin
143567482Sbabkin	for (i = logread; i != logwrite;) {
143667482Sbabkin		printf("%s", wds_log[i]);
143767482Sbabkin		if (i == NLOGLINES)
143867482Sbabkin			i = 0;
143967482Sbabkin		else
144067482Sbabkin			i++;
144167482Sbabkin	}
144267482Sbabkin}
144367482Sbabkin#endif /* WDS_DEBUG */
1444