1 /* $NetBSD: scsi.c,v 1.15 2024/02/05 22:18:17 andvar 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 #include <next68k/next68k/nextrom.h> 37 #include "scsireg.h" 38 #include "dmareg.h" 39 #include "scsivar.h" 40 41 #include <lib/libsa/stand.h> 42 43 #include "samachdep.h" 44 45 struct scsi_softc scsi_softc, *sc = &scsi_softc; 46 char the_dma_buffer[MAX_DMASIZE+DMA_ENDALIGNMENT], *dma_buffer; 47 48 int scsi_msgin(void); 49 int dma_start(char *addr, int len); 50 int dma_done(void); 51 52 void scsierror(char *error); 53 short scsi_getbyte(volatile uint8_t *sr); 54 int scsi_wait_for_intr(void); 55 56 #define NDPRINTF(x) 57 #define PRINTF(x) 58 /* printf x; */ 59 #ifdef SCSI_DEBUG 60 #define DPRINTF(x) printf x; 61 #else 62 #define DPRINTF(x) 63 #endif 64 65 void 66 scsi_init(void) 67 { 68 volatile uint8_t *sr; 69 struct dma_dev *dma; 70 71 sr = P_SCSI; 72 dma = (struct dma_dev *)P_SCSI_CSR; 73 74 dma_buffer = DMA_ALIGN(char *, the_dma_buffer); 75 76 P_FLOPPY[FLP_CTRL] &= ~FLC_82077_SEL; /* select SCSI chip */ 77 78 /* first reset DMA */ 79 dma->dd_csr = DMACSR_RESET; 80 DELAY(200); 81 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_RESET; 82 DELAY(10); 83 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB; 84 DELAY(10); 85 86 /* then reset the SCSI chip */ 87 sr[NCR_CMD] = NCRCMD_RSTCHIP; 88 sr[NCR_CMD] = NCRCMD_NOP; 89 DELAY(500); 90 91 /* now reset the SCSI bus */ 92 sr[NCR_CMD] = NCRCMD_RSTSCSI; 93 /* wait 2 seconds after SCSI bus reset */ 94 DELAY(2 * 1000 * 1000); 95 96 /* then reset the SCSI chip again and initialize it properly */ 97 sr[NCR_CMD] = NCRCMD_RSTCHIP; 98 sr[NCR_CMD] = NCRCMD_NOP; 99 DELAY(500); 100 sr[NCR_CFG1] = NCRCFG1_SLOW | NCRCFG1_BUSID; 101 sr[NCR_CFG2] = 0; 102 sr[NCR_CCF] = 4; /* S5RCLKCONV_FACTOR(20); */ 103 sr[NCR_TIMEOUT] = 152; /* S5RSELECT_TIMEOUT(20,250); */ 104 sr[NCR_SYNCOFF] = 0; 105 sr[NCR_SYNCTP] = 5; 106 /* 107 sc->sc_intrstatus = sr->s5r_intrstatus; 108 sc->sc_intrstatus = sr->s5r_intrstatus; 109 */ 110 sr[NCR_CFG1] = NCRCFG1_PARENB | NCRCFG1_BUSID; 111 112 sc->sc_state = SCSI_IDLE; 113 } 114 115 void 116 scsierror(char *error) 117 { 118 printf("scsierror: %s.\n", error); 119 } 120 121 short 122 scsi_getbyte(volatile uint8_t *sr) 123 { 124 if ((sr[NCR_FFLAG] & NCRFIFO_FF) == 0) 125 { 126 printf("getbyte: no data!\n"); 127 return -1; 128 } 129 return sr[NCR_FIFO]; 130 } 131 132 int 133 scsi_wait_for_intr(void) 134 { 135 #define MON(type, off) (*(type *)((u_int) (mg) + off)) 136 volatile int *intrstat = MON(volatile int *,MG_intrstat); 137 #ifdef SCSI_DEBUG 138 /* volatile int *intrmask = MON(volatile int *,MG_intrmask); */ 139 #endif 140 int count; 141 142 for(count = 0; count < SCSI_TIMEOUT; count++) { 143 NDPRINTF((" *intrstat = 0x%x\t*intrmask = 0x%x\n",*intrstat,*intrmask)); 144 145 if (*intrstat & SCSI_INTR) 146 return 0; 147 } 148 149 printf("scsiicmd: timed out.\n"); 150 return -1; 151 } 152 153 int 154 scsiicmd(char target, char lun, 155 u_char *cbuf, int clen, 156 char *addr, int *len) 157 { 158 volatile uint8_t *sr; 159 int i; 160 161 DPRINTF(("scsiicmd: [%x, %d] -> %d (%lx, %d)\n",*cbuf, clen, 162 target, (long)addr, *len)); 163 sr = P_SCSI; 164 165 if (sc->sc_state != SCSI_IDLE) { 166 scsierror("scsiiscmd: bad state"); 167 return EIO; 168 } 169 sc->sc_result = 0; 170 171 /* select target */ 172 sr[NCR_CMD] = NCRCMD_FLUSH; 173 DELAY(10); 174 sr[NCR_SELID] = target; 175 sr[NCR_FIFO] = MSG_IDENTIFY(lun, 0); 176 for (i=0; i<clen; i++) 177 sr[NCR_FIFO] = cbuf[i]; 178 sr[NCR_CMD] = NCRCMD_SELATN; 179 sc->sc_state = SCSI_SELECTING; 180 181 while(sc->sc_state != SCSI_DONE) { 182 if (scsi_wait_for_intr()) /* maybe we'd better use real intrs ? */ 183 return EIO; 184 185 if (sc->sc_state == SCSI_DMA) 186 { 187 /* registers are not valid on DMA intr */ 188 sc->sc_status = sc->sc_seqstep = sc->sc_intrstatus = 0; 189 DPRINTF(("scsiicmd: DMA intr\n")); 190 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMARD; 191 } 192 193 /* scsi processing */ 194 sc->sc_status = sr[NCR_STAT]; 195 sc->sc_seqstep = sr[NCR_STEP]; 196 sc->sc_intrstatus = sr[NCR_INTR]; 197 redo: 198 DPRINTF(("scsiicmd: regs[intr=%x, stat=%x, step=%x]\n", 199 sc->sc_intrstatus, sc->sc_status, sc->sc_seqstep)); 200 201 if (sc->sc_intrstatus & NCRINTR_SBR) { 202 scsierror("scsi bus reset"); 203 return EIO; 204 } 205 206 if ((sc->sc_status & NCRSTAT_GE) 207 || (sc->sc_intrstatus & NCRINTR_ILL)) { 208 scsierror("software error"); 209 return EIO; 210 } 211 if (sc->sc_status & NCRSTAT_PE) 212 { 213 scsierror("parity error"); 214 return EIO; 215 } 216 217 switch(sc->sc_state) 218 { 219 case SCSI_SELECTING: 220 if (sc->sc_intrstatus & NCRINTR_DIS) 221 { 222 sc->sc_state = SCSI_IDLE; 223 return EUNIT; /* device not present */ 224 } 225 226 #define NCRINTR_DONE (NCRINTR_BS | NCRINTR_FC) 227 if ((sc->sc_intrstatus & NCRINTR_DONE) != NCRINTR_DONE) 228 { 229 scsierror("selection failed"); 230 return EIO; 231 } 232 sc->sc_state = SCSI_HASBUS; 233 break; 234 case SCSI_HASBUS: 235 if (sc->sc_intrstatus & NCRINTR_DIS) 236 { 237 scsierror("target disconnected"); 238 return EIO; 239 } 240 break; 241 case SCSI_DMA: 242 if (sc->sc_intrstatus & NCRINTR_DIS) 243 { 244 scsierror("target disconnected"); 245 return EIO; 246 } 247 *len = dma_done(); 248 if (*len < 0) { 249 *len = 0; 250 return EIO; 251 } 252 /* continue; */ 253 sc->sc_status = sr[NCR_STAT]; 254 goto redo; 255 break; 256 case SCSI_CLEANUP: 257 if (sc->sc_intrstatus & NCRINTR_DIS) 258 { 259 sc->sc_state = SCSI_DONE; 260 continue; 261 } 262 DPRINTF(("hmm ... no disconnect on cleanup?\n")); 263 sc->sc_state = SCSI_DONE; /* maybe ... */ 264 break; 265 } 266 267 /* transfer information now */ 268 switch(sc->sc_status & NCRSTAT_PHASE) 269 { 270 case DATA_IN_PHASE: 271 sr[NCR_CMD] = NCRCMD_FLUSH; 272 if (dma_start(addr, *len) != 0) 273 return EIO; 274 break; 275 case DATA_OUT_PHASE: 276 scsierror("data out phase not implemented"); 277 return EIO; 278 case STATUS_PHASE: 279 DPRINTF(("status phase: ")); 280 sr[NCR_CMD] = NCRCMD_ICCS; 281 sc->sc_result = scsi_getbyte(sr); 282 DPRINTF(("status is 0x%x.\n", sc->sc_result)); 283 break; 284 case MSG_IN_PHASE: 285 if ((sc->sc_intrstatus & NCRINTR_BS) != 0) { 286 sr[NCR_CMD] = NCRCMD_FLUSH; 287 sr[NCR_CMD] = NCRCMD_TRANS; 288 } else 289 if (scsi_msgin() != 0) 290 return EIO; 291 break; 292 default: 293 DPRINTF(("phase not implemented: 0x%x.\n", 294 sc->sc_status & NCRSTAT_PHASE)); 295 scsierror("bad phase"); 296 return EIO; 297 } 298 } 299 300 sc->sc_state = SCSI_IDLE; 301 return -sc->sc_result; 302 } 303 304 int 305 scsi_msgin(void) 306 { 307 volatile uint8_t *sr; 308 u_char msg; 309 310 sr = P_SCSI; 311 312 msg = scsi_getbyte(sr); 313 if (msg) 314 { 315 printf("unexpected msg: 0x%x.\n",msg); 316 return -1; 317 } 318 if ((sc->sc_intrstatus & NCRINTR_FC) == 0) 319 { 320 printf("not function complete.\n"); 321 return -1; 322 } 323 sc->sc_state = SCSI_CLEANUP; 324 sr[NCR_CMD] = NCRCMD_MSGOK; 325 return 0; 326 } 327 328 int 329 dma_start(char *addr, int len) 330 { 331 volatile uint8_t *sr; 332 struct dma_dev *dma; 333 334 sr = P_SCSI; 335 dma = (struct dma_dev *)P_SCSI_CSR; 336 337 if (len > MAX_DMASIZE) 338 { 339 scsierror("DMA too long"); 340 return -1; 341 } 342 343 if (addr == NULL || len == 0) 344 { 345 #if 0 /* I'd take that as an error in my code */ 346 DPRINTF(("hmm ... no DMA requested.\n")); 347 sr[NCR_TCL] = 0; 348 sr[NCR_TCM] = 1; 349 sr[NCR_CMD] = NCRCMD_NOP; 350 sr[NCR_CMD] = NCRCMD_DMA | NCRCMD_TRPAD; 351 return 0; 352 #else 353 scsierror("unrequested DMA"); 354 return -1; 355 #endif 356 } 357 358 PRINTF(("DMA start: %lx, %d byte.\n", (long)addr, len)); 359 360 DPRINTF(("dma_buffer: start: 0x%lx end: 0x%lx \n", 361 (long)dma_buffer,(long)DMA_ENDALIGN(char *, dma_buffer+len))); 362 363 sc->dma_addr = addr; 364 sc->dma_len = len; 365 366 sr[NCR_TCL] = len & 0xff; 367 sr[NCR_TCM] = len >> 8; 368 sr[NCR_CMD] = NCRCMD_DMA | NCRCMD_NOP; 369 sr[NCR_CMD] = NCRCMD_DMA | NCRCMD_TRANS; 370 371 #if 0 372 dma->dd_csr = DMACSR_READ | DMACSR_RESET; 373 dma->dd_next_initbuf = dma_buffer; 374 dma->dd_limit = DMA_ENDALIGN(char *, dma_buffer+len); 375 dma->dd_csr = DMACSR_READ | DMACSR_SETENABLE; 376 #else 377 dma->dd_csr = 0; 378 dma->dd_csr = DMACSR_INITBUF | DMACSR_READ | DMACSR_RESET; 379 dma->dd_next = dma_buffer; 380 dma->dd_limit = DMA_ENDALIGN(char *, dma_buffer+len); 381 dma->dd_csr = DMACSR_READ | DMACSR_SETENABLE; 382 #endif 383 384 sr[ESP_DCTL] = ESPDCTL_20MHZ|ESPDCTL_INTENB|ESPDCTL_DMAMOD|ESPDCTL_DMARD; 385 386 sc->sc_state = SCSI_DMA; 387 return 0; 388 } 389 390 int 391 dma_done(void) 392 { 393 volatile uint8_t *sr; 394 struct dma_dev *dma; 395 int resid, state; 396 int flushcount = 0; 397 398 sr = P_SCSI; 399 dma = (struct dma_dev *)P_SCSI_CSR; 400 401 state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE 402 | DMACSR_SUPDATE | DMACSR_ENABLE); 403 404 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMARD; 405 resid = sr[NCR_TCM]<<8 | sr[NCR_TCL]; 406 DPRINTF(("DMA state = 0x%x, remain = %d.\n", state, resid)); 407 408 if (!(sr[NCR_FFLAG] & NCRFIFO_FF)) { 409 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD 410 | ESPDCTL_DMARD; 411 while (!(state & DMACSR_COMPLETE) && (state & DMACSR_ENABLE) && flushcount < 16) 412 { 413 414 DPRINTF(("DMA still enabled, flushing DCTL.\n")); 415 416 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD 417 | ESPDCTL_DMARD | ESPDCTL_FLUSH; 418 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB | ESPDCTL_DMAMOD 419 | ESPDCTL_DMARD; 420 421 flushcount++; 422 state = dma->dd_csr & (DMACSR_BUSEXC | DMACSR_COMPLETE 423 | DMACSR_SUPDATE | DMACSR_ENABLE); 424 } 425 } 426 sr[ESP_DCTL] = ESPDCTL_20MHZ | ESPDCTL_INTENB; 427 resid = (sr[NCR_TCM]<<8) + sr[NCR_TCL]; 428 429 dma->dd_csr = DMACSR_CLRCOMPLETE | DMACSR_RESET; 430 431 DPRINTF(("DMA done. remain = %d, state = 0x%x, fifo = 0x%x.\n", resid, state, sr[NCR_FFLAG] & NCRFIFO_FF)); 432 433 if (resid != 0) 434 { 435 #if 1 436 printf("WARNING: unexpected %d characters remain in DMA\n",resid); 437 scsierror("DMA transfer incomplete"); 438 return -1; 439 #endif 440 } 441 442 if (state & DMACSR_BUSEXC) 443 { 444 #if 0 445 scsierror("DMA failed"); 446 return -1; 447 #endif 448 } 449 450 sc->dma_len -= resid; 451 if (sc->dma_len < 0) 452 sc->dma_len = 0; 453 memcpy(sc->dma_addr, dma_buffer, sc->dma_len); 454 sc->sc_state = SCSI_HASBUS; 455 DPRINTF(("DMA done. got %d.\n", sc->dma_len)); 456 return sc->dma_len; 457 458 /* scsierror("DMA not completed\n"); */ 459 460 return 0; 461 } 462