The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/isa/wds.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $OpenBSD: wds.c,v 1.59 2022/04/16 19:19:59 naddy Exp $  */
    2 /*      $NetBSD: wds.c,v 1.13 1996/11/03 16:20:31 mycroft Exp $ */
    3 
    4 #undef  WDSDIAG
    5 #ifdef DDB
    6 #define integrate
    7 #else
    8 #define integrate       static inline
    9 #endif
   10 
   11 /*
   12  * XXX
   13  * sense data
   14  * aborts
   15  * resets
   16  */
   17 
   18 /*
   19  * Copyright (c) 1994, 1995 Julian Highfield.  All rights reserved.
   20  * Portions copyright (c) 1994, 1996 Charles M. Hannum.  All rights reserved.
   21  *
   22  * Redistribution and use in source and binary forms, with or without
   23  * modification, are permitted provided that the following conditions
   24  * are met:
   25  * 1. Redistributions of source code must retain the above copyright
   26  *    notice, this list of conditions and the following disclaimer.
   27  * 2. Redistributions in binary form must reproduce the above copyright
   28  *    notice, this list of conditions and the following disclaimer in the
   29  *    documentation and/or other materials provided with the distribution.
   30  * 3. All advertising materials mentioning features or use of this software
   31  *    must display the following acknowledgement:
   32  *      This product includes software developed by Julian Highfield.
   33  * 4. The name of the author may not be used to endorse or promote products
   34  *    derived from this software without specific prior written permission.
   35  *
   36  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   37  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   39  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   40  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   41  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   42  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   43  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   44  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   45  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   46  */
   47 
   48 /*
   49  * This driver is for the WD7000 family of SCSI controllers:
   50  *   the WD7000-ASC, a bus-mastering DMA controller,
   51  *   the WD7000-FASST2, an -ASC with new firmware and scatter-gather,
   52  *   and the WD7000-ASE, which was custom manufactured for Apollo
   53  *      workstations and seems to include an -ASC as well as floppy
   54  *      and ESDI interfaces.
   55  *
   56  * Loosely based on Theo Deraadt's unfinished attempt says the NetBSD group
   57  * so they decided to delete the copyright that file had on it.
   58  */
   59 
   60 #include <sys/param.h>
   61 #include <sys/systm.h>
   62 #include <sys/kernel.h>
   63 #include <sys/errno.h>
   64 #include <sys/ioctl.h>
   65 #include <sys/device.h>
   66 #include <sys/malloc.h>
   67 #include <sys/buf.h>
   68 #include <uvm/uvm_extern.h>
   69 
   70 #include <machine/bus.h>
   71 #include <machine/intr.h>
   72 
   73 #include <scsi/scsi_all.h>
   74 #include <scsi/scsiconf.h>
   75 
   76 #include <dev/isa/isavar.h>
   77 #include <dev/isa/isadmavar.h>
   78 #include <dev/isa/wdsreg.h>
   79 
   80 #define WDS_MBX_SIZE    16
   81 
   82 #define WDS_SCB_MAX     32
   83 #define SCB_HASH_SIZE   32      /* hash table size for phystokv */
   84 #define SCB_HASH_SHIFT  9
   85 #define SCB_HASH(x)     ((((long)(x))>>SCB_HASH_SHIFT) & (SCB_HASH_SIZE - 1))
   86 
   87 #define wds_nextmbx(wmb, mbx, mbio) \
   88         if ((wmb) == &(mbx)->mbio[WDS_MBX_SIZE - 1])    \
   89                 (wmb) = &(mbx)->mbio[0];                \
   90         else                                            \
   91                 (wmb)++;
   92 
   93 struct wds_mbx {
   94         struct wds_mbx_out mbo[WDS_MBX_SIZE];
   95         struct wds_mbx_in mbi[WDS_MBX_SIZE];
   96         struct wds_mbx_out *cmbo;       /* Collection Mail Box out */
   97         struct wds_mbx_out *tmbo;       /* Target Mail Box out */
   98         struct wds_mbx_in *tmbi;        /* Target Mail Box in */
   99 };
  100 
  101 #define KVTOPHYS(x)     vtophys((vaddr_t)(x))
  102 
  103 struct wds_softc {
  104         struct device sc_dev;
  105         struct isadev sc_id;
  106         void *sc_ih;
  107 
  108         bus_space_tag_t sc_iot;         /* bus identifier */
  109         bus_space_handle_t sc_ioh;      /* io handle */
  110         int sc_irq, sc_drq;
  111 
  112         int sc_revision;
  113 
  114         struct wds_mbx sc_mbx;
  115 #define wmbx    (&sc->sc_mbx)
  116         struct wds_scb *sc_scbhash[SCB_HASH_SIZE];
  117         TAILQ_HEAD(, wds_scb) sc_free_scb, sc_waiting_scb;
  118         int sc_numscbs, sc_mbofull;
  119         int sc_scsi_dev;
  120 
  121         struct mutex            sc_scb_mtx;
  122         struct scsi_iopool      sc_iopool;
  123 };
  124 
  125 /* Define the bounce buffer length... */
  126 #define BUFLEN (64*1024)
  127 /* ..and how many there are. One per device! Non-FASST boards need these. */
  128 #define BUFCNT 8
  129 /* The macro for deciding whether the board needs a buffer. */
  130 #define NEEDBUFFER(sc)  (sc->sc_revision < 0x800)
  131 
  132 struct wds_buf {
  133         u_char data[BUFLEN];
  134         int    busy;
  135         TAILQ_ENTRY(wds_buf) chain;
  136 } wds_buffer[BUFCNT];
  137 
  138 TAILQ_HEAD(, wds_buf) wds_free_buffer;
  139 
  140 #ifdef WDSDEBUG
  141 int wds_debug = WDSDEBUG;
  142 #endif
  143 
  144 integrate void    wds_wait(bus_space_tag_t, bus_space_handle_t, int, int, int);
  145 int     wds_cmd(struct wds_softc *, u_char *, int);
  146 integrate void wds_finish_scbs(struct wds_softc *);
  147 int     wdsintr(void *);
  148 integrate void wds_reset_scb(struct wds_softc *, struct wds_scb *);
  149 void    wds_scb_free(void *, void *);
  150 void    wds_free_buf(struct wds_softc *, struct wds_buf *);
  151 integrate void wds_init_scb(struct wds_softc *, struct wds_scb *);
  152 void *wds_scb_alloc(void *);
  153 struct  wds_buf *wds_get_buf(struct wds_softc *, int);
  154 struct  wds_scb *wds_scb_phys_kv(struct wds_softc *, u_long);
  155 void    wds_queue_scb(struct wds_softc *, struct wds_scb *);
  156 void    wds_collect_mbo(struct wds_softc *);
  157 void    wds_start_scbs(struct wds_softc *);
  158 void    wds_done(struct wds_softc *, struct wds_scb *, u_char);
  159 int     wds_find(struct isa_attach_args *, struct wds_softc *);
  160 void    wds_init(struct wds_softc *);
  161 void    wds_inquire_setup_information(struct wds_softc *);
  162 void    wds_scsi_cmd(struct scsi_xfer *);
  163 void    wds_sense(struct wds_softc *, struct wds_scb *);
  164 int     wds_poll(struct wds_softc *, struct scsi_xfer *, int);
  165 int     wds_ipoll(struct wds_softc *, struct wds_scb *, int);
  166 void    wds_timeout(void *);
  167 int     wdsprint(void *, const char *);
  168 
  169 const struct scsi_adapter wds_switch = {
  170         wds_scsi_cmd, NULL, NULL, NULL, NULL
  171 };
  172 
  173 int     wdsprobe(struct device *, void *, void *);
  174 void    wdsattach(struct device *, struct device *, void *);
  175 
  176 const struct cfattach wds_ca = {
  177         sizeof(struct wds_softc), wdsprobe, wdsattach
  178 };
  179 
  180 struct cfdriver wds_cd = {
  181         NULL, "wds", DV_DULL
  182 };
  183 
  184 #define WDS_ABORT_TIMEOUT       2000    /* time to wait for abort (mSec) */
  185 
  186 integrate void
  187 wds_wait(bus_space_tag_t iot, bus_space_handle_t ioh, int port, int mask,
  188     int val)
  189 {
  190         while ((bus_space_read_1(iot, ioh, port) & mask) != val)
  191                 ;
  192 }
  193 
  194 /*
  195  * Write a command to the board's I/O ports.
  196  */
  197 int
  198 wds_cmd(struct wds_softc *sc,  u_int8_t *ibuf, int icnt)
  199 {
  200         bus_space_tag_t iot = sc->sc_iot;
  201         bus_space_handle_t ioh = sc->sc_ioh;
  202         u_int8_t c;
  203 
  204         wds_wait(iot, ioh, WDS_STAT, WDSS_RDY, WDSS_RDY);
  205 
  206         while (icnt--) {
  207                 bus_space_write_1(iot, ioh, WDS_CMD, *ibuf++);
  208                 wds_wait(iot, ioh, WDS_STAT, WDSS_RDY, WDSS_RDY);
  209                 c = bus_space_read_1(iot, ioh, WDS_STAT);
  210                 if (c & WDSS_REJ)
  211                         return 1;
  212         }
  213 
  214         return 0;
  215 }
  216 
  217 /*
  218  * Check for the presence of a WD7000 SCSI controller.
  219  */
  220 int
  221 wdsprobe(struct device *parent, void *match, void *aux)
  222 {
  223         register struct isa_attach_args *ia = aux;
  224         bus_space_tag_t iot = ia->ia_iot;
  225         bus_space_handle_t ioh;
  226         int rv;
  227 
  228         if (bus_space_map(iot, ia->ia_iobase, WDS_IO_PORTS, 0, &ioh))
  229                 return (0);
  230 
  231         /* See if there is a unit at this location. */
  232         rv = wds_find(ia, NULL);
  233 
  234         bus_space_unmap(iot, ioh, WDS_IO_PORTS);
  235 
  236         if (rv) {
  237                 ia->ia_msize = 0;
  238                 ia->ia_iosize = WDS_IO_PORTS;
  239         }
  240 
  241         return (rv);
  242 }
  243 
  244 int
  245 wdsprint(void *aux, const char *name)
  246 {
  247         if (name != NULL)
  248                 printf("%s: scsibus ", name);
  249         return UNCONF;
  250 }
  251 
  252 /*
  253  * Attach all available units.
  254  */
  255 void
  256 wdsattach(struct device *parent, struct device *self, void *aux)
  257 {
  258         struct isa_attach_args *ia = aux;
  259         struct wds_softc *sc = (void *)self;
  260         struct scsibus_attach_args saa;
  261         bus_space_tag_t iot = ia->ia_iot;
  262         bus_space_handle_t ioh;
  263 
  264         if (bus_space_map(iot, ia->ia_iobase, WDS_IO_PORTS, 0, &ioh)) {
  265                 printf("%s: can't map i/o space\n", sc->sc_dev.dv_xname);
  266                 return;
  267         }
  268 
  269         if (!wds_find(ia, sc))
  270                 panic("wdsattach: wds_find of %s failed", self->dv_xname);
  271         wds_init(sc);
  272 
  273         if (sc->sc_drq != DRQUNK)
  274                 isadma_cascade(sc->sc_drq);
  275 
  276         TAILQ_INIT(&sc->sc_free_scb);
  277         TAILQ_INIT(&sc->sc_waiting_scb);
  278         mtx_init(&sc->sc_scb_mtx, IPL_BIO);
  279         scsi_iopool_init(&sc->sc_iopool, sc, wds_scb_alloc, wds_scb_free);
  280 
  281         wds_inquire_setup_information(sc);
  282 
  283         sc->sc_ih = isa_intr_establish(ia->ia_ic, sc->sc_irq, IST_EDGE,
  284             IPL_BIO, wdsintr, sc, sc->sc_dev.dv_xname);
  285 
  286         /* XXX */
  287         /* I don't think the -ASE can handle openings > 1. */
  288         /* It gives Vendor Error 26 whenever I try it.     */
  289         saa.saa_adapter_softc = sc;
  290         saa.saa_adapter_target = sc->sc_scsi_dev;
  291         saa.saa_adapter = &wds_switch;
  292         saa.saa_luns = saa.saa_adapter_buswidth = 8;
  293         saa.saa_openings = 1;
  294         saa.saa_pool = &sc->sc_iopool;
  295         saa.saa_quirks = saa.saa_flags = 0;
  296         saa.saa_wwpn = saa.saa_wwnn = 0;
  297 
  298         config_found(self, &saa, wdsprint);
  299 }
  300 
  301 integrate void
  302 wds_finish_scbs(struct wds_softc *sc)
  303 {
  304         struct wds_mbx_in *wmbi;
  305         struct wds_scb *scb;
  306         int i;
  307 
  308         wmbi = wmbx->tmbi;
  309 
  310         if (wmbi->stat == WDS_MBI_FREE) {
  311                 for (i = 0; i < WDS_MBX_SIZE; i++) {
  312                         if (wmbi->stat != WDS_MBI_FREE) {
  313                                 printf("%s: mbi not in round-robin order\n",
  314                                     sc->sc_dev.dv_xname);
  315                                 goto AGAIN;
  316                         }
  317                         wds_nextmbx(wmbi, wmbx, mbi);
  318                 }
  319 #ifdef WDSDIAGnot
  320                 printf("%s: mbi interrupt with no full mailboxes\n",
  321                     sc->sc_dev.dv_xname);
  322 #endif
  323                 return;
  324         }
  325 
  326 AGAIN:
  327         do {
  328                 scb = wds_scb_phys_kv(sc, phystol(wmbi->scb_addr));
  329                 if (!scb) {
  330                         printf("%s: bad mbi scb pointer; skipping\n",
  331                             sc->sc_dev.dv_xname);
  332                         goto next;
  333                 }
  334 
  335 #ifdef WDSDEBUG
  336                 if (wds_debug) {
  337                         u_int8_t *cp = (u_int8_t *)&scb->cmd.scb;
  338                         printf("op=%x %x %x %x %x %x\n",
  339                             cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
  340                         printf("stat %x for mbi addr = %p, ",
  341                             wmbi->stat, wmbi);
  342                         printf("scb addr = %p\n", scb);
  343                 }
  344 #endif /* WDSDEBUG */
  345 
  346                 timeout_del(&scb->xs->stimeout);
  347                 wds_done(sc, scb, wmbi->stat);
  348 
  349         next:
  350                 wmbi->stat = WDS_MBI_FREE;
  351                 wds_nextmbx(wmbi, wmbx, mbi);
  352         } while (wmbi->stat != WDS_MBI_FREE);
  353 
  354         wmbx->tmbi = wmbi;
  355 }
  356 
  357 /*
  358  * Process an interrupt.
  359  */
  360 int
  361 wdsintr(void *arg)
  362 {
  363         struct wds_softc *sc = arg;
  364         bus_space_tag_t iot = sc->sc_iot;
  365         bus_space_handle_t ioh = sc->sc_ioh;
  366         u_char c;
  367 
  368         /* Was it really an interrupt from the board? */
  369         if ((bus_space_read_1(iot, ioh, WDS_STAT) & WDSS_IRQ) == 0)
  370                 return 0;
  371 
  372         /* Get the interrupt status byte. */
  373         c = bus_space_read_1(iot, ioh, WDS_IRQSTAT) & WDSI_MASK;
  374 
  375         /* Acknowledge (which resets) the interrupt. */
  376         bus_space_write_1(iot, ioh, WDS_IRQACK, 0x00);
  377 
  378         switch (c) {
  379         case WDSI_MSVC:
  380                 wds_finish_scbs(sc);
  381                 break;
  382 
  383         case WDSI_MFREE:
  384                 wds_start_scbs(sc);
  385                 break;
  386 
  387         default:
  388                 printf("%s: unrecognized interrupt type %02x",
  389                     sc->sc_dev.dv_xname, c);
  390                 break;
  391         }
  392 
  393         return 1;
  394 }
  395 
  396 integrate void
  397 wds_reset_scb(struct wds_softc *sc, struct wds_scb *scb)
  398 {
  399         scb->flags = 0;
  400 }
  401 
  402 /*
  403  * Free the command structure, the outgoing mailbox and the data buffer.
  404  */
  405 void
  406 wds_scb_free(void *xsc, void *xscb)
  407 {
  408         struct wds_softc *sc = xsc;
  409         struct wds_scb *scb = xscb;
  410 
  411         if (scb->buf) {
  412                 wds_free_buf(sc, scb->buf);
  413                 scb->buf = NULL;
  414         }
  415 
  416         wds_reset_scb(sc, scb);
  417         mtx_enter(&sc->sc_scb_mtx);
  418         TAILQ_INSERT_HEAD(&sc->sc_free_scb, scb, chain);
  419         mtx_leave(&sc->sc_scb_mtx);
  420 }
  421 
  422 void
  423 wds_free_buf(struct wds_softc *sc, struct wds_buf *buf)
  424 {
  425         int s;
  426 
  427         s = splbio();
  428 
  429         buf->busy = 0;
  430         TAILQ_INSERT_HEAD(&wds_free_buffer, buf, chain);
  431 
  432         /*
  433          * If there were none, wake anybody waiting for one to come free,
  434          * starting with queued entries.
  435          */
  436         if (TAILQ_NEXT(buf, chain) == NULL)
  437                 wakeup(&wds_free_buffer);
  438 
  439         splx(s);
  440 }
  441 
  442 integrate void
  443 wds_init_scb(struct wds_softc *sc, struct wds_scb *scb)
  444 {
  445         int hashnum;
  446 
  447         bzero(scb, sizeof(struct wds_scb));
  448         /*
  449          * put in the phystokv hash table
  450          * Never gets taken out.
  451          */
  452         scb->hashkey = KVTOPHYS(scb);
  453         hashnum = SCB_HASH(scb->hashkey);
  454         scb->nexthash = sc->sc_scbhash[hashnum];
  455         sc->sc_scbhash[hashnum] = scb;
  456         wds_reset_scb(sc, scb);
  457 }
  458 
  459 /*
  460  * Get a free scb
  461  */
  462 void *
  463 wds_scb_alloc(void *xsc)
  464 {
  465         struct wds_softc *sc = xsc;
  466         struct wds_scb *scb;
  467 
  468         mtx_enter(&sc->sc_scb_mtx);
  469         scb = TAILQ_FIRST(&sc->sc_free_scb);
  470         if (scb) {
  471                 TAILQ_REMOVE(&sc->sc_free_scb, scb, chain);
  472                 scb->flags |= SCB_ALLOC;
  473         }
  474         mtx_leave(&sc->sc_scb_mtx);
  475 
  476         return (scb);
  477 }
  478 
  479 struct wds_buf *
  480 wds_get_buf(struct wds_softc *sc, int flags)
  481 {
  482         struct wds_buf *buf;
  483         int s;
  484 
  485         s = splbio();
  486 
  487         for (;;) {
  488                 buf = TAILQ_FIRST(&wds_free_buffer);
  489                 if (buf) {
  490                         TAILQ_REMOVE(&wds_free_buffer, buf, chain);
  491                         break;
  492                 }
  493                 if ((flags & SCSI_NOSLEEP) != 0)
  494                         goto out;
  495                 tsleep_nsec(&wds_free_buffer, PRIBIO, "wdsbuf", INFSLP);
  496         }
  497 
  498         buf->busy = 1;
  499 
  500 out:
  501         splx(s);
  502         return (buf);
  503 }
  504 
  505 struct wds_scb *
  506 wds_scb_phys_kv(struct wds_softc *sc, u_long scb_phys)
  507 {
  508         int hashnum = SCB_HASH(scb_phys);
  509         struct wds_scb *scb = sc->sc_scbhash[hashnum];
  510 
  511         while (scb) {
  512                 if (scb->hashkey == scb_phys)
  513                         break;
  514                 /* XXX Check to see if it matches the sense command block. */
  515                 if (scb->hashkey == (scb_phys - sizeof(struct wds_cmd)))
  516                         break;
  517                 scb = scb->nexthash;
  518         }
  519         return scb;
  520 }
  521 
  522 /*
  523  * Queue a SCB to be sent to the controller, and send it if possible.
  524  */
  525 void
  526 wds_queue_scb(struct wds_softc *sc, struct wds_scb *scb)
  527 {
  528         TAILQ_INSERT_TAIL(&sc->sc_waiting_scb, scb, chain);
  529         wds_start_scbs(sc);
  530 }
  531 
  532 /*
  533  * Garbage collect mailboxes that are no longer in use.
  534  */
  535 void
  536 wds_collect_mbo(struct wds_softc *sc)
  537 {
  538         struct wds_mbx_out *wmbo;       /* Mail Box Out pointer */
  539 #ifdef WDSDIAG
  540         struct wds_scb *scb;
  541 #endif
  542 
  543         wmbo = wmbx->cmbo;
  544 
  545         while (sc->sc_mbofull > 0) {
  546                 if (wmbo->cmd != WDS_MBO_FREE)
  547                         break;
  548 
  549 #ifdef WDSDIAG
  550                 scb = wds_scb_phys_kv(sc, phystol(wmbo->scb_addr));
  551                 scb->flags &= ~SCB_SENDING;
  552 #endif
  553 
  554                 --sc->sc_mbofull;
  555                 wds_nextmbx(wmbo, wmbx, mbo);
  556         }
  557 
  558         wmbx->cmbo = wmbo;
  559 }
  560 
  561 /*
  562  * Send as many SCBs as we have empty mailboxes for.
  563  */
  564 void
  565 wds_start_scbs(struct wds_softc *sc)
  566 {
  567         struct wds_mbx_out *wmbo;       /* Mail Box Out pointer */
  568         struct wds_scb *scb;
  569         u_char c;
  570 
  571         wmbo = wmbx->tmbo;
  572 
  573         while ((scb = TAILQ_FIRST(&sc->sc_waiting_scb)) != NULL) {
  574                 if (sc->sc_mbofull >= WDS_MBX_SIZE) {
  575                         wds_collect_mbo(sc);
  576                         if (sc->sc_mbofull >= WDS_MBX_SIZE) {
  577                                 c = WDSC_IRQMFREE;
  578                                 wds_cmd(sc, &c, sizeof c);
  579                                 break;
  580                         }
  581                 }
  582 
  583                 TAILQ_REMOVE(&sc->sc_waiting_scb, scb, chain);
  584 #ifdef WDSDIAG
  585                 scb->flags |= SCB_SENDING;
  586 #endif
  587                 timeout_set(&scb->xs->stimeout, wds_timeout, scb);
  588 
  589                 /* Link scb to mbo. */
  590                 if (scb->flags & SCB_SENSE)
  591                         ltophys(KVTOPHYS(&scb->sense), wmbo->scb_addr);
  592                 else
  593                         ltophys(KVTOPHYS(&scb->cmd), wmbo->scb_addr);
  594                 /* XXX What about aborts? */
  595                 wmbo->cmd = WDS_MBO_START;
  596 
  597                 /* Tell the card to poll immediately. */
  598                 c = WDSC_MSTART(wmbo - wmbx->mbo);
  599                 wds_cmd(sc, &c, sizeof c);
  600 
  601                 if ((scb->flags & SCB_POLLED) == 0)
  602                         timeout_add_msec(&scb->xs->stimeout, scb->timeout);
  603 
  604                 ++sc->sc_mbofull;
  605                 wds_nextmbx(wmbo, wmbx, mbo);
  606         }
  607 
  608         wmbx->tmbo = wmbo;
  609 }
  610 
  611 /*
  612  * Process the result of a SCSI command.
  613  */
  614 void
  615 wds_done(struct wds_softc *sc, struct wds_scb *scb, u_int8_t stat)
  616 {
  617         struct scsi_xfer *xs = scb->xs;
  618 
  619         /* XXXXX */
  620 
  621         /* Don't release the SCB if it was an internal command. */
  622         if (xs == 0) {
  623                 scb->flags |= SCB_DONE;
  624                 return;
  625         }
  626 
  627         /* Sense handling. */
  628         if (xs->error == XS_SENSE) {
  629                 bcopy(&scb->sense_data, &xs->sense, sizeof (struct scsi_sense_data));
  630         } else {
  631                 if (xs->error == XS_NOERROR) {
  632                         /* If all went well, or an error is acceptable. */
  633                         if (stat == WDS_MBI_OK) {
  634                                 /* OK, set the result */
  635                                 xs->resid = 0;
  636                         } else {
  637                                 /* Check the mailbox status. */
  638                                 switch (stat) {
  639                                 case WDS_MBI_OKERR:
  640                                         /* SCSI error recorded in scb, counts as WDS_MBI_OK */
  641                                         switch (scb->cmd.venderr) {
  642                                         case 0x00:
  643                                                 printf("%s: Is this an error?\n", sc->sc_dev.dv_xname);
  644                                                 xs->error = XS_DRIVER_STUFFUP; /* Experiment */
  645                                                 break;
  646                                         case 0x01:
  647                                                 /*printf("%s: OK, see SCSI error field.\n", sc->sc_dev.dv_xname);*/
  648                                                 if (scb->cmd.stat == SCSI_CHECK) {
  649                                                         /* Do sense. */
  650                                                         wds_sense (sc, scb);
  651                                                         return;
  652                                                 } else if (scb->cmd.stat == SCSI_BUSY) {
  653                                                         xs->error = XS_BUSY;
  654                                                 }
  655                                                 break;
  656                                         case 0x40:
  657                                                 /*printf("%s: DMA underrun!\n", sc->sc_dev.dv_xname);*/
  658                                                 /* Hits this if the target returns fewer that datalen bytes (eg my CD-ROM,
  659                                                 which returns a short version string, or if DMA is turned off etc. */
  660                                                 xs->resid = 0;
  661                                                 break;
  662                                         default:
  663                                                 printf("%s: VENDOR ERROR %02x, scsi %02x\n", sc->sc_dev.dv_xname, scb->cmd.venderr, scb->cmd.stat);
  664                                                 xs->error = XS_DRIVER_STUFFUP; /* Experiment */
  665                                                 break;
  666                                         }
  667                                         break;
  668                                 case WDS_MBI_ETIME:
  669                                         /*
  670                                          * The documentation isn't clear on
  671                                          * what conditions might generate this,
  672                                          * but selection timeouts are the only
  673                                          * one I can think of.
  674                                          */
  675                                         xs->error = XS_SELTIMEOUT;
  676                                         break;
  677                                 case WDS_MBI_ERESET:
  678                                 case WDS_MBI_ETARCMD:
  679                                 case WDS_MBI_ERESEL:
  680                                 case WDS_MBI_ESEL:
  681                                 case WDS_MBI_EABORT:
  682                                 case WDS_MBI_ESRESET:
  683                                 case WDS_MBI_EHRESET:
  684                                         xs->error = XS_DRIVER_STUFFUP;
  685                                         break;
  686                                 }
  687                         }
  688                 } /* else sense */
  689 
  690                 if (NEEDBUFFER(sc) && xs->datalen) {
  691                         if (xs->flags & SCSI_DATA_IN)
  692                                 bcopy(scb->buf->data, xs->data, xs->datalen);
  693                 }
  694         } /* XS_NOERROR */
  695 
  696         scsi_done(xs);
  697 }
  698 
  699 int
  700 wds_find(struct isa_attach_args *ia, struct wds_softc *sc)
  701 {
  702         bus_space_tag_t iot = ia->ia_iot;
  703         bus_space_handle_t ioh = ia->ia_ioh;
  704         u_char c;
  705         int i;
  706 
  707         /*
  708          * Sending a command causes the CMDRDY bit to clear.
  709          */
  710         c = bus_space_read_1(iot, ioh, WDS_STAT);
  711         for (i = 0; i < 4; i++) {
  712                 if ((bus_space_read_1(iot, ioh, WDS_STAT) & WDSS_RDY) != 0)
  713                         goto ready;
  714                 delay(10);
  715         }
  716         return (0);
  717 
  718 ready:
  719         bus_space_write_1(iot, ioh, WDS_CMD, WDSC_NOOP);
  720         if (bus_space_read_1(iot, ioh, WDS_STAT) & WDSS_RDY)
  721                 return (0);
  722 
  723         bus_space_write_1(iot, ioh, WDS_HCR, WDSH_SCSIRESET|WDSH_ASCRESET);
  724         delay(10000);
  725         bus_space_write_1(iot, ioh, WDS_HCR, 0x00);
  726         delay(500000);
  727         wds_wait(iot, ioh, WDS_STAT, WDSS_RDY, WDSS_RDY);
  728         if (bus_space_read_1(iot, ioh, WDS_IRQSTAT) != 1)
  729                 if (bus_space_read_1(iot, ioh, WDS_IRQSTAT) != 7)
  730                         printf("%s: failed reset!!! %2x\n",
  731                             sc ? sc->sc_dev.dv_xname : "wds?",
  732                             bus_space_read_1(iot, ioh, WDS_IRQSTAT));
  733 
  734         if ((bus_space_read_1(iot, ioh, WDS_STAT) & (WDSS_RDY)) != WDSS_RDY) {
  735                 printf("%s: waiting for controller to become ready.",
  736                     sc ? sc->sc_dev.dv_xname : "wds?");
  737                 for (i = 0; i < 20; i++) {
  738                         if ((bus_space_read_1(iot, ioh, WDS_STAT) &
  739                             (WDSS_RDY)) == WDSS_RDY)
  740                                 break;
  741                         printf(".");
  742                         delay(10000);
  743                 }
  744                 if ((bus_space_read_1(iot, ioh, WDS_STAT) & (WDSS_RDY)) !=
  745                     WDSS_RDY) {
  746                         printf(" failed\n");
  747                         return (0);
  748                 }
  749                 printf("\n");
  750         }
  751 
  752         if (sc != NULL) {
  753                 /* XXX Can we do this better? */
  754                 /* who are we on the scsi bus? */
  755                 sc->sc_scsi_dev = 7;
  756 
  757                 sc->sc_iot = iot;
  758                 sc->sc_ioh = ioh;
  759                 sc->sc_irq = ia->ia_irq;
  760                 sc->sc_drq = ia->ia_drq;
  761         }
  762 
  763         return (1);
  764 }
  765 
  766 /*
  767  * Initialise the board and driver.
  768  */
  769 void
  770 wds_init(struct wds_softc *sc)
  771 {
  772         bus_space_tag_t iot = sc->sc_iot;
  773         bus_space_handle_t ioh = sc->sc_ioh;
  774         struct wds_setup init;
  775         u_char c;
  776         int i;
  777 
  778         /*
  779          * Set up initial mail box for round-robin operation.
  780          */
  781         for (i = 0; i < WDS_MBX_SIZE; i++) {
  782                 wmbx->mbo[i].cmd = WDS_MBO_FREE;
  783                 wmbx->mbi[i].stat = WDS_MBI_FREE;
  784         }
  785         wmbx->cmbo = wmbx->tmbo = &wmbx->mbo[0];
  786         wmbx->tmbi = &wmbx->mbi[0];
  787         sc->sc_mbofull = 0;
  788 
  789         /* Clear the buffers. */
  790         TAILQ_INIT(&wds_free_buffer);
  791         for (i = 0; i < BUFCNT; i++) {
  792                 wds_buffer[i].busy = 0;
  793                 TAILQ_INSERT_HEAD(&wds_free_buffer, &wds_buffer[i], chain);
  794         }
  795 
  796         init.opcode = WDSC_INIT;
  797         init.scsi_id = sc->sc_scsi_dev;
  798         /* Record scsi id of controller for use in scsi_attach */
  799         sc->sc_scsi_dev = init.scsi_id;
  800         init.buson_t = 48;
  801         init.busoff_t = 24;
  802         init.xx = 0;
  803         ltophys(KVTOPHYS(wmbx), init.mbaddr);
  804         init.nomb = init.nimb = WDS_MBX_SIZE;
  805         wds_cmd(sc, (u_char *)&init, sizeof init);
  806 
  807         wds_wait(iot, ioh, WDS_STAT, WDSS_INIT, WDSS_INIT);
  808 
  809         c = WDSC_DISUNSOL;
  810         wds_cmd(sc, &c, sizeof c);
  811 
  812         bus_space_write_1(iot, ioh, WDS_HCR, WDSH_DRQEN);
  813 }
  814 
  815 /*
  816  * Read the board's firmware revision information.
  817  */
  818 void
  819 wds_inquire_setup_information(struct wds_softc *sc)
  820 {
  821         struct wds_scb *scb;
  822         u_char *j;
  823         int s;
  824 
  825         scb = scsi_io_get(&sc->sc_iopool, SCSI_NOSLEEP);
  826         if (scb == NULL) {
  827                 printf("%s: no request slot available in getvers()!\n",
  828                     sc->sc_dev.dv_xname);
  829                 return;
  830         }
  831         scb->xs = NULL;
  832         scb->timeout = 40;
  833 
  834         bzero(&scb->cmd, sizeof scb->cmd);
  835         scb->cmd.write = 0x80;
  836         scb->cmd.opcode = WDSX_GETFIRMREV;
  837 
  838         /* Will poll card, await result. */
  839         bus_space_write_1(sc->sc_iot, sc->sc_ioh, WDS_HCR, WDSH_DRQEN);
  840         scb->flags |= SCB_POLLED;
  841 
  842         s = splbio();
  843         wds_queue_scb(sc, scb);
  844         splx(s);
  845 
  846         if (wds_ipoll(sc, scb, scb->timeout))
  847                 goto out;
  848 
  849         /* Print the version number. */
  850         printf(": version %x.%02x ", scb->cmd.targ, scb->cmd.scb.opcode);
  851         sc->sc_revision = (scb->cmd.targ << 8) | scb->cmd.scb.opcode;
  852         /* Print out the version string. */
  853         j = 2 + &(scb->cmd.targ);
  854         while ((*j >= 32) && (*j < 128)) {
  855                 printf("%c", *j);
  856                 j++;
  857         }
  858 
  859 out:
  860         printf("\n");
  861         scsi_io_put(&sc->sc_iopool, scb);
  862 }
  863 
  864 /*
  865  * Send a SCSI command.
  866  */
  867 void
  868 wds_scsi_cmd(struct scsi_xfer *xs)
  869 {
  870         struct scsi_link *sc_link = xs->sc_link;
  871         struct wds_softc *sc = sc_link->bus->sb_adapter_softc;
  872         bus_space_tag_t iot = sc->sc_iot;
  873         bus_space_handle_t ioh = sc->sc_ioh;
  874         struct wds_scb *scb;
  875         struct wds_scat_gath *sg;
  876         int seg;
  877         u_long thiskv, thisphys, nextphys;
  878         int bytes_this_seg, bytes_this_page, datalen, flags;
  879         int s;
  880 
  881         if (xs->flags & SCSI_RESET) {
  882                 /* XXX Fix me! */
  883                 printf("%s: reset!\n", sc->sc_dev.dv_xname);
  884                 wds_init(sc);
  885                 scsi_done(xs);
  886                 return;
  887         }
  888 
  889         flags = xs->flags;
  890         scb = xs->io;
  891         scb->xs = xs;
  892         scb->timeout = xs->timeout;
  893 
  894         /* Zero out the command structure. */
  895         bzero(&scb->cmd, sizeof scb->cmd);
  896         bcopy(&xs->cmd, &scb->cmd.scb, xs->cmdlen < 12 ? xs->cmdlen : 12);
  897 
  898         /* Set up some of the command fields. */
  899         scb->cmd.targ = (xs->sc_link->target << 5) | xs->sc_link->lun;
  900 
  901         /* NOTE: cmd.write may be OK as 0x40 (disable direction checking)
  902          * on boards other than the WD-7000V-ASE. Need this for the ASE:
  903          */
  904         scb->cmd.write = (xs->flags & SCSI_DATA_IN) ? 0x80 : 0x00;
  905 
  906         if (!NEEDBUFFER(sc) && xs->datalen) {
  907                 sg = scb->scat_gath;
  908                 seg = 0;
  909 
  910                 /*
  911                  * Set up the scatter-gather block.
  912                  */
  913 #ifdef WDSDEBUG
  914                 printf("%s: %d @%p:- ", sc->sc_dev.dv_xname, xs->datalen, xs->data);
  915 #endif
  916 
  917                 datalen = xs->datalen;
  918                 thiskv = (int)xs->data;
  919                 thisphys = KVTOPHYS(xs->data);
  920 
  921                 while (datalen && seg < WDS_NSEG) {
  922                         bytes_this_seg = 0;
  923 
  924                         /* put in the base address */
  925                         ltophys(thisphys, sg->seg_addr);
  926 
  927 #ifdef WDSDEBUG
  928                         printf("0x%lx", thisphys);
  929 #endif
  930 
  931                         /* do it at least once */
  932                         nextphys = thisphys;
  933                         while (datalen && thisphys == nextphys) {
  934                                 /*
  935                                  * This page is contiguous (physically)
  936                                  * with the last, just extend the
  937                                  * length
  938                                  */
  939                                 /* check it fits on the ISA bus */
  940                                 if (thisphys > 0xFFFFFF) {
  941                                         printf("%s: DMA beyond"
  942                                                 " end of ISA\n",
  943                                                 sc->sc_dev.dv_xname);
  944                                         goto bad;
  945                                 }
  946                                 /* how far to the end of the page */
  947                                 nextphys = (thisphys & ~PGOFSET) + NBPG;
  948                                 bytes_this_page = nextphys - thisphys;
  949                                 /**** or the data ****/
  950                                 bytes_this_page = min(bytes_this_page,
  951                                                       datalen);
  952                                 bytes_this_seg += bytes_this_page;
  953                                 datalen -= bytes_this_page;
  954 
  955                                 /* get more ready for the next page */
  956                                 thiskv = (thiskv & ~PGOFSET) + NBPG;
  957                                 if (datalen)
  958                                         thisphys = KVTOPHYS(thiskv);
  959                         }
  960                         /*
  961                          * next page isn't contiguous, finish the seg
  962                          */
  963 #ifdef WDSDEBUG
  964                         printf("(0x%x)", bytes_this_seg);
  965 #endif
  966                         ltophys(bytes_this_seg, sg->seg_len);
  967                         sg++;
  968                         seg++;
  969                 }
  970 
  971 #ifdef WDSDEBUG
  972                 printf("\n");
  973 #endif
  974                 if (datalen) {
  975                         /*
  976                          * there's still data, must have run out of segs!
  977                          */
  978                         printf("%s: wds_scsi_cmd, more than %d dma segs\n",
  979                             sc->sc_dev.dv_xname, WDS_NSEG);
  980                         goto bad;
  981                 }
  982                 scb->cmd.opcode = WDSX_SCSISG;
  983                 ltophys(KVTOPHYS(scb->scat_gath), scb->cmd.data);
  984                 ltophys(seg * sizeof(struct wds_scat_gath), scb->cmd.len);
  985         } else if (xs->datalen > 0) {
  986                 /* The board is an ASC or ASE. Do not use scatter/gather. */
  987                 if (xs->datalen > BUFLEN) {
  988                         printf("%s: wds_scsi_cmd, I/O too large for bounce buffer\n",
  989                             sc->sc_dev.dv_xname);
  990                         goto bad;
  991                 }
  992                 if (xs->flags & SCSI_DATA_OUT)
  993                         bcopy(xs->data, scb->buf->data, xs->datalen);
  994                 else
  995                         bzero(scb->buf->data, xs->datalen);
  996                 scb->cmd.opcode = WDSX_SCSICMD;
  997                 ltophys(KVTOPHYS(scb->buf->data), scb->cmd.data);
  998                 ltophys(xs->datalen, scb->cmd.len);
  999         } else {
 1000                 scb->cmd.opcode = WDSX_SCSICMD;
 1001                 ltophys(0, scb->cmd.data);
 1002                 ltophys(0, scb->cmd.len);
 1003         }
 1004 
 1005         scb->cmd.stat = 0x00;
 1006         scb->cmd.venderr = 0x00;
 1007         ltophys(0, scb->cmd.link);
 1008 
 1009         /* XXX Do we really want to do this? */
 1010         if (flags & SCSI_POLL) {
 1011                 /* Will poll card, await result. */
 1012                 bus_space_write_1(iot, ioh, WDS_HCR, WDSH_DRQEN);
 1013                 scb->flags |= SCB_POLLED;
 1014         } else {
 1015                 /* Will send command, let interrupt routine handle result. */
 1016                 bus_space_write_1(iot, ioh, WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
 1017         }
 1018 
 1019         s = splbio();
 1020         wds_queue_scb(sc, scb);
 1021 
 1022         splx(s);
 1023 
 1024         if ((flags & SCSI_POLL) == 0)
 1025                 return;
 1026 
 1027         if (wds_poll(sc, xs, scb->timeout)) {
 1028                 wds_timeout(scb);
 1029                 if (wds_poll(sc, xs, scb->timeout))
 1030                         wds_timeout(scb);
 1031         }
 1032         return;
 1033 
 1034 bad:
 1035         xs->error = XS_DRIVER_STUFFUP;
 1036 }
 1037 
 1038 /*
 1039  * Send a sense request.
 1040  */
 1041 void
 1042 wds_sense(struct wds_softc *sc, struct wds_scb *scb)
 1043 {
 1044         struct scsi_xfer *xs = scb->xs;
 1045         struct scsi_sense *ss = (void *)&scb->sense.scb;
 1046         int s;
 1047 
 1048         /* XXXXX */
 1049 
 1050         /* Send sense request SCSI command. */
 1051         xs->error = XS_SENSE;
 1052         scb->flags |= SCB_SENSE;
 1053 
 1054         /* First, save the return values */
 1055         if (NEEDBUFFER(sc) && xs->datalen) {
 1056                 if (xs->flags & SCSI_DATA_IN)
 1057                         bcopy(scb->buf->data, xs->data, xs->datalen);
 1058         }
 1059 
 1060         /* Next, setup a request sense command block */
 1061         bzero(ss, sizeof(*ss));
 1062         ss->opcode = REQUEST_SENSE;
 1063         ss->byte2 = xs->sc_link->lun << 5;
 1064         ss->length = sizeof(struct scsi_sense_data);
 1065 
 1066         /* Set up some of the command fields. */
 1067         scb->sense.targ = scb->cmd.targ;
 1068         scb->sense.write = 0x80;
 1069         scb->sense.opcode = WDSX_SCSICMD;
 1070         ltophys(KVTOPHYS(&scb->sense_data), scb->sense.data);
 1071         ltophys(sizeof(struct scsi_sense_data), scb->sense.len);
 1072 
 1073         s = splbio();
 1074         wds_queue_scb(sc, scb);
 1075         splx(s);
 1076 
 1077         /*
 1078          * There's no reason for us to poll here.  There are two cases:
 1079          * 1) If it's a polling operation, then we're called from the interrupt
 1080          *    handler, and we return and continue polling.
 1081          * 2) If it's an interrupt-driven operation, then it gets completed
 1082          *    later on when the REQUEST SENSE finishes.
 1083          */
 1084 }
 1085 
 1086 /*
 1087  * Poll a particular unit, looking for a particular scb
 1088  */
 1089 int
 1090 wds_poll(struct wds_softc *sc, struct scsi_xfer *xs,  int count)
 1091 {
 1092         bus_space_tag_t iot = sc->sc_iot;
 1093         bus_space_handle_t ioh = sc->sc_ioh;
 1094         int s;
 1095 
 1096         /* timeouts are in msec, so we loop in 1000 usec cycles */
 1097         while (count) {
 1098                 /*
 1099                  * If we had interrupts enabled, would we
 1100                  * have got an interrupt?
 1101                  */
 1102                 if (bus_space_read_1(iot, ioh, WDS_STAT) & WDSS_IRQ) {
 1103                         s = splbio();
 1104                         wdsintr(sc);
 1105                         splx(s);
 1106                 }
 1107                 if (xs->flags & ITSDONE)
 1108                         return 0;
 1109                 delay(1000);    /* only happens in boot so ok */
 1110                 count--;
 1111         }
 1112         return 1;
 1113 }
 1114 
 1115 /*
 1116  * Poll a particular unit, looking for a particular scb
 1117  */
 1118 int
 1119 wds_ipoll(struct wds_softc *sc, struct wds_scb *scb, int count)
 1120 {
 1121         bus_space_tag_t iot = sc->sc_iot;
 1122         bus_space_handle_t ioh = sc->sc_ioh;
 1123         int s;
 1124 
 1125         /* timeouts are in msec, so we loop in 1000 usec cycles */
 1126         while (count) {
 1127                 /*
 1128                  * If we had interrupts enabled, would we
 1129                  * have got an interrupt?
 1130                  */
 1131                 if (bus_space_read_1(iot, ioh, WDS_STAT) & WDSS_IRQ) {
 1132                         s = splbio();
 1133                         wdsintr(sc);
 1134                         splx(s);
 1135                 }
 1136                 if (scb->flags & SCB_DONE)
 1137                         return 0;
 1138                 delay(1000);    /* only happens in boot so ok */
 1139                 count--;
 1140         }
 1141         return 1;
 1142 }
 1143 
 1144 void
 1145 wds_timeout(void *arg)
 1146 {
 1147         struct wds_scb *scb = arg;
 1148         struct scsi_xfer *xs;
 1149         struct scsi_link *sc_link;
 1150         struct wds_softc *sc;
 1151         int s;
 1152 
 1153         s = splbio();
 1154         xs = scb->xs;
 1155         sc_link = xs->sc_link;
 1156         sc = sc_link->bus->sb_adapter_softc;
 1157 
 1158         sc_print_addr(sc_link);
 1159         printf("timed out");
 1160 
 1161 #ifdef WDSDIAG
 1162         /*
 1163          * If The scb's mbx is not free, then the board has gone south?
 1164          */
 1165         wds_collect_mbo(sc);
 1166         if (scb->flags & SCB_SENDING)
 1167                 panic("%s: not taking commands!", sc->sc_dev.dv_xname);
 1168 #endif
 1169 
 1170         /*
 1171          * If it has been through before, then
 1172          * a previous abort has failed, don't
 1173          * try abort again
 1174          */
 1175         if (scb->flags & SCB_ABORT) {
 1176                 /* abort timed out */
 1177                 printf(" AGAIN\n");
 1178                 /* XXX Must reset! */
 1179         } else {
 1180                 /* abort the operation that has timed out */
 1181                 printf("\n");
 1182                 scb->xs->error = XS_TIMEOUT;
 1183                 scb->timeout = WDS_ABORT_TIMEOUT;
 1184                 scb->flags |= SCB_ABORT;
 1185                 wds_queue_scb(sc, scb);
 1186         }
 1187 
 1188         splx(s);
 1189 }

Cache object: c0cb5852c5c655cdc0a28a7c6cb8f7a1


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.