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