1/* $NetBSD: scsi.c,v 1.9 2007/03/05 18:06:09 he Exp $ */ 2/* 3 * Copyright (c) 1994, 1997 Rolf Grossmann 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Rolf Grossmann. 17 * 4. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/param.h> 33#include <next68k/dev/espreg.h> 34#include <dev/ic/ncr53c9xreg.h> 35#include <dev/scsipi/scsi_message.h> 36#if 0 37#include <next/next/prominfo.h> 38#else 39#include <next68k/next68k/nextrom.h> 40#endif 41#include "scsireg.h" 42#include "dmareg.h" 43#include "scsivar.h" 44 45#include <lib/libsa/stand.h> 46 47struct scsi_softc scsi_softc, *sc = &scsi_softc; 48char the_dma_buffer[MAX_DMASIZE+DMA_ENDALIGNMENT], *dma_buffer; 49 50int scsi_msgin(void); 51int dma_start(char *addr, int len); 52int dma_done(void); 53 54void scsi_init(void); 55void scsierror(char *error); 56short scsi_getbyte(volatile uint8_t *sr); 57int scsi_wait_for_intr(void); 58int scsiicmd(char target, char lun, 59 u_char *cbuf, int clen, char *addr, int *len); 60 61#define NDPRINTF(x) 62#define PRINTF(x) 63/* printf x; */ 64#ifdef xSCSI_DEBUG 65#define DPRINTF(x) printf x; 66#else 67#define DPRINTF(x) 68#endif 69 70void 71scsi_init(void) 72{ 73 volatile uint8_t *sr; 74 struct dma_dev *dma; 75 76 sr = P_SCSI; 77 dma = (struct dma_dev *)P_SCSI_CSR; 78 79 dma_buffer = DMA_ALIGN(char *, the_dma_buffer); 80 81 P_FLOPPY[FLP_CTRL] &= ~FLC_82077_SEL; /* select SCSI chip */ 82 83 /* first reset DMA */ 84 dma->dd_csr = DMACSR_RESET; 85 DELAY(200); 86 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_RESET; 87 DELAY(10); 88 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB; 89 DELAY(10); 90 91 /* then reset the SCSI chip */ 92 sr[NCR_CMD] = NCRCMD_RSTCHIP; 93 sr[NCR_CMD] = NCRCMD_NOP; 94 DELAY(500); 95 96 /* now reset the SCSI bus */ 97 sr[NCR_CMD] = NCRCMD_RSTSCSI; 98 DELAY(4000000); /* XXX should be about 2-3 seconds at least */ 99 100 /* then reset the SCSI chip again and initialize it properly */ 101 sr[NCR_CMD] = NCRCMD_RSTCHIP; 102 sr[NCR_CMD] = NCRCMD_NOP; 103 DELAY(500); 104 sr[NCR_CFG1] = NCRCFG1_SLOW | NCRCFG1_BUSID; 105 sr[NCR_CFG2] = 0; 106 sr[NCR_CCF] = 4; /* S5RCLKCONV_FACTOR(20); */ 107 sr[NCR_TIMEOUT] = 152; /* S5RSELECT_TIMEOUT(20,250); */ 108 sr[NCR_SYNCOFF] = 0; 109 sr[NCR_SYNCTP] = 5; 110 /* 111 sc->sc_intrstatus = sr->s5r_intrstatus; 112 sc->sc_intrstatus = sr->s5r_intrstatus; 113 */ 114 sr[NCR_CFG1] = NCRCFG1_PARENB | NCRCFG1_BUSID; 115 116 sc->sc_state = SCSI_IDLE; 117} 118 119void 120scsierror(char *error) 121{ 122 printf("scsierror: %s.\n", error); 123} 124 125short 126scsi_getbyte(volatile uint8_t *sr) 127{ 128 if ((sr[NCR_FFLAG] & NCRFIFO_FF) == 0) 129 { 130 printf("getbyte: no data!\n"); 131 return -1; 132 } 133 return sr[NCR_FIFO]; 134} 135 136int 137scsi_wait_for_intr(void) 138{ 139#if 0 140 extern struct prominfo *pi; 141 volitle int = pi->pi_intrstat; /* ### use constant? */ 142#else 143 extern char *mg; 144#define MON(type, off) (*(type *)((u_int) (mg) + off)) 145 volatile int *intrstat = MON(volatile int *,MG_intrstat); 146#ifdef SCSI_DEBUG 147/* volatile int *intrmask = MON(volatile int *,MG_intrmask); */ 148#endif 149#endif 150 int count; 151 152 for(count = 0; count < SCSI_TIMEOUT; count++) { 153 NDPRINTF((" *intrstat = 0x%x\t*intrmask = 0x%x\n",*intrstat,*intrmask)); 154 155 if (*intrstat & SCSI_INTR) 156 return 0; 157 } 158 159 printf("scsiicmd: timed out.\n"); 160 return -1; 161} 162 163int 164scsiicmd(char target, char lun, 165 u_char *cbuf, int clen, 166 char *addr, int *len) 167{ 168 volatile uint8_t *sr; 169 int i; 170 171 DPRINTF(("scsiicmd: [%x, %d] -> %d (%lx, %d)\n",*cbuf, clen, 172 target, (long)addr, *len)); 173 sr = P_SCSI; 174 175 if (sc->sc_state != SCSI_IDLE) { 176 scsierror("scsiiscmd: bad state"); 177 return EIO; 178 } 179 sc->sc_result = 0; 180 181 /* select target */ 182 sr[NCR_CMD] = NCRCMD_FLUSH; 183 DELAY(10); 184 sr[NCR_SELID] = target; 185 sr[NCR_FIFO] = MSG_IDENTIFY(lun, 0); 186 for (i=0; i<clen; i++) 187 sr[NCR_FIFO] = cbuf[i]; 188 sr[NCR_CMD] = NCRCMD_SELATN; 189 sc->sc_state = SCSI_SELECTING; 190 191 while(sc->sc_state != SCSI_DONE) { 192 if (scsi_wait_for_intr()) /* maybe we'd better use real intrs ? */ 193 return EIO; 194 195 if (sc->sc_state == SCSI_DMA) 196 { 197 /* registers are not valid on DMA intr */ 198 sc->sc_status = sc->sc_seqstep = sc->sc_intrstatus = 0; 199 DPRINTF(("scsiicmd: DMA intr\n")); 200 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMARD; 201 } 202 203 /* scsi processing */ 204 sc->sc_status = sr[NCR_STAT]; 205 sc->sc_seqstep = sr[NCR_STEP]; 206 sc->sc_intrstatus = sr[NCR_INTR]; 207 redo: 208 DPRINTF(("scsiicmd: regs[intr=%x, stat=%x, step=%x]\n", 209 sc->sc_intrstatus, sc->sc_status, sc->sc_seqstep)); 210 211 if (sc->sc_intrstatus & NCRINTR_SBR) { 212 scsierror("scsi bus reset"); 213 return EIO; 214 } 215 216 if ((sc->sc_status & NCRSTAT_GE) 217 || (sc->sc_intrstatus & NCRINTR_ILL)) { 218 scsierror("software error"); 219 return EIO; 220 } 221 if (sc->sc_status & NCRSTAT_PE) 222 { 223 scsierror("parity error"); 224 return EIO; 225 } 226 227 switch(sc->sc_state) 228 { 229 case SCSI_SELECTING: 230 if (sc->sc_intrstatus & NCRINTR_DIS) 231 { 232 sc->sc_state = SCSI_IDLE; 233 return EUNIT; /* device not present */ 234 } 235 236#define NCRINTR_DONE (NCRINTR_BS | NCRINTR_FC) 237 if ((sc->sc_intrstatus & NCRINTR_DONE) != NCRINTR_DONE) 238 { 239 scsierror("selection failed"); 240 return EIO; 241 } 242 sc->sc_state = SCSI_HASBUS; 243 break; 244 case SCSI_HASBUS: 245 if (sc->sc_intrstatus & NCRINTR_DIS) 246 { 247 scsierror("target disconnected"); 248 return EIO; 249 } 250 break; 251 case SCSI_DMA: 252 if (sc->sc_intrstatus & NCRINTR_DIS) 253 { 254 scsierror("target disconnected"); 255 return EIO; 256 } 257 *len = dma_done(); 258 if (*len < 0) { 259 *len = 0; 260 return EIO; 261 } 262 /* continue; */ 263 sc->sc_status = sr[NCR_STAT]; 264 goto redo; 265 break; 266 case SCSI_CLEANUP: 267 if (sc->sc_intrstatus & NCRINTR_DIS) 268 { 269 sc->sc_state = SCSI_DONE; 270 continue; 271 } 272 DPRINTF(("hmm ... no disconnect on cleanup?\n")); 273 sc->sc_state = SCSI_DONE; /* maybe ... */ 274 break; 275 } 276 277 /* transfer information now */ 278 switch(sc->sc_status & NCRSTAT_PHASE) 279 { 280 case DATA_IN_PHASE: 281 sr[NCR_CMD] = NCRCMD_FLUSH; 282 if (dma_start(addr, *len) != 0) 283 return EIO; 284 break; 285 case DATA_OUT_PHASE: 286 scsierror("data out phase not implemented"); 287 return EIO; 288 case STATUS_PHASE: 289 DPRINTF(("status phase: ")); 290 sr[NCR_CMD] = NCRCMD_ICCS; 291 sc->sc_result = scsi_getbyte(sr); 292 DPRINTF(("status is 0x%x.\n", sc->sc_result)); 293 break; 294 case MSG_IN_PHASE: 295 if ((sc->sc_intrstatus & NCRINTR_BS) != 0) { 296 sr[NCR_CMD] = NCRCMD_FLUSH; 297 sr[NCR_CMD] = NCRCMD_TRANS; 298 } else 299 if (scsi_msgin() != 0) 300 return EIO; 301 break; 302 default: 303 DPRINTF(("phase not implemented: 0x%x.\n", 304 sc->sc_status & NCRSTAT_PHASE)); 305 scsierror("bad phase"); 306 return EIO; 307 } 308 } 309 310 sc->sc_state = SCSI_IDLE; 311 return -sc->sc_result; 312} 313 314int 315scsi_msgin(void) 316{ 317 volatile uint8_t *sr; 318 u_char msg; 319 320 sr = P_SCSI; 321 322 msg = scsi_getbyte(sr); 323 if (msg) 324 { 325 printf("unexpected msg: 0x%x.\n",msg); 326 return -1; 327 } 328 if ((sc->sc_intrstatus & NCRINTR_FC) == 0) 329 { 330 printf("not function complete.\n"); 331 return -1; 332 } 333 sc->sc_state = SCSI_CLEANUP; 334 sr[NCR_CMD] = NCRCMD_MSGOK; 335 return 0; 336} 337 338int 339dma_start(char *addr, int len) 340{ 341 volatile uint8_t *sr; 342 struct dma_dev *dma; 343 344 345 sr = P_SCSI; 346 dma = (struct dma_dev *)P_SCSI_CSR; 347 348 if (len > MAX_DMASIZE) 349 { 350 scsierror("DMA too long"); 351 return -1; 352 } 353 354 if (addr == NULL || len == 0) 355 { 356#if 0 /* I'd take that as an error in my code */ 357 DPRINTF(("hmm ... no DMA requested.\n")); 358 sr[NCR_TCL] = 0; 359 sr[NCR_TCM] = 1; 360 sr[NCR_CMD] = NCRCMD_NOP; 361 sr[NCR_CMD] = NCRCMD_DMA | NCRCMD_TRPAD; 362 return 0; 363#else 364 scsierror("unrequested DMA"); 365 return -1; 366#endif 367 } 368 369 PRINTF(("DMA start: %lx, %d byte.\n", (long)addr, len)); 370 371 DPRINTF(("dma_bufffer: start: 0x%lx end: 0x%lx \n", 372 (long)dma_buffer,(long)DMA_ENDALIGN(char *, dma_buffer+len))); 373 374 sc->dma_addr = addr; 375 sc->dma_len = len; 376 377 sr[NCR_TCL] = len & 0xff; 378 sr[NCR_TCM] = len >> 8; 379 sr[NCR_CMD] = NCRCMD_DMA | NCRCMD_NOP; 380 sr[NCR_CMD] = NCRCMD_DMA | NCRCMD_TRANS; 381 382#if 0 383 dma->dd_csr = DMACSR_READ | DMACSR_RESET; 384 dma->dd_next_initbuf = dma_buffer; 385 dma->dd_limit = DMA_ENDALIGN(char *, dma_buffer+len); 386 dma->dd_csr = DMACSR_READ | DMACSR_SETENABLE; 387#else 388 dma->dd_csr = 0; 389 dma->dd_csr = DMACSR_INITBUF | DMACSR_READ | DMACSR_RESET; 390 dma->dd_next = dma_buffer; 391 dma->dd_limit = DMA_ENDALIGN(char *, dma_buffer+len); 392 dma->dd_csr = DMACSR_READ | DMACSR_SETENABLE; 393#endif 394 395 sr[ESP_DCTL] = ESPDCTL_20MHZ|ESPDCTL_INTENB|ESPDCTL_DMAMOD|ESPDCTL_DMARD; 396 397 sc->sc_state = SCSI_DMA; 398 return 0; 399} 400 401int 402dma_done(void) 403{ 404 volatile uint8_t *sr; 405 struct dma_dev *dma; 406 int resid, state; 407 int flushcount = 0; 408 409 sr = P_SCSI; 410 dma = (struct dma_dev *)P_SCSI_CSR; 411 412 state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE 413 | DMACSR_SUPDATE | DMACSR_ENABLE); 414 415 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMARD; 416 resid = sr[NCR_TCM]<<8 | sr[NCR_TCL]; 417 DPRINTF(("DMA state = 0x%x, remain = %d.\n", state, resid)); 418 419 if (!(sr[NCR_FFLAG] & NCRFIFO_FF)) { 420 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD 421 | ESPDCTL_DMARD; 422 while (!(state & DMACSR_COMPLETE) && (state & DMACSR_ENABLE) && flushcount < 16) 423 { 424 425 DPRINTF(("DMA still enabled, flushing DCTL.\n")); 426 427 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD 428 | ESPDCTL_DMARD | ESPDCTL_FLUSH; 429 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD 430 | ESPDCTL_DMARD; 431 432 flushcount++; 433 state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE 434 | DMACSR_SUPDATE | DMACSR_ENABLE); 435 } 436 } 437 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB; 438 resid = (sr[NCR_TCM]<<8) + sr[NCR_TCL]; 439 440 dma->dd_csr = DMACSR_CLRCOMPLETE | DMACSR_RESET; 441 442 DPRINTF(("DMA done. remain = %d, state = 0x%x, fifo = 0x%x.\n", resid, state, sr[NCR_FFLAG] & NCRFIFO_FF)); 443 444 if (resid != 0) 445 { 446#if 1 447 printf("WARNING: unexpected %d characters remain in DMA\n",resid); 448 scsierror("DMA transfer incomplete"); 449 return -1; 450#endif 451 } 452 453 if (state & DMACSR_BUSEXC) 454 { 455#if 0 456 scsierror("DMA failed"); 457 return -1; 458#endif 459 } 460 461 sc->dma_len -= resid; 462 if (sc->dma_len < 0) 463 sc->dma_len = 0; 464 memcpy(sc->dma_addr, dma_buffer, sc->dma_len); 465 sc->sc_state = SCSI_HASBUS; 466 DPRINTF(("DMA done. got %d.\n", sc->dma_len)); 467 return sc->dma_len; 468 469 /* scsierror("DMA not completed\n"); */ 470 471 return 0; 472} 473