/[gxemul]/upstream/0.3.6/src/devices/dev_asc.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.3.6/src/devices/dev_asc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 15 - (show annotations)
Mon Oct 8 16:18:56 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 31789 byte(s)
0.3.6
1 /*
2 * Copyright (C) 2003-2005 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: dev_asc.c,v 1.74 2005/07/27 06:57:34 debug Exp $
29 *
30 * 'asc' SCSI controller for some DECstation/DECsystem models, and
31 * for PICA-61.
32 *
33 * Supposed to support SCSI-1 and SCSI-2. I've not yet found any docs
34 * on NCR53C9X, so I'll try to implement this device from LSI53CF92A docs
35 * instead.
36 *
37 *
38 * Memory layout on DECstation:
39 *
40 * NCR53C94 registers at base + 0
41 * DMA address register at base + 0x40000
42 * 128K SRAM buffer at base + 0x80000
43 * ROM at base + 0xc0000
44 *
45 * Memory layout on PICA-61:
46 *
47 * I haven't had time to look this up yet, but length = 0x1000.
48 *
49 *
50 * TODO: This module needs a clean-up, and some testing to see that
51 * it works will all OSes that might use it (NetBSD, OpenBSD,
52 * Ultrix, Linux, Mach, OSF/1, Sprite, ...)
53 */
54
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58
59 #include "cpu.h"
60 #include "devices.h"
61 #include "diskimage.h"
62 #include "machine.h"
63 #include "memory.h"
64 #include "misc.h"
65
66 #include "ncr53c9xreg.h"
67
68
69 /* #define ASC_DEBUG */
70 /* #define debug fatal */
71 /* #define ASC_FULL_REGISTER_ACCESS_DEBUG */
72 /* static int quiet_mode = 0; */
73
74 #define ASC_TICK_SHIFT 15
75
76 extern int quiet_mode;
77
78
79 #define ASC_FIFO_LEN 16
80 #define STATE_DISCONNECTED 0
81 #define STATE_INITIATOR 1
82 #define STATE_TARGET 2
83
84 #define PHASE_DATA_OUT 0
85 #define PHASE_DATA_IN 1
86 #define PHASE_COMMAND 2
87 #define PHASE_STATUS 3
88 #define PHASE_MSG_OUT 6
89 #define PHASE_MSG_IN 7
90
91
92 /* The controller's SCSI id: */
93 #define ASC_SCSI_ID 7
94
95 #define ASC_DMA_SIZE (128*1024)
96
97 struct asc_data {
98 int mode;
99
100 void *turbochannel;
101 int irq_nr;
102 int irq_caused_last_time;
103
104 /* Current state and transfer: */
105 int cur_state;
106 int cur_phase;
107 struct scsi_transfer *xferp;
108
109 /* FIFO: */
110 unsigned char fifo[ASC_FIFO_LEN];
111 int fifo_in;
112 int fifo_out;
113 int n_bytes_in_fifo; /* cached */
114
115 /* ATN signal: */
116 int atn;
117
118 /* Incoming dma data: */
119 unsigned char *incoming_data;
120 int incoming_len;
121 int incoming_data_addr;
122
123 /* Built-in DMA memory (for DECstation 5000/200): */
124 uint32_t dma_address_reg;
125 unsigned char *dma_address_reg_memory;
126 unsigned char *dma;
127
128 void *dma_controller_data;
129 size_t (*dma_controller)(void *dma_controller_data,
130 unsigned char *data, size_t len, int writeflag);
131
132 /* Read registers and write registers: */
133 uint32_t reg_ro[0x10];
134 uint32_t reg_wo[0x10];
135 };
136
137 /* (READ/WRITE name, if split) */
138 char *asc_reg_names[0x10] = {
139 "NCR_TCL", "NCR_TCM", "NCR_FIFO", "NCR_CMD",
140 "NCR_STAT/NCR_SELID", "NCR_INTR/NCR_TIMEOUT",
141 "NCR_STEP/NCR_SYNCTP", "NCR_FFLAG/NCR_SYNCOFF",
142 "NCR_CFG1", "NCR_CCF", "NCR_TEST", "NCR_CFG2",
143 "NCR_CFG3", "reg_0xd", "NCR_TCH", "reg_0xf"
144 };
145
146
147 /* This is referenced below. */
148 static int dev_asc_select(struct cpu *cpu, struct asc_data *d, int from_id,
149 int to_id, int dmaflag, int n_messagebytes);
150
151
152 /*
153 * dev_asc_tick():
154 *
155 * This function is called "every now and then" from the CPU
156 * main loop.
157 */
158 void dev_asc_tick(struct cpu *cpu, void *extra)
159 {
160 struct asc_data *d = extra;
161
162 if (d->reg_ro[NCR_STAT] & NCRSTAT_INT)
163 cpu_interrupt(cpu, d->irq_nr);
164 }
165
166
167 /*
168 * dev_asc_fifo_flush():
169 *
170 * Flush the fifo.
171 */
172 static void dev_asc_fifo_flush(struct asc_data *d)
173 {
174 d->fifo[0] = 0x00;
175 d->fifo_in = 0;
176 d->fifo_out = 0;
177 d->n_bytes_in_fifo = 0;
178 }
179
180
181 /*
182 * dev_asc_reset():
183 *
184 * Reset the state of the asc.
185 */
186 static void dev_asc_reset(struct asc_data *d)
187 {
188 d->cur_state = STATE_DISCONNECTED;
189 d->atn = 0;
190
191 if (d->xferp != NULL)
192 scsi_transfer_free(d->xferp);
193 d->xferp = NULL;
194
195 dev_asc_fifo_flush(d);
196
197 /* According to table 4.1 in the LSI53CF92A manual: */
198 memset(d->reg_wo, 0, sizeof(d->reg_wo));
199 d->reg_wo[NCR_TCH] = 0x94;
200 d->reg_wo[NCR_CCF] = 2;
201 memcpy(d->reg_ro, d->reg_wo, sizeof(d->reg_ro));
202 d->reg_wo[NCR_SYNCTP] = 5;
203 }
204
205
206 /*
207 * dev_asc_fifo_read():
208 *
209 * Read a byte from the asc FIFO.
210 */
211 static int dev_asc_fifo_read(struct asc_data *d)
212 {
213 int res = d->fifo[d->fifo_out];
214
215 if (d->fifo_in == d->fifo_out)
216 fatal("dev_asc: WARNING! FIFO overrun!\n");
217
218 d->fifo_out = (d->fifo_out + 1) % ASC_FIFO_LEN;
219 d->n_bytes_in_fifo --;
220
221 return res;
222 }
223
224
225 /*
226 * dev_asc_fifo_write():
227 *
228 * Write a byte to the asc FIFO.
229 */
230 static void dev_asc_fifo_write(struct asc_data *d, unsigned char data)
231 {
232 d->fifo[d->fifo_in] = data;
233 d->fifo_in = (d->fifo_in + 1) % ASC_FIFO_LEN;
234 d->n_bytes_in_fifo ++;
235
236 if (d->fifo_in == d->fifo_out)
237 fatal("dev_asc: WARNING! FIFO overrun on write!\n");
238 }
239
240
241 /*
242 * dev_asc_newxfer():
243 *
244 * Allocate memory for a new transfer.
245 */
246 static void dev_asc_newxfer(struct asc_data *d)
247 {
248 if (d->xferp != NULL) {
249 printf("WARNING! dev_asc_newxfer(): freeing previous"
250 " transfer\n");
251 scsi_transfer_free(d->xferp);
252 d->xferp = NULL;
253 }
254
255 d->xferp = scsi_transfer_alloc();
256 #if 0
257 d->xferp->get_data_out = dev_asc_get_data_out;
258 d->xferp->gdo_extra = (void *) d;
259 #endif
260 }
261
262
263 /*
264 * dev_asc_transfer():
265 *
266 * Transfer data from a SCSI device to the controller (or vice versa),
267 * depending on the current phase.
268 *
269 * Returns 1 if ok, 0 on error.
270 */
271 static int dev_asc_transfer(struct cpu *cpu, struct asc_data *d, int dmaflag)
272 {
273 int res = 1, all_done = 1;
274 int len, i, ch;
275
276 if (!quiet_mode)
277 debug(" { TRANSFER to/from id %i: ", d->reg_wo[NCR_SELID] & 7);
278
279 if (d->cur_phase == PHASE_DATA_IN) {
280 /* Data coming into the controller from external device: */
281 if (!dmaflag) {
282 if (d->xferp->data_in == NULL) {
283 fatal("no incoming data?\n");
284 res = 0;
285 } else {
286 /* TODO */
287 fatal("TODO..............\n");
288 len = d->reg_wo[NCR_TCL] +
289 d->reg_wo[NCR_TCM] * 256;
290
291 len--;
292 ch = d->incoming_data[d->incoming_data_addr];
293 debug(" %02x", ch);
294
295 d->incoming_data_addr ++;
296 dev_asc_fifo_write(d, ch);
297
298 if (len == 0) {
299 free(d->incoming_data);
300 d->incoming_data = NULL;
301 }
302
303 d->reg_ro[NCR_TCL] = len & 255;
304 d->reg_ro[NCR_TCM] = (len >> 8) & 255;
305 }
306 } else {
307 /* Copy from the incoming data into dma memory: */
308 if (d->xferp->data_in == NULL) {
309 fatal("no incoming DMA data?\n");
310 res = 0;
311 } else {
312 int len = d->xferp->data_in_len;
313 int len2 = d->reg_wo[NCR_TCL] +
314 d->reg_wo[NCR_TCM] * 256;
315 if (len2 == 0)
316 len2 = 65536;
317
318 if (len < len2) {
319 fatal("{ asc: data in, len=%i len2=%i "
320 "}\n", len, len2);
321 }
322
323 /* TODO: check len2 in a similar way? */
324 if (len + (d->dma_address_reg &
325 (ASC_DMA_SIZE-1)) > ASC_DMA_SIZE)
326 len = ASC_DMA_SIZE -
327 (d->dma_address_reg &
328 (ASC_DMA_SIZE-1));
329
330 if (len2 > len) {
331 memset(d->dma + (d->dma_address_reg &
332 (ASC_DMA_SIZE-1)), 0, len2);
333 len2 = len;
334 }
335
336 #ifdef ASC_DEBUG
337 if (!quiet_mode) {
338 int i;
339 for (i=0; i<len; i++)
340 debug(" %02x", d->xferp->
341 data_in[i]);
342 }
343 #endif
344
345 /*
346 * Are we using an external DMA controller?
347 * Then use it. Otherwise place the data in
348 * the DECstation 5000/200 built-in DMA
349 * region.
350 */
351 if (d->dma_controller != NULL)
352 d->dma_controller(
353 d->dma_controller_data,
354 d->xferp->data_in,
355 len2, 1);
356 else
357 memcpy(d->dma + (d->dma_address_reg &
358 (ASC_DMA_SIZE-1)),
359 d->xferp->data_in, len2);
360
361 if (d->xferp->data_in_len > len2) {
362 unsigned char *n;
363
364 if (d->dma_controller != NULL)
365 printf("WARNING!!!!!!!!! BUG!!!! Unexpected stuff..."
366 "len2=%i d->xferp->data_in_len=%i\n", (int)len2,
367 (int)d->xferp->data_in_len);
368
369 all_done = 0;
370 /* fatal("{ asc: multi-transfer"
371 " data_in, len=%i len2=%i }\n",
372 (int)len, (int)len2); */
373
374 d->xferp->data_in_len -= len2;
375 n = malloc(d->xferp->data_in_len);
376 if (n == NULL) {
377 fprintf(stderr, "out of memory"
378 " in dev_asc\n");
379 exit(1);
380 }
381 memcpy(n, d->xferp->data_in + len2,
382 d->xferp->data_in_len);
383 free(d->xferp->data_in);
384 d->xferp->data_in = n;
385
386 len = len2;
387 }
388
389 len = 0;
390
391 d->reg_ro[NCR_TCL] = len & 255;
392 d->reg_ro[NCR_TCM] = (len >> 8) & 255;
393
394 /* Successful DMA transfer: */
395 d->reg_ro[NCR_STAT] |= NCRSTAT_TC;
396 }
397 }
398 } else if (d->cur_phase == PHASE_DATA_OUT) {
399 /* Data going from the controller to an external device: */
400 if (!dmaflag) {
401 fatal("TODO.......asdgasin\n");
402 } else {
403 /* Copy data from DMA to data_out: */
404 int len = d->xferp->data_out_len;
405 int len2 = d->reg_wo[NCR_TCL] +
406 d->reg_wo[NCR_TCM] * 256;
407 if (len2 == 0)
408 len2 = 65536;
409
410 if (len == 0) {
411 fprintf(stderr, "d->xferp->data_out_len == "
412 "0 ?\n");
413 exit(1);
414 }
415
416 /* TODO: Make sure that len2 doesn't go outside
417 of the dma memory? */
418
419 /* fatal(" data out offset=%5i len=%5i\n",
420 d->xferp->data_out_offset, len2); */
421
422 if (d->xferp->data_out_offset + len2 >
423 d->xferp->data_out_len) {
424 len2 = d->xferp->data_out_len -
425 d->xferp->data_out_offset;
426 }
427
428 /*
429 * Are we using an external DMA controller? Then use
430 * it. Otherwise place the data in the DECstation
431 * 5000/200 built-in DMA region.
432 */
433 if (d->xferp->data_out == NULL) {
434 scsi_transfer_allocbuf(&d->xferp->data_out_len,
435 &d->xferp->data_out, len, 0);
436
437 if (d->dma_controller != NULL)
438 d->dma_controller(
439 d->dma_controller_data,
440 d->xferp->data_out,
441 len2, 0);
442 else
443 memcpy(d->xferp->data_out,
444 d->dma + (d->dma_address_reg &
445 (ASC_DMA_SIZE-1)), len2);
446 d->xferp->data_out_offset = len2;
447 } else {
448 /* Continuing a multi-transfer: */
449 if (d->dma_controller != NULL)
450 d->dma_controller(
451 d->dma_controller_data,
452 d->xferp->data_out +
453 d->xferp->data_out_offset,
454 len2, 0);
455 else
456 memcpy(d->xferp->data_out +
457 d->xferp->data_out_offset,
458 d->dma + (d->dma_address_reg &
459 (ASC_DMA_SIZE-1)), len2);
460 d->xferp->data_out_offset += len2;
461 }
462
463 /* If the disk wants more than we're DMAing,
464 then this is a multitransfer: */
465 if (d->xferp->data_out_offset !=
466 d->xferp->data_out_len) {
467 if (!quiet_mode)
468 debug("[ asc: data_out, multitransfer "
469 "len = %i, len2 = %i ]\n",
470 (int)len, (int)len2);
471 if (d->xferp->data_out_offset >
472 d->xferp->data_out_len)
473 fatal("[ asc data_out dma: too much?"
474 " ]\n");
475 else
476 all_done = 0;
477 }
478
479 #ifdef ASC_DEBUG
480 if (!quiet_mode) {
481 int i;
482 for (i=0; i<len; i++)
483 debug(" %02x", d->xferp->data_out[i]);
484 }
485 #endif
486 len = 0;
487
488 d->reg_ro[NCR_TCL] = len & 255;
489 d->reg_ro[NCR_TCM] = (len >> 8) & 255;
490
491 /* Successful DMA transfer: */
492 d->reg_ro[NCR_STAT] |= NCRSTAT_TC;
493 }
494 } else if (d->cur_phase == PHASE_MSG_OUT) {
495 if (!quiet_mode)
496 debug("MSG OUT: ");
497 /* Data going from the controller to an external device: */
498 if (!dmaflag) {
499 /* There should already be one byte in msg_out, so we
500 just extend the message: */
501 int oldlen = d->xferp->msg_out_len;
502 int newlen;
503
504 if (oldlen != 1) {
505 fatal(" (PHASE OUT MSG len == %i, "
506 "should be 1)\n", oldlen);
507 }
508
509 newlen = oldlen + d->n_bytes_in_fifo;
510 d->xferp->msg_out = realloc(d->xferp->msg_out, newlen);
511 d->xferp->msg_out_len = newlen;
512 if (d->xferp->msg_out == NULL) {
513 fprintf(stderr, "out of memory realloc'ing "
514 "msg_out\n");
515 exit(1);
516 }
517
518 i = oldlen;
519 while (d->fifo_in != d->fifo_out) {
520 ch = dev_asc_fifo_read(d);
521 d->xferp->msg_out[i++] = ch;
522 #ifdef ASC_DEBUG
523 debug("0x%02x ", ch);
524 #endif
525 }
526
527 #ifdef MACH
528 /* Super-ugly hack for Mach/PMAX: TODO: make nicer */
529 if (d->xferp->msg_out_len == 6 &&
530 (d->xferp->msg_out[0] == 0x80 ||
531 d->xferp->msg_out[0] == 0xc0) &&
532 d->xferp->msg_out[1] == 0x01 &&
533 d->xferp->msg_out[2] == 0x03 &&
534 d->xferp->msg_out[3] == 0x01 &&
535 d->xferp->msg_out[4] == 0x32 &&
536 d->xferp->msg_out[5] == 0x0f) {
537 fatal(" !! Mach/PMAX hack !! ");
538 all_done = 0;
539 d->cur_phase = PHASE_MSG_IN;
540 }
541 #endif
542 } else {
543 /* Copy data from DMA to msg_out: */
544 fatal("[ DMA MSG OUT: xxx TODO! ]");
545 /* TODO */
546 res = 0;
547 }
548 } else if (d->cur_phase == PHASE_MSG_IN) {
549 if (!quiet_mode)
550 debug(" MSG IN");
551 fatal("[ MACH HACK! ]");
552 /* Super-ugly hack for Mach/PMAX: TODO: make nicer */
553 dev_asc_fifo_write(d, 0x07);
554 d->cur_phase = PHASE_COMMAND;
555 all_done = 0;
556 } else if (d->cur_phase == PHASE_COMMAND) {
557 if (!quiet_mode)
558 debug(" COMMAND ==> select ");
559 res = dev_asc_select(cpu, d, d->reg_ro[NCR_CFG1] & 7,
560 d->reg_wo[NCR_SELID] & 7, dmaflag, 0);
561 return res;
562 } else {
563 fatal("!!! TODO: unknown/unimplemented phase "
564 "in transfer: %i\n", d->cur_phase);
565 }
566
567 /* Redo the command if data was just sent using DATA_OUT: */
568 if (d->cur_phase == PHASE_DATA_OUT) {
569 res = diskimage_scsicommand(cpu, d->reg_wo[NCR_SELID] & 7,
570 DISKIMAGE_SCSI, d->xferp);
571 }
572
573 if (all_done) {
574 if (d->cur_phase == PHASE_MSG_OUT)
575 d->cur_phase = PHASE_COMMAND;
576 else
577 d->cur_phase = PHASE_STATUS;
578 }
579
580 /*
581 * Cause an interrupt after the transfer:
582 *
583 * NOTE: Earlier I had this in here as well:
584 * d->reg_ro[NCR_INTR] |= NCRINTR_FC;
585 * but Linux/DECstation and OpenBSD/pmax seems to choke on that.
586 */
587 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
588 d->reg_ro[NCR_INTR] |= NCRINTR_BS;
589 d->reg_ro[NCR_STAT] = (d->reg_ro[NCR_STAT] & ~7) | d->cur_phase;
590 d->reg_ro[NCR_STEP] = (d->reg_ro[NCR_STEP] & ~7) | 4; /* 4? */
591
592 if (!quiet_mode)
593 debug("}");
594 return res;
595 }
596
597
598 /*
599 * dev_asc_select():
600 *
601 * Select a SCSI device, send msg bytes (if any), and send command bytes.
602 * (Call diskimage_scsicommand() to handle the command.)
603 *
604 * Return value: 1 if ok, 0 on error.
605 */
606 static int dev_asc_select(struct cpu *cpu, struct asc_data *d, int from_id,
607 int to_id, int dmaflag, int n_messagebytes)
608 {
609 int ok, len, i, ch;
610
611 if (!quiet_mode)
612 debug(" { SELECT id %i: ", to_id);
613
614 /*
615 * Message bytes, if any:
616 */
617 if (!quiet_mode)
618 debug("msg:");
619
620 if (n_messagebytes > 0) {
621 scsi_transfer_allocbuf(&d->xferp->msg_out_len,
622 &d->xferp->msg_out, n_messagebytes, 0);
623
624 i = 0;
625 while (n_messagebytes-- > 0) {
626 int ch = dev_asc_fifo_read(d);
627 if (!quiet_mode)
628 debug(" %02x", ch);
629 d->xferp->msg_out[i++] = ch;
630 }
631
632 if ((d->xferp->msg_out[0] & 0x7) != 0x00) {
633 debug(" (LUNs not implemented yet: 0x%02x) }",
634 d->xferp->msg_out[0]);
635 return 0;
636 }
637
638 if (((d->xferp->msg_out[0] & ~0x7) != 0xc0) &&
639 ((d->xferp->msg_out[0] & ~0x7) != 0x80)) {
640 fatal(" (Unimplemented msg out: 0x%02x) }",
641 d->xferp->msg_out[0]);
642 return 0;
643 }
644
645 if (d->xferp->msg_out_len > 1) {
646 fatal(" (Long msg out, not implemented yet;"
647 " len=%i) }", d->xferp->msg_out_len);
648 return 0;
649 }
650 } else {
651 if (!quiet_mode)
652 debug(" none");
653 }
654
655 /* Special case: SELATNS (with STOP sequence): */
656 if (d->cur_phase == PHASE_MSG_OUT) {
657 if (!quiet_mode)
658 debug(" MSG OUT DEBUG");
659 if (d->xferp->msg_out_len != 1) {
660 fatal(" (SELATNS: msg out len == %i, should be 1)",
661 d->xferp->msg_out_len);
662 return 0;
663 }
664
665 /* d->cur_phase = PHASE_COMMAND; */
666
667 /* According to the LSI manual: */
668 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
669 d->reg_ro[NCR_INTR] |= NCRINTR_FC;
670 d->reg_ro[NCR_INTR] |= NCRINTR_BS;
671 d->reg_ro[NCR_STAT] = (d->reg_ro[NCR_STAT] & ~7) | d->cur_phase;
672 d->reg_ro[NCR_STEP] = (d->reg_ro[NCR_STEP] & ~7) | 1;
673
674 if (!quiet_mode)
675 debug("}");
676 return 1;
677 }
678
679 /*
680 * Command bytes:
681 */
682 if (!quiet_mode)
683 debug(", cmd: ");
684
685 if (!dmaflag) {
686 if (!quiet_mode)
687 debug("[non-DMA] ");
688
689 scsi_transfer_allocbuf(&d->xferp->cmd_len,
690 &d->xferp->cmd, d->n_bytes_in_fifo, 0);
691
692 i = 0;
693 while (d->fifo_in != d->fifo_out) {
694 ch = dev_asc_fifo_read(d);
695 d->xferp->cmd[i++] = ch;
696 if (!quiet_mode)
697 debug("%02x ", ch);
698 }
699 } else {
700 if (!quiet_mode)
701 debug("[DMA] ");
702 len = d->reg_wo[NCR_TCL] + d->reg_wo[NCR_TCM] * 256;
703 if (len == 0)
704 len = 65536;
705
706 scsi_transfer_allocbuf(&d->xferp->cmd_len,
707 &d->xferp->cmd, len, 0);
708
709 for (i=0; i<len; i++) {
710 int ofs = d->dma_address_reg + i;
711 ch = d->dma[ofs & (ASC_DMA_SIZE-1)];
712 d->xferp->cmd[i] = ch;
713 if (!quiet_mode)
714 debug("%02x ", ch);
715 }
716
717 d->reg_ro[NCR_TCL] = len & 255;
718 d->reg_ro[NCR_TCM] = (len >> 8) & 255;
719
720 d->reg_ro[NCR_STAT] |= NCRSTAT_TC;
721 }
722
723 /*
724 * Call the SCSI device to perform the command:
725 */
726 ok = diskimage_scsicommand(cpu, to_id, DISKIMAGE_SCSI, d->xferp);
727
728
729 /* Cause an interrupt: */
730 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
731 d->reg_ro[NCR_INTR] |= NCRINTR_FC;
732 d->reg_ro[NCR_INTR] |= NCRINTR_BS;
733
734 if (ok == 2)
735 d->cur_phase = PHASE_DATA_OUT;
736 else if (d->xferp->data_in != NULL)
737 d->cur_phase = PHASE_DATA_IN;
738 else
739 d->cur_phase = PHASE_STATUS;
740
741 d->reg_ro[NCR_STAT] = (d->reg_ro[NCR_STAT] & ~7) | d->cur_phase;
742 d->reg_ro[NCR_STEP] = (d->reg_ro[NCR_STEP] & ~7) | 4; /* DONE (?) */
743
744 if (!quiet_mode)
745 debug("}");
746
747 return ok;
748 }
749
750
751 /*
752 * dev_asc_address_reg_access():
753 */
754 int dev_asc_address_reg_access(struct cpu *cpu, struct memory *mem,
755 uint64_t relative_addr, unsigned char *data, size_t len,
756 int writeflag, void *extra)
757 {
758 struct asc_data *d = extra;
759
760 if (relative_addr + len > 4)
761 return 0;
762
763 if (writeflag==MEM_READ) {
764 memcpy(data, d->dma_address_reg_memory + relative_addr, len);
765 } else {
766 memcpy(d->dma_address_reg_memory + relative_addr, data, len);
767 }
768
769 return 1;
770 }
771
772
773 /*
774 * dev_asc_dma_access():
775 */
776 int dev_asc_dma_access(struct cpu *cpu, struct memory *mem,
777 uint64_t relative_addr, unsigned char *data, size_t len,
778 int writeflag, void *extra)
779 {
780 struct asc_data *d = extra;
781
782 if (writeflag==MEM_READ) {
783 memcpy(data, d->dma + relative_addr, len);
784 #ifdef ASC_DEBUG
785 {
786 int i;
787 debug("[ asc: read from DMA addr 0x%05x:",
788 (int) relative_addr);
789 for (i=0; i<len; i++)
790 debug(" %02x", data[i]);
791 debug(" ]\n");
792 }
793 #endif
794
795 /* Don't return the common way, as that
796 would overwrite data. */
797 return 1;
798 } else {
799 memcpy(d->dma + relative_addr, data, len);
800 #ifdef ASC_DEBUG
801 {
802 int i;
803 debug("[ asc: write to DMA addr 0x%05x:",
804 (int) relative_addr);
805 for (i=0; i<len; i++)
806 debug(" %02x", data[i]);
807 debug(" ]\n");
808 }
809 #endif
810 /* Quick return. */
811 return 1;
812 }
813 }
814
815
816 /*
817 * dev_asc_access():
818 */
819 int dev_asc_access(struct cpu *cpu, struct memory *mem,
820 uint64_t relative_addr, unsigned char *data, size_t len,
821 int writeflag, void *extra)
822 {
823 int regnr;
824 struct asc_data *d = extra;
825 int target_exists;
826 int n_messagebytes = 0;
827 uint64_t idata = 0, odata = 0;
828
829
830 idata = memory_readmax64(cpu, data, len);
831
832 #if 0
833 /* Debug stuff useful when trying to make dev_asc compatible
834 with the 'arc' emulation mode, which is different from
835 the DECstation mode. */
836 fatal("[ asc: writeflag=%i addr=%08x idata=%016llx ]\n",
837 writeflag, (int)relative_addr, (long long)idata);
838 #endif
839
840 switch (d->mode) {
841 case DEV_ASC_DEC:
842 regnr = relative_addr / 4;
843 break;
844 case DEV_ASC_PICA:
845 default:
846 regnr = relative_addr;
847 }
848
849 /* Controller's ID is fixed: */
850 d->reg_ro[NCR_CFG1] = (d->reg_ro[NCR_CFG1] & ~7) | ASC_SCSI_ID;
851
852 d->reg_ro[NCR_FFLAG] = ((d->reg_ro[NCR_STEP] & 0x7) << 5)
853 + d->n_bytes_in_fifo;
854
855 d->dma_address_reg =
856 d->dma_address_reg_memory[0] +
857 (d->dma_address_reg_memory[1] << 8) +
858 (d->dma_address_reg_memory[2] << 16) +
859 (d->dma_address_reg_memory[3] << 24);
860
861 if (regnr < 0x10) {
862 if (regnr == NCR_FIFO) {
863 if (writeflag == MEM_WRITE)
864 dev_asc_fifo_write(d, idata);
865 else
866 odata = dev_asc_fifo_read(d);
867 } else {
868 if (writeflag==MEM_WRITE)
869 d->reg_wo[regnr] = idata;
870 else
871 odata = d->reg_ro[regnr];
872 }
873
874 #ifdef ASC_FULL_REGISTER_ACCESS_DEBUG
875 if (!quiet_mode) {
876 if (writeflag==MEM_READ) {
877 debug("[ asc: read from %s: 0x%02x",
878 asc_reg_names[regnr], (int)odata);
879 } else {
880 debug("[ asc: write to %s: 0x%02x",
881 asc_reg_names[regnr], (int)idata);
882 }
883 }
884 #endif
885 } else if (relative_addr >= 0x300 && relative_addr < 0x600
886 && d->turbochannel != NULL) {
887 debug("[ asc: offset 0x%x, redirecting to turbochannel"
888 " access ]\n", relative_addr);
889 return dev_turbochannel_access(cpu, mem,
890 relative_addr, data, len, writeflag,
891 d->turbochannel);
892 } else {
893 if (writeflag==MEM_READ) {
894 fatal("[ asc: read from 0x%04x: 0x%02x ]\n",
895 relative_addr, (int)odata);
896 } else {
897 fatal("[ asc: write to 0x%04x: 0x%02x ]\n",
898 relative_addr, (int)idata);
899 }
900 }
901
902 /*
903 * Some registers are read/write. Copy contents of
904 * reg_wo to reg_ro:
905 */
906 #if 0
907 d->reg_ro[ 0] = d->reg_wo[0]; /* Transfer count lo and */
908 d->reg_ro[ 1] = d->reg_wo[1]; /* middle */
909 #endif
910 d->reg_ro[ 2] = d->reg_wo[2];
911 d->reg_ro[ 3] = d->reg_wo[3];
912 d->reg_ro[ 8] = d->reg_wo[8];
913 d->reg_ro[ 9] = d->reg_wo[9];
914 d->reg_ro[10] = d->reg_wo[10];
915 d->reg_ro[11] = d->reg_wo[11];
916 d->reg_ro[12] = d->reg_wo[12];
917
918 if (regnr == NCR_CMD && writeflag == MEM_WRITE) {
919 if (!quiet_mode)
920 debug(" ");
921
922 /* TODO: Perhaps turn off others here too? */
923 d->reg_ro[NCR_INTR] &= ~NCRINTR_SBR;
924
925 if (idata & NCRCMD_DMA) {
926 if (!quiet_mode)
927 debug("[DMA] ");
928
929 /*
930 * DMA commands load the transfer count from the
931 * write-only registers to the read-only ones, and
932 * the Terminal Count bit is cleared.
933 */
934 d->reg_ro[NCR_TCL] = d->reg_wo[NCR_TCL];
935 d->reg_ro[NCR_TCM] = d->reg_wo[NCR_TCM];
936 d->reg_ro[NCR_TCH] = d->reg_wo[NCR_TCH];
937 d->reg_ro[NCR_STAT] &= ~NCRSTAT_TC;
938 }
939
940 switch (idata & ~NCRCMD_DMA) {
941
942 case NCRCMD_NOP:
943 if (!quiet_mode)
944 debug("NOP");
945 break;
946
947 case NCRCMD_FLUSH:
948 if (!quiet_mode)
949 debug("FLUSH");
950 /* Flush the FIFO: */
951 dev_asc_fifo_flush(d);
952 break;
953
954 case NCRCMD_RSTCHIP:
955 if (!quiet_mode)
956 debug("RSTCHIP");
957 /* Hardware reset. */
958 dev_asc_reset(d);
959 break;
960
961 case NCRCMD_RSTSCSI:
962 if (!quiet_mode)
963 debug("RSTSCSI");
964 /* No interrupt if interrupts are disabled. */
965 if (!(d->reg_wo[NCR_CFG1] & NCRCFG1_SRR))
966 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
967 d->reg_ro[NCR_INTR] |= NCRINTR_SBR;
968 d->reg_ro[NCR_INTR] |= NCRINTR_FC;
969 d->cur_state = STATE_DISCONNECTED;
970 break;
971
972 case NCRCMD_ENSEL:
973 if (!quiet_mode)
974 debug("ENSEL");
975 /* TODO */
976 break;
977
978 case NCRCMD_ICCS:
979 if (!quiet_mode)
980 debug("ICCS");
981 /* Reveice a status byte + a message byte. */
982
983 /* TODO: how about other status and message bytes? */
984 if (d->xferp != NULL && d->xferp->status != NULL)
985 dev_asc_fifo_write(d, d->xferp->status[0]);
986 else
987 dev_asc_fifo_write(d, 0x00);
988
989 if (d->xferp != NULL && d->xferp->msg_in != NULL)
990 dev_asc_fifo_write(d, d->xferp->msg_in[0]);
991 else
992 dev_asc_fifo_write(d, 0x00);
993
994 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
995 d->reg_ro[NCR_INTR] |= NCRINTR_FC;
996 /* d->reg_ro[NCR_INTR] |= NCRINTR_BS; */
997 d->reg_ro[NCR_STAT] = (d->reg_ro[NCR_STAT] & ~7) | 7;
998 /* ? probably 7 */
999 d->reg_ro[NCR_STEP] = (d->reg_ro[NCR_STEP] & ~7) | 4;
1000 /* ? */
1001 break;
1002
1003 case NCRCMD_MSGOK:
1004 /* Message is being Rejected if ATN is set,
1005 otherwise Accepted. */
1006 if (!quiet_mode) {
1007 debug("MSGOK");
1008 if (d->atn)
1009 debug("; Rejecting message");
1010 else
1011 debug("; Accepting message");
1012 }
1013 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
1014 d->reg_ro[NCR_INTR] |= NCRINTR_DIS;
1015
1016 d->reg_ro[NCR_STAT] = (d->reg_ro[NCR_STAT] & ~7) |
1017 d->cur_phase; /* 6? */
1018 d->reg_ro[NCR_STEP] = (d->reg_ro[NCR_STEP] & ~7) |
1019 4; /* ? */
1020
1021 d->cur_state = STATE_DISCONNECTED;
1022
1023 if (d->xferp != NULL)
1024 scsi_transfer_free(d->xferp);
1025 d->xferp = NULL;
1026 break;
1027
1028 case NCRCMD_SETATN:
1029 if (!quiet_mode)
1030 debug("SETATN");
1031 d->atn = 1;
1032 break;
1033
1034 case NCRCMD_RSTATN:
1035 if (!quiet_mode)
1036 debug("RSTATN");
1037 d->atn = 0;
1038 break;
1039
1040 case NCRCMD_SELNATN:
1041 case NCRCMD_SELATN:
1042 case NCRCMD_SELATNS:
1043 case NCRCMD_SELATN3:
1044 d->cur_phase = PHASE_COMMAND;
1045 switch (idata & ~NCRCMD_DMA) {
1046 case NCRCMD_SELATN:
1047 case NCRCMD_SELATNS:
1048 if ((idata & ~NCRCMD_DMA) == NCRCMD_SELATNS) {
1049 if (!quiet_mode)
1050 debug("SELATNS: select with "
1051 "atn and stop, id %i",
1052 d->reg_wo[NCR_SELID] & 7);
1053 d->cur_phase = PHASE_MSG_OUT;
1054 } else {
1055 if (!quiet_mode)
1056 debug("SELATN: select with atn"
1057 ", id %i",
1058 d->reg_wo[NCR_SELID] & 7);
1059 }
1060 n_messagebytes = 1;
1061 break;
1062 case NCRCMD_SELATN3:
1063 if (!quiet_mode)
1064 debug("SELNATN: select with atn3, "
1065 "id %i", d->reg_wo[NCR_SELID] & 7);
1066 n_messagebytes = 3;
1067 break;
1068 case NCRCMD_SELNATN:
1069 if (!quiet_mode)
1070 debug("SELNATN: select without atn, "
1071 "id %i", d->reg_wo[NCR_SELID] & 7);
1072 n_messagebytes = 0;
1073 }
1074
1075 /* TODO: not just disk, but some generic
1076 SCSI device */
1077 target_exists = diskimage_exist(cpu->machine,
1078 d->reg_wo[NCR_SELID] & 7, DISKIMAGE_SCSI);
1079
1080 if (target_exists) {
1081 /*
1082 * Select a SCSI device, send message bytes
1083 * (if any) and command bytes to the target.
1084 */
1085 int ok;
1086
1087 dev_asc_newxfer(d);
1088
1089 ok = dev_asc_select(cpu, d,
1090 d->reg_ro[NCR_CFG1] & 7,
1091 d->reg_wo[NCR_SELID] & 7,
1092 idata & NCRCMD_DMA? 1 : 0,
1093 n_messagebytes);
1094
1095 if (ok)
1096 d->cur_state = STATE_INITIATOR;
1097 else {
1098 d->cur_state = STATE_DISCONNECTED;
1099 d->reg_ro[NCR_INTR] |= NCRINTR_DIS;
1100 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
1101 d->reg_ro[NCR_STEP] =
1102 (d->reg_ro[NCR_STEP] & ~7) | 0;
1103 if (d->xferp != NULL)
1104 scsi_transfer_free(d->xferp);
1105 d->xferp = NULL;
1106 }
1107 } else {
1108 /*
1109 * Selection failed, non-existant scsi ID:
1110 *
1111 * This is good enough to fool Ultrix, NetBSD,
1112 * OpenBSD and Linux to continue detection of
1113 * other IDs, without giving any warnings.
1114 */
1115 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
1116 d->reg_ro[NCR_INTR] |= NCRINTR_DIS;
1117 d->reg_ro[NCR_STEP] &= ~7;
1118 d->reg_ro[NCR_STEP] |= 0;
1119 dev_asc_fifo_flush(d);
1120 d->cur_state = STATE_DISCONNECTED;
1121 }
1122 break;
1123
1124 case NCRCMD_TRPAD:
1125 if (!quiet_mode)
1126 debug("TRPAD");
1127
1128 dev_asc_newxfer(d);
1129 {
1130 int ok;
1131
1132 ok = dev_asc_transfer(cpu, d,
1133 idata & NCRCMD_DMA? 1 : 0);
1134 if (!ok) {
1135 d->cur_state = STATE_DISCONNECTED;
1136 d->reg_ro[NCR_INTR] |= NCRINTR_DIS;
1137 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
1138 d->reg_ro[NCR_STEP] = (d->reg_ro[
1139 NCR_STEP] & ~7) | 0;
1140 if (d->xferp != NULL)
1141 scsi_transfer_free(d->xferp);
1142 d->xferp = NULL;
1143 }
1144 }
1145 break;
1146
1147 /* Old code which didn't work with Mach: */
1148 #if 0
1149 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
1150 d->reg_ro[NCR_INTR] |= NCRINTR_BS;
1151 d->reg_ro[NCR_INTR] |= NCRINTR_FC;
1152 d->reg_ro[NCR_STAT] |= NCRSTAT_TC;
1153
1154 d->reg_ro[NCR_TCL] = 0;
1155 d->reg_ro[NCR_TCM] = 0;
1156
1157 d->reg_ro[NCR_STEP] &= ~7;
1158 #if 0
1159 d->reg_ro[NCR_STEP] |= 0;
1160 dev_asc_fifo_flush(d);
1161 #else
1162 d->reg_ro[NCR_STEP] |= 4;
1163 #endif
1164 break;
1165 #endif
1166
1167 case NCRCMD_TRANS:
1168 if (!quiet_mode)
1169 debug("TRANS");
1170
1171 {
1172 int ok;
1173
1174 ok = dev_asc_transfer(cpu, d,
1175 idata & NCRCMD_DMA? 1 : 0);
1176 if (!ok) {
1177 d->cur_state = STATE_DISCONNECTED;
1178 d->reg_ro[NCR_INTR] |= NCRINTR_DIS;
1179 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
1180 d->reg_ro[NCR_STEP] = (d->reg_ro[
1181 NCR_STEP] & ~7) | 0;
1182 if (d->xferp != NULL)
1183 scsi_transfer_free(d->xferp);
1184 d->xferp = NULL;
1185 }
1186 }
1187 break;
1188
1189 default:
1190 fatal("(unimplemented asc cmd 0x%02x)", (int)idata);
1191 d->reg_ro[NCR_STAT] |= NCRSTAT_INT;
1192 d->reg_ro[NCR_INTR] |= NCRINTR_ILL;
1193 /*
1194 * TODO: exit or continue with Illegal command
1195 * interrupt?
1196 */
1197 exit(1);
1198 }
1199 }
1200
1201 if (regnr == NCR_INTR && writeflag == MEM_READ) {
1202 /*
1203 * Reading the interrupt register de-asserts the
1204 * interrupt pin. Also, INTR, STEP, and STAT are all
1205 * cleared, according to page 64 of the LSI53CF92A manual,
1206 * if "interrupt output is true".
1207 */
1208 if (d->reg_ro[NCR_STAT] & NCRSTAT_INT) {
1209 d->reg_ro[NCR_INTR] = 0;
1210 d->reg_ro[NCR_STEP] = 0;
1211 d->reg_ro[NCR_STAT] = 0;
1212
1213 /* For Mach/PMAX? TODO */
1214 d->reg_ro[NCR_STAT] = PHASE_COMMAND;
1215 }
1216
1217 cpu_interrupt_ack(cpu, d->irq_nr);
1218 }
1219
1220 if (regnr == NCR_CFG1) {
1221 /* TODO: other bits */
1222 if (!quiet_mode) {
1223 debug(" parity %s,", d->reg_ro[regnr] &
1224 NCRCFG1_PARENB? "enabled" : "disabled");
1225 debug(" scsi_id %i", d->reg_ro[regnr] & 0x7);
1226 }
1227 }
1228
1229 #ifdef ASC_FULL_REGISTER_ACCESS_DEBUG
1230 debug(" ]\n");
1231 #endif
1232 dev_asc_tick(cpu, extra);
1233
1234 if (writeflag == MEM_READ)
1235 memory_writemax64(cpu, data, len, odata);
1236
1237 return 1;
1238 }
1239
1240
1241 /*
1242 * dev_asc_init():
1243 *
1244 * Register an 'asc' device.
1245 */
1246 void dev_asc_init(struct machine *machine, struct memory *mem,
1247 uint64_t baseaddr, int irq_nr, void *turbochannel,
1248 int mode,
1249 size_t (*dma_controller)(void *dma_controller_data,
1250 unsigned char *data, size_t len, int writeflag),
1251 void *dma_controller_data)
1252 {
1253 struct asc_data *d;
1254
1255 d = malloc(sizeof(struct asc_data));
1256 if (d == NULL) {
1257 fprintf(stderr, "out of memory\n");
1258 exit(1);
1259 }
1260 memset(d, 0, sizeof(struct asc_data));
1261 d->irq_nr = irq_nr;
1262 d->turbochannel = turbochannel;
1263 d->mode = mode;
1264
1265 d->reg_ro[NCR_CFG3] = NCRF9XCFG3_CDB;
1266
1267 d->dma_address_reg_memory = malloc(machine->arch_pagesize);
1268 d->dma = malloc(ASC_DMA_SIZE);
1269 if (d->dma == NULL || d->dma_address_reg_memory == NULL) {
1270 fprintf(stderr, "out of memory\n");
1271 exit(1);
1272 }
1273 memset(d->dma_address_reg_memory, 0, machine->arch_pagesize);
1274 memset(d->dma, 0, ASC_DMA_SIZE);
1275
1276 d->dma_controller = dma_controller;
1277 d->dma_controller_data = dma_controller_data;
1278
1279 memory_device_register(mem, "asc", baseaddr,
1280 mode == DEV_ASC_PICA?
1281 DEV_ASC_PICA_LENGTH : DEV_ASC_DEC_LENGTH,
1282 dev_asc_access, d, MEM_DEFAULT, NULL);
1283
1284 if (mode == DEV_ASC_DEC) {
1285 memory_device_register(mem, "asc_dma_address_reg",
1286 baseaddr + 0x40000, 4096, dev_asc_address_reg_access, d,
1287 MEM_DYNTRANS_OK | MEM_DYNTRANS_WRITE_OK,
1288 (unsigned char *)&d->dma_address_reg_memory[0]);
1289 memory_device_register(mem, "asc_dma", baseaddr + 0x80000,
1290 ASC_DMA_SIZE, dev_asc_dma_access, d,
1291 MEM_DYNTRANS_OK | MEM_DYNTRANS_WRITE_OK, d->dma);
1292 }
1293
1294 machine_add_tickfunction(machine, dev_asc_tick, d, ASC_TICK_SHIFT);
1295 }
1296

  ViewVC Help
Powered by ViewVC 1.1.26