1 /*
2 * Copyright (C) 2003-2009 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 * COMMENT: SII SCSI controller, used in some DECstation systems
29 *
30 * TODO: This is huge and ugly. Fix this.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "cpu.h"
38 #include "devices.h"
39 #include "interrupt.h"
40 #include "machine.h"
41 #include "memory.h"
42 #include "misc.h"
43
44 #include "thirdparty/siireg.h"
45
46
47 #define SII_TICK_SHIFT 14
48
49
50 struct sii_data {
51 struct interrupt irq;
52
53 uint64_t buf_start;
54 uint64_t buf_end;
55
56 int connected;
57 int connected_to_id;
58
59 int register_choice;
60 SIIRegs siiregs;
61 uint16_t *regs;
62 };
63
64
65 /*
66 * combine_sii_bits():
67 *
68 * Combines some bits of CSTAT and DSTAT that are connected.
69 */
combine_sii_bits(struct sii_data * d)70 void combine_sii_bits(struct sii_data *d)
71 {
72 int ci, di;
73
74 di = ((d->siiregs.dstat & SII_MIS) |
75 (d->siiregs.dstat & SII_IBF) |
76 (d->siiregs.dstat & SII_TBE) |
77 (d->siiregs.dstat & SII_DNE))==0? 0 : SII_DI;
78
79 ci = ((d->siiregs.cstat & SII_RST) |
80 (d->siiregs.cstat & SII_BER) |
81 (d->siiregs.cstat & SII_OBC) |
82 (d->siiregs.cstat & SII_BUF) |
83 (d->siiregs.cstat & SII_LDN) |
84 (d->siiregs.cstat & SII_SCH))==0? 0 : SII_CI;
85
86 d->siiregs.cstat &= ~(SII_CI | SII_DI);
87 d->siiregs.dstat &= ~(SII_CI | SII_DI);
88
89 d->siiregs.cstat |= (ci | di);
90 d->siiregs.dstat |= (ci | di);
91 }
92
93
DEVICE_TICK(sii)94 DEVICE_TICK(sii)
95 {
96 struct sii_data *d = (struct sii_data *) extra;
97
98 /* ? */
99 d->siiregs.dstat = (d->siiregs.dstat & ~0x7)
100 | ((d->siiregs.dstat + 1) & 0x7);
101
102 /* SCSI Commands: */
103
104 if (d->siiregs.comm & SII_CHRESET) { /* (I,T,D) */
105 /* debug("[ sii: command TODO: CHRESET ]\n"); */
106 }
107
108 if (d->siiregs.comm & SII_DISCON) { /* (I,T,D) */
109 /* debug("[ sii: command TODO: DISCON ]\n"); */
110 d->siiregs.cstat &= ~SII_CON; /* Connected */
111
112 if (d->connected) {
113 d->siiregs.cstat |= SII_SCH; /* State change */
114 d->connected = 0;
115 }
116
117 d->siiregs.cstat &= ~SII_SIP; /* Selection in progress */
118 d->siiregs.comm &= ~SII_DISCON;
119 }
120
121 if (d->siiregs.comm & SII_REQDATA) { /* (T) */
122 /* debug("[ sii: command TODO: REQDATA ]\n"); */
123 }
124
125 if (d->siiregs.comm & SII_SELECT) { /* (D) */
126 /* debug("[ sii: command SELECT ]\n"); */
127 d->siiregs.comm &= ~SII_SELECT;
128
129 /* slcsr contains the other target's id */
130 d->siiregs.cstat |= SII_SIP; /* Selection in progress */
131 d->connected = 0;
132 d->connected_to_id = 0;
133
134 /* Is the target available for selection?
135 TODO: make this nicer */
136 #if 0
137 if ((d->siiregs.slcsr & 7) == 0) {
138 d->siiregs.cstat |= SII_CON; /* Connected */
139 d->siiregs.cstat |= SII_SCH; /* State change */
140 d->siiregs.cstat &= ~SII_SIP; /* Sel. in progress */
141
142 d->connected = 1;
143 d->connected_to_id = 0;
144 }
145 #endif
146 }
147
148 if (d->siiregs.comm & SII_INXFER
149 && (d->siiregs.comm & 0x70) == (d->siiregs.cstat & 0x70) &&
150 (d->siiregs.comm & 0x03) == (d->siiregs.dstat & 0x03) &&
151 !(d->siiregs.cstat & SII_SIP)) { /* (I,T) */
152 debug("[ sii: command INXFER to scsiid=%i ]\n",
153 d->siiregs.slcsr);
154 if (d->siiregs.comm & SII_DMA)
155 debug("[ sii DMA: TODO ]\n");
156 else {
157 debug("[ sii: transmitting byte 0x%02x using "
158 "PIO mode ]\n", d->siiregs.data);
159 d->siiregs.comm &= ~SII_INXFER;
160
161 /* d->siiregs.dstat |= SII_DNE; */
162 /* Done, only for DMA? */
163 d->siiregs.dstat |= SII_TBE; /* Buffer empty? */
164 }
165 }
166
167 combine_sii_bits(d);
168
169 if (d->siiregs.csr & SII_IE && d->siiregs.cstat & (SII_CI | SII_DI))
170 INTERRUPT_ASSERT(d->irq);
171 else
172 INTERRUPT_DEASSERT(d->irq);
173 }
174
175
DEVICE_ACCESS(sii)176 DEVICE_ACCESS(sii)
177 {
178 uint64_t idata = 0, odata = 0;
179 int regnr;
180 struct sii_data *d = (struct sii_data *) extra;
181
182 if (relative_addr & 3) {
183 debug("[ sii relative_addr = 0x%x !!! ]\n",
184 (int) relative_addr);
185 return 0;
186 }
187
188 dev_sii_tick(cpu, extra);
189
190 if (writeflag == MEM_WRITE)
191 idata = memory_readmax64(cpu, data, len);
192
193 regnr = relative_addr / 2;
194 odata = d->regs[regnr];
195
196 switch (relative_addr) {
197 case 0x00: /* SII_SDB: Diagnostic */
198 if (writeflag == MEM_READ) {
199 debug("[ sii: read from SDB (data=0x%04x) ]\n",
200 d->regs[regnr]);
201 } else {
202 debug("[ sii: write to SDB (data=0x%04x) ]\n",
203 (int)idata);
204 d->regs[regnr] = idata;
205 return 1;
206 }
207 break;
208 case 0x0c: /* SII_CSR: Control/status */
209 if (writeflag == MEM_READ) {
210 debug("[ sii: read from CSR (data=0x%04x) ]\n",
211 d->regs[regnr]);
212 } else {
213 debug("[ sii: write to CSR (data=0x%04x: %s %s "
214 "%s %s %s) ]\n", (int)idata,
215 idata & SII_HPM? "HPM" : "!hpm",
216 idata & SII_RSE? "RSE" : "!rse",
217 idata & SII_SLE? "SLE" : "!sle",
218 idata & SII_PCE? "PCE" : "!pce",
219 idata & SII_IE? "IE" : "!ie");
220 d->regs[regnr] = idata;
221 return 1;
222 }
223 break;
224 case 0x10: /* SII_ID: SCSI ID */
225 if (writeflag == MEM_READ) {
226 debug("[ sii: read from ID (data=0x%04x) ]\n",
227 d->regs[regnr]);
228 } else {
229 debug("[ sii: write to ID (data=0x%04x: scsi id %i)"
230 " ]\n", (int)idata, (int)(idata & 7));
231 if (!(idata & SII_ID_IO))
232 debug("WARNING: sii ID bit SII_ID_IO not "
233 "set on write!\n");
234 idata &= ~SII_ID_IO;
235 if ((idata & ~0x7) != 0)
236 debug("WARNING: sii ID bits that should "
237 "be zero are not zero!\n");
238 idata &= 0x7;
239 d->regs[regnr] = idata & 0x7;
240 return 1;
241 }
242 break;
243 case 0x14: /* SII_SLCSR: Selector control */
244 if (writeflag == MEM_READ) {
245 debug("[ sii: read from SLCSR (data=0x%04x: "
246 "scsi_id=%i) ]\n", d->regs[regnr],
247 d->regs[regnr] & 7);
248 } else {
249 debug("[ sii: write to SLCSR (data=0x%04x: "
250 "scsi_id=%i) ]\n", (int)idata, (int)(idata & 7));
251 if ((idata & ~0x7) != 0)
252 debug("WARNING: sii SLCSR bits that should "
253 "be zero are not zero!\n");
254 idata &= 0x7;
255 d->regs[regnr] = idata & 0x7;
256 return 1;
257 }
258 break;
259 case 0x18: /* SII_DESTAT: Selection detector status */
260 if (writeflag == MEM_READ) {
261 /* TODO: set DESTAT from somewhere else? */
262 debug("[ sii: read from DESTAT (data=0x%04x: "
263 "scsi_id=%i) ]\n", d->regs[regnr],
264 d->regs[regnr] & 7);
265 } else {
266 debug("[ sii: write to DESTAT (data=0x%04x: "
267 "scsi_id=%i) ]\n", (int)idata, (int)(idata & 7));
268 debug("WARNING: sii DESTAT is read-only!\n");
269 return 1;
270 }
271 break;
272 case 0x20: /* SII_DATA: Data register */
273 if (writeflag == MEM_READ) {
274 /* TODO */
275 debug("[ sii: read from DATA (data=0x%04x) ]\n",
276 d->regs[regnr]);
277 } else {
278 /* TODO */
279 debug("[ sii: write to DATA (data=0x%04x) ]\n",
280 (int)idata);
281 idata &= 0xff;
282 d->regs[regnr] = idata;
283 return 1;
284 }
285 break;
286 case 0x24: /* SII_DMCTRL: DMA control */
287 if (writeflag == MEM_READ) {
288 debug("[ sii: read from DMCTRL (data=0x%04x) ]\n",
289 d->regs[regnr]);
290 } else {
291 debug("[ sii: write to DMCTRL (data=0x%04x: %s) ]\n",
292 (int)idata, (idata & 3)==0? "async" : "sync");
293 if ((idata & ~0x3) != 0)
294 debug("WARNING: sii DMCTRL bits that "
295 "should be zero are not zero!\n");
296 idata &= 0x3;
297 d->regs[regnr] = idata;
298 return 1;
299 }
300 break;
301 case 0x48: /* SII_CSTAT: Connection status */
302 if (writeflag == MEM_READ) {
303 debug("[ sii: read from CSTAT (data=0x%04x) ]\n",
304 d->regs[regnr]);
305 } else {
306 debug("[ sii: write to CSTAT (data=0x%04x) ]\n",
307 (int)idata);
308
309 /* readonly / writeoncetoclear bits according
310 to page 21 in the DS3100 manual: */
311 if (idata & (1<<13)) {
312 idata &= ~(1<<13); d->regs[regnr] &= ~(1<<13);
313 }
314 if (idata & (1<<12)) {
315 idata &= ~(1<<12); d->regs[regnr] &= ~(1<<12);
316 }
317 if (idata & (1<<11)) {
318 /* is this actually write-1-to-clear? */
319 idata &= ~(1<<11); d->regs[regnr] &= ~(1<<11);
320 }
321 if (idata & (1<<9)) {
322 /* ? */
323 idata &= ~(1<<9); d->regs[regnr] &= ~(1<<9);
324 }
325 if (idata & (1<<8)) {
326 /* ? */
327 idata &= ~(1<<8); d->regs[regnr] &= ~(1<<8);
328 }
329 if (idata & (1<<7)) {
330 idata &= ~(1<<7); d->regs[regnr] &= ~(1<<7);
331 }
332 if (idata & (1<<3)) {
333 idata &= ~(1<<3); d->regs[regnr] &= ~(1<<3);
334 }
335
336 /* Read-only bits are taken from the old register: */
337 idata &= ~0x3bf7;
338 idata |= d->regs[regnr] & 0x3bf7;
339
340 d->regs[regnr] = idata;
341 return 1;
342 }
343 break;
344 case 0x4c: /* SII_DSTAT: Data transfer status */
345 if (writeflag == MEM_READ) {
346 debug("[ sii: read from DSTAT (data=0x%04x) ]\n",
347 d->regs[regnr]);
348 } else {
349 debug("[ sii: write to DSTAT (data=0x%04x) ]\n",
350 (int)idata);
351
352 /* readonly / writeoncetoclear bits
353 according to page 22 in the DS3100 manual: */
354 if (idata & (1<<13)) {
355 idata &= ~(1<<13); d->regs[regnr] &= ~(1<<13);
356 }
357 if (idata & (1<<11)) {
358 /* is this write-1-to-clear? */
359 idata &= ~(1<<11); d->regs[regnr] &= ~(1<<11);
360 }
361 if (idata & (1<<10)) {
362 /* is this write-1-to-clear? */
363 idata &= ~(1<<10); d->regs[regnr] &= ~(1<<10);
364 }
365 if (idata & (1<<4)) {
366 /* is this write-1-to-clear? */
367 idata &= ~(1<<4); d->regs[regnr] &= ~(1<<4);
368 }
369 if (idata & (1<<3)) {
370 idata &= ~(1<<3); d->regs[regnr] &= ~(1<<3);
371 }
372
373 /* Read-only bits are taken from the old register: */
374 idata &= ~0x0c17;
375 idata |= d->regs[regnr] & 0x0c17;
376
377 d->regs[regnr] = idata;
378 return 1;
379 }
380 break;
381 case 0x50: /* SII_COMM: Command */
382 if (writeflag == MEM_READ) {
383 debug("[ sii: read from COMM (data=0x%04x) ]\n",
384 d->regs[regnr]);
385 } else {
386 debug("[ sii: write to COMM (data=0x%04x: %s %s "
387 "%s command=0x%02x rest=0x%02x) ]\n", (int)idata,
388 idata & SII_DMA? "DMA" : "!dma",
389 idata & SII_DO_RST? "RST" : "!rst",
390 idata & SII_RSL? "RSL" : "!rsl",
391 /* command, 5 bits: */
392 (int)((idata >> 7) & 0x1f),
393 /* rest, 7 bits: */
394 (int)(idata & 0x3f));
395
396 if (idata & SII_DO_RST) {
397 /* Reset: TODO */
398 }
399
400 idata &= ~SII_DO_RST;
401 d->regs[regnr] = idata;
402
403 dev_sii_tick(cpu, extra);
404 return 1;
405 }
406 break;
407 case 0x54: /* SII_DICTRL: Diagnostics control */
408 if (writeflag == MEM_READ) {
409 debug("[ sii: read from DICTRL (data=0x%04x) ]\n",
410 d->regs[regnr]);
411 } else {
412 debug("[ sii: write to DICTRL (data=0x%04x: "
413 "port=%s) ]\n", (int)idata,
414 idata & SII_PRE? "enabled" : "disabled");
415 if ((idata & ~0xf) != 0)
416 debug("WARNING: sii DICTRL bits that "
417 "should be zero are not zero!\n");
418 d->regs[regnr] = idata;
419 return 1;
420 }
421 break;
422 default:
423 if (writeflag==MEM_READ) {
424 debug("[ sii: read from %08lx (data=0x%04x) ]\n",
425 (long)relative_addr, d->regs[regnr]);
426 } else {
427 debug("[ sii: write to %08lx (data=0x%04x) ]\n",
428 (long)relative_addr, (int)idata);
429 d->regs[regnr] = idata;
430 }
431 }
432
433 if (writeflag == MEM_READ)
434 memory_writemax64(cpu, data, len, odata);
435
436 return 1;
437 }
438
439
dev_sii_init(struct machine * machine,struct memory * mem,uint64_t baseaddr,uint64_t buf_start,uint64_t buf_end,char * irq_path)440 void dev_sii_init(struct machine *machine, struct memory *mem,
441 uint64_t baseaddr, uint64_t buf_start, uint64_t buf_end,
442 char *irq_path)
443 {
444 struct sii_data *d;
445
446 CHECK_ALLOCATION(d = (struct sii_data *) malloc(sizeof(struct sii_data)));
447 memset(d, 0, sizeof(struct sii_data));
448
449 INTERRUPT_CONNECT(irq_path, d->irq);
450 d->buf_start = buf_start;
451 d->buf_end = buf_end;
452 d->regs = (uint16_t *) &d->siiregs;
453
454 memory_device_register(mem, "sii", baseaddr, DEV_SII_LENGTH,
455 dev_sii_access, (void *)d, DM_DEFAULT, NULL);
456
457 machine_add_tickfunction(machine, dev_sii_tick, d,
458 SII_TICK_SHIFT);
459 }
460
461