FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/wd7000.c
1 /*
2 * Copyright (c) 1994 Ludd, University of Lule}, Sweden.
3 * All rights reserved.
4 *
5 * Written by Olof Johansson (offe@ludd.luth.se) 1995.
6 * Based on code written by Theo de Raadt (deraadt@fsa.ca).
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed at Ludd, University of Lule}.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /* All bugs are subject to removal without further notice */
35
36 /*
37 * offe 01/07/95
38 *
39 * This version of the driver _still_ doesn't implement scatter/gather for the
40 * WD7000-FASST2. This is due to the fact that my controller doesn't seem to
41 * support it. That, and the lack of documentation makes it impossible for
42 * me to implement it.
43 * What I've done instead is allocated a local buffer, contiguous buffer big
44 * enough to handle the requests. I haven't seen any read/write bigger than 64k,
45 * so I allocate a buffer of 64+16k. The data that needs to be DMA'd to/from
46 * the controller is copied to/from that buffer before/after the command is
47 * sent to the card.
48 */
49
50 #include "wds.h"
51 #if NWDS > 0
52
53 #include <sys/types.h>
54 #include <sys/param.h>
55 #include <sys/systm.h>
56 #include <sys/errno.h>
57 #include <sys/ioctl.h>
58 #include <sys/buf.h>
59 #include <sys/proc.h>
60 #include <sys/dkbad.h>
61 #include <sys/disklabel.h>
62
63 #include <scsi/scsi_all.h>
64 #include <scsi/scsiconf.h>
65
66 #include <machine/clock.h>
67
68 #include <vm/vm.h>
69 #include <vm/vm_param.h>
70 #include <vm/pmap.h>
71
72 #include <i386/isa/isa_device.h>
73
74 static struct scsi_device wds_dev =
75 {
76 NULL,
77 NULL,
78 NULL,
79 NULL,
80 "wds",
81 0,
82 { 0, 0 }
83 };
84
85 /*
86 XXX THIS SHOULD BE FIXED!
87 I haven't got the KERNBASE-version to work, but on my system the kernel
88 is at virtual address 0xFxxxxxxx, responding to physical address
89 0x0xxxxxxx.
90 #define PHYSTOKV(x) ((x) + KERNBASE)
91 */
92 #define PHYSTOKV(x) ((x) | 0xf0000000)
93 #define KVTOPHYS(x) vtophys(x)
94 /* 0x10000 (64k) should be enough. But just to be sure... */
95 #define BUFSIZ 0x12000
96
97
98 /* WD7000 registers */
99 #define WDS_STAT 0 /* read */
100 #define WDS_IRQSTAT 1 /* read */
101
102 #define WDS_CMD 0 /* write */
103 #define WDS_IRQACK 1 /* write */
104 #define WDS_HCR 2 /* write */
105
106 /* WDS_STAT (read) defs */
107 #define WDS_IRQ 0x80
108 #define WDS_RDY 0x40
109 #define WDS_REJ 0x20
110 #define WDS_INIT 0x10
111
112 /* WDS_IRQSTAT (read) defs */
113 #define WDSI_MASK 0xc0
114 #define WDSI_ERR 0x00
115 #define WDSI_MFREE 0x80
116 #define WDSI_MSVC 0xc0
117
118 /* WDS_CMD (write) defs */
119 #define WDSC_NOOP 0x00
120 #define WDSC_INIT 0x01
121 #define WDSC_DISUNSOL 0x02
122 #define WDSC_ENAUNSOL 0x03
123 #define WDSC_IRQMFREE 0x04
124 #define WDSC_SCSIRESETSOFT 0x05
125 #define WDSC_SCSIRESETHARD 0x06
126 #define WDSC_MSTART(m) (0x80 + (m))
127 #define WDSC_MMSTART(m) (0xc0 + (m))
128
129 /* WDS_HCR (write) defs */
130 #define WDSH_IRQEN 0x08
131 #define WDSH_DRQEN 0x04
132 #define WDSH_SCSIRESET 0x02
133 #define WDSH_ASCRESET 0x01
134
135 struct wds_cmd {
136 u_char cmd;
137 u_char targ;
138 u_char scb[12]; /*u_char scb[12];*/
139 u_char stat;
140 u_char venderr;
141 u_char len[3];
142 u_char data[3];
143 u_char next[3];
144 u_char write;
145 u_char xx[6];
146 };
147
148 struct wds_req {
149 struct wds_cmd cmd;
150 struct wds_cmd sense;
151 struct scsi_xfer *sxp;
152 int busy, polled;
153 int done, ret, ombn;
154 };
155
156 #define WDSX_SCSICMD 0x00
157 #define WDSX_OPEN_RCVBUF 0x80
158 #define WDSX_RCV_CMD 0x81
159 #define WDSX_RCV_DATA 0x82
160 #define WDSX_RCV_DATASTAT 0x83
161 #define WDSX_SND_DATA 0x84
162 #define WDSX_SND_DATASTAT 0x85
163 #define WDSX_SND_CMDSTAT 0x86
164 #define WDSX_READINIT 0x88
165 #define WDSX_READSCSIID 0x89
166 #define WDSX_SETUNSOLIRQMASK 0x8a
167 #define WDSX_GETUNSOLIRQMASK 0x8b
168 #define WDSX_GETFIRMREV 0x8c
169 #define WDSX_EXECDIAG 0x8d
170 #define WDSX_SETEXECPARM 0x8e
171 #define WDSX_GETEXECPARM 0x8f
172
173 struct wds_mb {
174 u_char stat;
175 u_char addr[3];
176 };
177 /* ICMB status value */
178 #define ICMB_OK 0x01
179 #define ICMB_OKERR 0x02
180 #define ICMB_ETIME 0x04
181 #define ICMB_ERESET 0x05
182 #define ICMB_ETARCMD 0x06
183 #define ICMB_ERESEL 0x80
184 #define ICMB_ESEL 0x81
185 #define ICMB_EABORT 0x82
186 #define ICMB_ESRESET 0x83
187 #define ICMB_EHRESET 0x84
188
189 struct wds_setup {
190 u_char cmd;
191 u_char scsi_id;
192 u_char buson_t;
193 u_char busoff_t;
194 u_char xx;
195 u_char mbaddr[3];
196 u_char nomb;
197 u_char nimb;
198 };
199
200 #define WDS_NOMB 8
201 #define WDS_NIMB 8
202 #define MAXSIMUL 8
203
204 static int wdsunit=0;
205
206 static u_char wds_data[NWDS][BUFSIZ];
207 static u_char wds_data_in_use[NWDS];
208
209 static struct wds {
210 int addr;
211 struct wds_req wdsr[MAXSIMUL];
212 struct wds_mb ombs[WDS_NOMB], imbs[WDS_NIMB];
213 struct scsi_link sc_link;
214 } wds[NWDS];
215
216 static int wdsprobe(struct isa_device *);
217 static void wds_minphys(struct buf *);
218 static struct wds_req *wdsr_alloc(int);
219 static int32_t wds_scsi_cmd(struct scsi_xfer *);
220 static u_int32_t wds_adapter_info(int);
221 static int wds_done(int, struct wds_cmd *, u_char);
222 static int wdsattach(struct isa_device *);
223 static int wds_init(struct isa_device *);
224 static int wds_cmd(int, u_char *, int);
225 static void wds_wait(int, int, int);
226
227 struct isa_driver wdsdriver =
228 {
229 wdsprobe,
230 wdsattach,
231 "wds"
232 };
233
234 static struct scsi_adapter wds_switch =
235 {
236 wds_scsi_cmd,
237 wds_minphys,
238 0,
239 0,
240 wds_adapter_info,
241 "wds",
242 {0,0}
243 };
244
245 int
246 wdsprobe(struct isa_device *dev)
247 {
248 if(wdsunit > NWDS)
249 return 0;
250
251 dev->id_unit = wdsunit; /* XXX WRONG! */
252 wds[wdsunit].addr = dev->id_iobase;
253
254 if(wds_init(dev) != 0)
255 return 0;
256 wdsunit++;
257 return 8;
258 }
259
260 void
261 wds_minphys(struct buf *bp)
262 {
263 if(bp->b_bcount > BUFSIZ)
264 bp->b_bcount = BUFSIZ;
265 }
266
267 struct wds_req *
268 wdsr_alloc(int unit)
269 {
270 struct wds_req *r;
271 int x;
272 int i;
273
274 r = NULL;
275 x = splbio();
276 for(i=0; i<MAXSIMUL; i++)
277 if(!wds[unit].wdsr[i].busy)
278 {
279 r = &wds[unit].wdsr[i];
280 r->busy = 1;
281 break;
282 }
283 if(!r)
284 {
285 splx(x);
286 return NULL;
287 }
288
289 r->ombn = -1;
290 for(i=0; i<WDS_NOMB; i++)
291 if(!wds[unit].ombs[i].stat)
292 {
293 wds[unit].ombs[i].stat = 1;
294 r->ombn = i;
295 break;
296 }
297 if(r->ombn == -1 )
298 {
299 r->busy = 0;
300 splx(x);
301 return NULL;
302 }
303 splx(x);
304 return r;
305 }
306
307 int32_t
308 wds_scsi_cmd(struct scsi_xfer *sxp)
309 {
310 struct wds_req *r;
311 int unit = sxp->sc_link->adapter_unit;
312 int base;
313 u_char c;
314 int i;
315
316 base = wds[unit].addr;
317
318 if( sxp->flags & SCSI_RESET)
319 {
320 printf("reset!\n");
321 return COMPLETE;
322 }
323
324 r = wdsr_alloc(unit);
325 if(r==NULL)
326 {
327 printf("no request slot available!\n");
328 sxp->error = XS_DRIVER_STUFFUP;
329 return TRY_AGAIN_LATER;
330 }
331 r->done = 0;
332 r->sxp = sxp;
333
334 if(sxp->flags & SCSI_DATA_UIO)
335 {
336 printf("UIO!\n");
337 sxp->error = XS_DRIVER_STUFFUP;
338 return TRY_AGAIN_LATER;
339 }
340
341 scsi_uto3b(KVTOPHYS(&r->cmd),wds[unit].ombs[r->ombn].addr);
342
343 bzero(&r->cmd, sizeof r->cmd);
344 r->cmd.cmd = WDSX_SCSICMD;
345 r->cmd.targ = (sxp->sc_link->target << 5) | sxp->sc_link->lun;
346 bcopy(sxp->cmd, &r->cmd.scb, sxp->cmdlen<12 ? sxp->cmdlen : 12);
347 scsi_uto3b(sxp->datalen, r->cmd.len);
348
349 if(wds_data_in_use[unit])
350 {
351 sxp->error = XS_DRIVER_STUFFUP;
352 return TRY_AGAIN_LATER;
353 }
354 else
355 wds_data_in_use[unit] = 1;
356
357 if(sxp->datalen && !(sxp->flags&SCSI_DATA_IN))
358 bcopy(sxp->data, wds_data[unit], sxp->datalen);
359
360 scsi_uto3b(sxp->datalen ? KVTOPHYS(wds_data[unit]) : 0, r->cmd.data);
361
362 r->cmd.write = (sxp->flags&SCSI_DATA_IN)? 0x80 : 0x00;
363
364 scsi_uto3b(KVTOPHYS(&r->sense),r->cmd.next);
365
366 bzero(&r->sense, sizeof r->sense);
367 r->sense.cmd = r->cmd.cmd;
368 r->sense.targ = r->cmd.targ;
369 r->sense.scb[0] = REQUEST_SENSE;
370 scsi_uto3b(KVTOPHYS(&sxp->sense),r->sense.data);
371 scsi_uto3b(sizeof(sxp->sense), r->sense.len);
372 r->sense.write = 0x80;
373
374 if(sxp->flags & SCSI_NOMASK)
375 {
376 outb(base+WDS_HCR, WDSH_DRQEN);
377 r->polled = 1;
378 } else
379 {
380 outb(base+WDS_HCR, WDSH_IRQEN|WDSH_DRQEN);
381 r->polled = 0;
382 }
383
384 c = WDSC_MSTART(r->ombn);
385
386 if( wds_cmd(base, &c, sizeof c) != 0)
387 {
388 printf("wds%d: unable to start outgoing mbox\n", unit);
389 r->busy = 0;
390 wds[unit].ombs[r->ombn].stat = 0;
391
392 return TRY_AGAIN_LATER;
393 }
394
395 if(sxp->flags & SCSI_NOMASK)
396 {
397 repoll:
398
399 i = 0;
400 while(!(inb(base+WDS_STAT) & WDS_IRQ))
401 {
402
403 DELAY(20000);
404 if(++i == 20)
405 {
406 outb(base + WDS_IRQACK, 0);
407 /*r->busy = 0;*/
408 sxp->error = XS_TIMEOUT;
409 return HAD_ERROR;
410 }
411 }
412 wdsintr(unit);
413 if(r->done)
414 {
415 r->sxp->flags |= ITSDONE;
416 r->busy = 0;
417 return r->ret;
418 }
419 goto repoll;
420 }
421
422 return SUCCESSFULLY_QUEUED;
423 }
424
425 u_int32_t
426 wds_adapter_info(int unit)
427 {
428 return 1;
429 }
430
431 void
432 wdsintr(int unit)
433 {
434 struct wds_cmd *pc, *vc;
435 struct wds_mb *in;
436 u_char stat;
437 u_char c;
438
439 if(!inb(wds[unit].addr+WDS_STAT) & WDS_IRQ)
440 {
441 outb(wds[unit].addr + WDS_IRQACK, 0);
442 return;
443 }
444
445 c = inb(wds[unit].addr + WDS_IRQSTAT);
446 if( (c&WDSI_MASK) == WDSI_MSVC)
447 {
448 c = c & ~WDSI_MASK;
449 in = &wds[unit].imbs[c];
450
451 pc = (struct wds_cmd *)scsi_3btou(in->addr);
452 vc = (struct wds_cmd *)PHYSTOKV((long)pc);
453 stat = in->stat;
454
455 wds_done(unit, vc, stat);
456 in->stat = 0;
457
458 outb(wds[unit].addr + WDS_IRQACK, 0);
459 }
460 }
461
462 int
463 wds_done(int unit, struct wds_cmd *c, u_char stat)
464 {
465 struct wds_req *r;
466 int i;
467
468 r = (struct wds_req *)NULL;
469
470 for(i=0; i<MAXSIMUL; i++)
471 if( c == &wds[unit].wdsr[i].cmd && !wds[unit].wdsr[i].done)
472 {
473 r = &wds[unit].wdsr[i];
474 break;
475 }
476 if(r == (struct wds_req *)NULL)
477 {
478 /* failed to find request! */
479 return 1;
480 }
481
482 r->done = 1;
483 wds[unit].ombs[r->ombn].stat = 0;
484 r->ret = HAD_ERROR;
485 switch(stat)
486 {
487 case ICMB_OK:
488 r->ret = COMPLETE;
489 if(r->sxp)
490 r->sxp->resid = 0;
491 break;
492 case ICMB_OKERR:
493 if(!(r->sxp->flags & SCSI_ERR_OK) && c->stat)
494 {
495 r->sxp->sense.error_code = c->venderr;
496 r->sxp->error=XS_SENSE;
497 }
498 else
499 r->sxp->error=XS_NOERROR;
500 r->ret = COMPLETE;
501 break;
502 case ICMB_ETIME:
503 r->sxp->error = XS_TIMEOUT;
504 r->ret = HAD_ERROR;
505 break;
506 case ICMB_ERESET:
507 case ICMB_ETARCMD:
508 case ICMB_ERESEL:
509 case ICMB_ESEL:
510 case ICMB_EABORT:
511 case ICMB_ESRESET:
512 case ICMB_EHRESET:
513 r->sxp->error = XS_DRIVER_STUFFUP;
514 r->ret = HAD_ERROR;
515 break;
516 }
517
518 if(r->sxp)
519 if(r->sxp->datalen && (r->sxp->flags&SCSI_DATA_IN))
520 bcopy(wds_data[unit],r->sxp->data,r->sxp->datalen);
521
522 wds_data_in_use[unit] = 0;
523
524 if(!r->polled)
525 {
526 r->sxp->flags |= ITSDONE;
527 scsi_done(r->sxp);
528 }
529
530 r->busy = 0;
531
532 return 0;
533 }
534
535 static int
536 wds_getvers(int unit)
537 {
538 struct wds_req *r;
539 int base;
540 u_char c;
541 int i;
542
543 base = wds[unit].addr;
544
545 r = wdsr_alloc(unit);
546 if(!r)
547 {
548 printf("wds%d: no request slot available!\n", unit);
549 return -1;
550 }
551
552 r->done = 0;
553 r->sxp = NULL;
554
555 scsi_uto3b(KVTOPHYS(&r->cmd), wds[unit].ombs[r->ombn].addr);
556
557 bzero(&r->cmd, sizeof r->cmd);
558 r->cmd.cmd = WDSX_GETFIRMREV;
559
560 outb(base+WDS_HCR, WDSH_DRQEN);
561 r->polled = 1;
562
563 c = WDSC_MSTART(r->ombn);
564 if(wds_cmd(base, (u_char *)&c, sizeof c))
565 {
566 printf("wds%d: version request failed\n", unit);
567 r->busy = 0;
568 wds[unit].ombs[r->ombn].stat = 0;
569 return -1;
570 }
571
572 while(1)
573 {
574 i = 0;
575 while( (inb(base+WDS_STAT) & WDS_IRQ) == 0)
576 {
577 DELAY(9000);
578 if(++i == 20)
579 return -1;
580 }
581 wdsintr(unit);
582 if(r->done)
583 {
584 printf("wds%d: firmware version %d.%02d\n", unit,
585 r->cmd.targ, r->cmd.scb[0]);
586 r->busy = 0;
587 return 0;
588 }
589 }
590 }
591
592 int
593 wdsattach(struct isa_device *dev)
594 {
595 int masunit;
596 static u_long versprobe=0; /* max 32 controllers */
597 int unit = dev->id_unit;
598 struct scsibus_data *scbus;
599
600 masunit = dev->id_unit;
601
602 if( !(versprobe & (1<<masunit)))
603 {
604 versprobe |= (1<<masunit);
605 if(wds_getvers(masunit)==-1)
606 printf("wds%d: getvers failed\n", masunit);
607 }
608
609 printf("wds%d: using %d bytes for dma buffer\n",unit,BUFSIZ);
610
611 wds[unit].sc_link.adapter_unit = unit;
612 wds[unit].sc_link.adapter_targ = 7;
613 wds[unit].sc_link.adapter = &wds_switch;
614 wds[unit].sc_link.device = &wds_dev;
615 wds[unit].sc_link.flags = SDEV_BOUNCE;
616
617 /*
618 * Prepare the scsibus_data area for the upperlevel
619 * scsi code.
620 */
621 scbus = scsi_alloc_bus();
622 if(!scbus)
623 return 0;
624 scbus->adapter_link = &wds[unit].sc_link;
625
626 scsi_attachdevs(scbus);
627
628 return 1;
629 }
630
631 int
632 wds_init(struct isa_device *dev)
633 {
634 struct wds_setup init;
635 int base;
636 int unit, i;
637 struct wds_cmd wc;
638
639 unit = dev->id_unit;
640 base = wds[unit].addr;
641
642 /*
643 * Sending a command causes the CMDRDY bit to clear.
644 */
645
646 outb(base+WDS_CMD, WDSC_NOOP);
647 if( inb(base+WDS_STAT) & WDS_RDY)
648 return 1;
649
650 /*
651 * the controller exists. reset and init.
652 */
653 outb(base+WDS_HCR, WDSH_ASCRESET|WDSH_SCSIRESET);
654 DELAY(30);
655 outb(base+WDS_HCR, 0);
656
657 outb(base+WDS_HCR, WDSH_DRQEN);
658
659 isa_dmacascade(dev->id_drq);
660
661 if( (inb(base+WDS_STAT) & (WDS_RDY)) != WDS_RDY)
662 {
663 for(i=0; i<10; i++)
664 {
665 if( (inb(base+WDS_STAT) & (WDS_RDY)) == WDS_RDY)
666 break;
667 DELAY(40000);
668 }
669 if( (inb(base+WDS_STAT) & (WDS_RDY)) != WDS_RDY) /* probe timeout */
670 return 1;
671 }
672
673 bzero(&init, sizeof init);
674 init.cmd = WDSC_INIT;
675 init.scsi_id = 7;
676 init.buson_t = 24;
677 init.busoff_t = 48;
678 scsi_uto3b(KVTOPHYS(wds[unit].ombs), init.mbaddr);
679 init.xx = 0;
680 init.nomb = WDS_NOMB;
681 init.nimb = WDS_NIMB;
682
683 wds_wait(base+WDS_STAT, WDS_RDY, WDS_RDY);
684 if( wds_cmd(base, (u_char *)&init, sizeof init) != 0)
685 {
686 printf("wds%d: wds_cmd failed\n", unit);
687 return 1;
688 }
689
690 wds_wait(base+WDS_STAT, WDS_INIT, WDS_INIT);
691
692 wds_wait(base+WDS_STAT, WDS_RDY, WDS_RDY);
693
694 bzero(&wc,sizeof wc);
695 wc.cmd = WDSC_DISUNSOL;
696 if( wds_cmd(base, (char *)&wc, sizeof wc) != 0)
697 {
698 printf("wds%d: wds_cmd failed\n", unit);
699 return 1;
700 }
701
702 return 0;
703 }
704
705 int
706 wds_cmd(int base, u_char *p, int l)
707 {
708 int s=splbio();
709
710 while(l--)
711 {
712 do
713 {
714 outb(base+WDS_CMD,*p);
715 wds_wait(base+WDS_STAT,WDS_RDY,WDS_RDY);
716 } while (inb(base+WDS_STAT) & WDS_REJ);
717 p++;
718 }
719
720 wds_wait(base+WDS_STAT,WDS_RDY,WDS_RDY);
721
722 splx(s);
723
724 return 0;
725 }
726
727 void
728 wds_wait(int reg, int mask, int val)
729 {
730 while((inb(reg) & mask) != val);
731 }
732
733 #endif
Cache object: 84d9e62ba94fff98db0701c7be3b3cce
|