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