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