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: DC7085 serial controller, used in some DECstation models
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include "console.h"
36 #include "cpu.h"
37 #include "devices.h"
38 #include "machine.h"
39 #include "memory.h"
40 #include "misc.h"
41 
42 #include "thirdparty/dc7085.h"
43 
44 
45 #define	DC_TICK_SHIFT		14
46 
47 #define	MAX_QUEUE_LEN		32768
48 
49 struct dc_data {
50 	struct dc7085regs	regs;
51 
52 	int			console_handle;
53 
54 	/*  For slow_serial_interrupts_hack_for_linux:  */
55 	int			just_transmitted_something;
56 
57 	unsigned char		rx_queue_char[MAX_QUEUE_LEN];
58 	char			rx_queue_lineno[MAX_QUEUE_LEN];
59 	int			cur_rx_queue_pos_write;
60 	int			cur_rx_queue_pos_read;
61 
62 	int			tx_scanner;
63 
64 	struct interrupt	irq;
65 	int			use_fb;
66 
67 	struct lk201_data	lk201;
68 };
69 
70 
71 /*
72  *  Add a character to the receive queue.
73  */
add_to_rx_queue(void * e,int ch,int line_no)74 void add_to_rx_queue(void *e, int ch, int line_no)
75 {
76 	struct dc_data *d = (struct dc_data *) e;
77 	int entries_in_use = d->cur_rx_queue_pos_write -
78 	    d->cur_rx_queue_pos_read;
79 
80 	while (entries_in_use < 0)
81 		entries_in_use += MAX_QUEUE_LEN;
82 
83 	/*  Ignore mouse updates, if they come too often:  */
84 	if (entries_in_use > MAX_QUEUE_LEN/2 && line_no == DCMOUSE_PORT)
85 		return;
86 
87 	d->rx_queue_char[d->cur_rx_queue_pos_write]   = ch;
88 	d->rx_queue_lineno[d->cur_rx_queue_pos_write] = line_no;
89 	d->cur_rx_queue_pos_write ++;
90 	if (d->cur_rx_queue_pos_write == MAX_QUEUE_LEN)
91 		d->cur_rx_queue_pos_write = 0;
92 
93 	if (d->cur_rx_queue_pos_write == d->cur_rx_queue_pos_read)
94 		fatal("warning: add_to_rx_queue(): rx_queue overrun!\n");
95 }
96 
97 
DEVICE_TICK(dc7085)98 DEVICE_TICK(dc7085)
99 {
100 	/*
101 	 *  If a key is available from the keyboard, add it to the rx queue.
102 	 *  If other bits are set, an interrupt might need to be caused.
103 	 */
104 	struct dc_data *d = (struct dc_data *) extra;
105 	int avail;
106 
107 	if (cpu->machine->slow_serial_interrupts_hack_for_linux) {
108 		/*
109 		 *  Special hack to prevent Linux from Oopsing. (This makes
110 		 *  interrupts not come as fast as possible.)
111 		 */
112 		if (d->just_transmitted_something) {
113 			d->just_transmitted_something --;
114 			return;
115 		}
116 	}
117 
118 	d->regs.dc_csr &= ~CSR_RDONE;
119 
120 	if ((d->regs.dc_csr & CSR_MSE) && !(d->regs.dc_csr & CSR_TRDY)) {
121 		int scanner_start = d->tx_scanner;
122 
123 		/*  Loop until we've checked all 4 channels, or some
124 			channel was ready to transmit:  */
125 
126 		do {
127 			d->tx_scanner = (d->tx_scanner + 1) % 4;
128 
129 			if (d->regs.dc_tcr & (1 << d->tx_scanner)) {
130 				d->regs.dc_csr |= CSR_TRDY;
131 				if (d->regs.dc_csr & CSR_TIE)
132 					INTERRUPT_ASSERT(d->irq);
133 
134 				d->regs.dc_csr &= ~CSR_TX_LINE_NUM;
135 				d->regs.dc_csr |= (d->tx_scanner << 8);
136 			}
137 		} while (!(d->regs.dc_csr & CSR_TRDY) &&
138 		    d->tx_scanner != scanner_start);
139 
140 		/*  We have to return here. NetBSD can handle both
141 		    rx and tx interrupts simultaneously, but Ultrix
142 		    doesn't like that?  */
143 
144 		if (d->regs.dc_csr & CSR_TRDY)
145 			return;
146 	}
147 
148 	lk201_tick(cpu->machine, &d->lk201);
149 
150 	avail = d->cur_rx_queue_pos_write != d->cur_rx_queue_pos_read;
151 
152 	if (avail && (d->regs.dc_csr & CSR_MSE))
153 		d->regs.dc_csr |= CSR_RDONE;
154 
155 	if ((d->regs.dc_csr & CSR_RDONE) && (d->regs.dc_csr & CSR_RIE))
156 		INTERRUPT_ASSERT(d->irq);
157 }
158 
159 
DEVICE_ACCESS(dc7085)160 DEVICE_ACCESS(dc7085)
161 {
162 	struct dc_data *d = (struct dc_data *) extra;
163 	uint64_t idata = 0, odata = 0;
164 	size_t i;
165 
166 	if (writeflag == MEM_WRITE)
167 		idata = memory_readmax64(cpu, data, len);
168 
169 	/*  Always clear:  */
170 	d->regs.dc_csr &= ~CSR_CLR;
171 
172 	switch (relative_addr) {
173 
174 	case 0x00:	/*  CSR:  Control and Status  */
175 		if (writeflag == MEM_WRITE) {
176 			debug("[ dc7085 write to CSR: 0x%04x ]\n", idata);
177 			idata &= (CSR_TIE | CSR_RIE | CSR_MSE | CSR_CLR
178 			    | CSR_MAINT);
179 			d->regs.dc_csr &= ~(CSR_TIE | CSR_RIE | CSR_MSE
180 			    | CSR_CLR | CSR_MAINT);
181 			d->regs.dc_csr |= idata;
182 			if (!(d->regs.dc_csr & CSR_MSE))
183 				d->regs.dc_csr &= ~(CSR_TRDY | CSR_RDONE);
184 			goto do_return;
185 		} else {
186 			/*  read:  */
187 
188 			/*  fatal("[ dc7085 read from CSR: (csr = 0x%04x) ]\n",
189 			    d->regs.dc_csr);  */
190 			odata = d->regs.dc_csr;
191 		}
192 		break;
193 
194 	case 0x08:	/*  LPR:  */
195 		if (writeflag == MEM_WRITE) {
196 			debug("[ dc7085 write to LPR: 0x%04x ]\n", idata);
197 			d->regs.dc_rbuf_lpr = idata;
198 			goto do_return;
199 		} else {
200 			/*  read:  */
201 			int avail = d->cur_rx_queue_pos_write !=
202 			    d->cur_rx_queue_pos_read;
203 			int ch = 0, lineno = 0;
204 			/*  debug("[ dc7085 read from RBUF: ");  */
205 			if (avail) {
206 				ch = d->rx_queue_char[d->cur_rx_queue_pos_read];
207 				lineno = d->rx_queue_lineno[
208 				    d->cur_rx_queue_pos_read];
209 				d->cur_rx_queue_pos_read++;
210 				if (d->cur_rx_queue_pos_read == MAX_QUEUE_LEN)
211 					d->cur_rx_queue_pos_read = 0;
212 				/*  if (ch >= ' ' && ch < 127)
213 					debug("'%c'", ch);
214 				else
215 					debug("0x%x", ch);
216 				debug(" for lineno %i ", lineno);  */
217 			}  /*  else
218 				debug("empty ");
219 			debug("]\n");  */
220 			odata = (avail? RBUF_DVAL:0) |
221 			    (lineno << RBUF_LINE_NUM_SHIFT) | ch;
222 
223 			d->regs.dc_csr &= ~CSR_RDONE;
224 			INTERRUPT_DEASSERT(d->irq);
225 
226 			d->just_transmitted_something = 4;
227 		}
228 		break;
229 
230 	case 0x10:	/*  TCR:  */
231 		if (writeflag == MEM_WRITE) {
232 			/*  fatal("[ dc7085 write to TCR: 0x%04x) ]\n",
233 			    (int)idata);  */
234 			d->regs.dc_tcr = idata;
235 			d->regs.dc_csr &= ~CSR_TRDY;
236 			INTERRUPT_DEASSERT(d->irq);
237 			goto do_return;
238 		} else {
239 			/*  read:  */
240 			/*  debug("[ dc7085 read from TCR: (tcr = 0x%04x) ]\n",
241 			    d->regs.dc_tcr);  */
242 			odata = d->regs.dc_tcr;
243 		}
244 		break;
245 
246 	case 0x18:	/*  Modem status (R), transmit data (W)  */
247 		if (writeflag == MEM_WRITE) {
248 			int line_no = (d->regs.dc_csr >>
249 			    RBUF_LINE_NUM_SHIFT) & 0x3;
250 			idata &= 0xff;
251 
252 			lk201_tx_data(&d->lk201, line_no, idata);
253 
254 			d->regs.dc_csr &= ~CSR_TRDY;
255 			INTERRUPT_DEASSERT(d->irq);
256 
257 			d->just_transmitted_something = 4;
258 		} else {
259 			/*  read:  */
260 			d->regs.dc_msr_tdr |= MSR_DSR2 | MSR_CD2 |
261 			    MSR_DSR3 | MSR_CD3;
262 			debug("[ dc7085 read from MSR: (msr_tdr = 0x%04x) ]\n",
263 			    d->regs.dc_msr_tdr);
264 			odata = d->regs.dc_msr_tdr;
265 		}
266 		break;
267 
268 	default:
269 		if (writeflag==MEM_READ) {
270 			debug("[ dc7085 read from 0x%08lx ]\n",
271 			    (long)relative_addr);
272 		} else {
273 			debug("[ dc7085 write to 0x%08lx:",
274 			    (long)relative_addr);
275 			for (i=0; i<len; i++)
276 				debug(" %02x", data[i]);
277 			debug(" ]\n");
278 		}
279 	}
280 
281 	if (writeflag == MEM_READ)
282 		memory_writemax64(cpu, data, len, odata);
283 
284 do_return:
285 	dev_dc7085_tick(cpu, extra);
286 
287 	return 1;
288 }
289 
290 
291 /*
292  *  dev_dc7085_init():
293  *
294  *  Initialize a dc7085 serial controller device. use_fb should be non-zero
295  *  if a framebuffer device is used. Channel 0 will then be treated as a
296  *  DECstation keyboard, instead of a plain serial console.
297  */
dev_dc7085_init(struct machine * machine,struct memory * mem,uint64_t baseaddr,char * irq_path,int use_fb)298 int dev_dc7085_init(struct machine *machine, struct memory *mem,
299 	uint64_t baseaddr, char *irq_path, int use_fb)
300 {
301 	struct dc_data *d;
302 
303 	CHECK_ALLOCATION(d = (struct dc_data *) malloc(sizeof(struct dc_data)));
304 	memset(d, 0, sizeof(struct dc_data));
305 
306 	INTERRUPT_CONNECT(irq_path, d->irq);
307 	d->use_fb = use_fb;
308 
309 	d->regs.dc_csr = CSR_TRDY | CSR_MSE;
310 	d->regs.dc_tcr = 0x00;
311 
312 	d->console_handle = console_start_slave(machine, "DC7085", 1);
313 
314 	lk201_init(&d->lk201, use_fb, add_to_rx_queue, d->console_handle, d);
315 
316 	memory_device_register(mem, "dc7085", baseaddr, DEV_DC7085_LENGTH,
317 	    dev_dc7085_access, d, DM_DEFAULT, NULL);
318 	machine_add_tickfunction(machine, dev_dc7085_tick, d,
319 	    DC_TICK_SHIFT);
320 
321 	return d->console_handle;
322 }
323 
324