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: NS16550 serial controller
29  *
30  *  TODO: Implement the FIFO.
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "console.h"
38 #include "cpu.h"
39 #include "device.h"
40 #include "interrupt.h"
41 #include "machine.h"
42 #include "memory.h"
43 #include "misc.h"
44 
45 #include "thirdparty/comreg.h"
46 
47 
48 /*  #define debug fatal  */
49 
50 #define	TICK_SHIFT		14
51 #define	DEV_NS16550_LENGTH	8
52 
53 struct ns_data {
54 	int		addrmult;
55 	int		in_use;
56 	const char	*name;
57 	int		console_handle;
58 	int		enable_fifo;
59 
60 	struct interrupt irq;
61 
62 	unsigned char	reg[DEV_NS16550_LENGTH];
63 	unsigned char	fcr;		/*  FIFO control register  */
64 	int		int_asserted;
65 	int		dlab;		/*  Divisor Latch Access bit  */
66 	int		divisor;
67 
68 	int		databits;
69 	char		parity;
70 	const char	*stopbits;
71 };
72 
73 
DEVICE_TICK(ns16550)74 DEVICE_TICK(ns16550)
75 {
76 	/*
77 	 *  This function is called at regular intervals. An interrupt is
78 	 *  asserted if there is a character available for reading, or if the
79 	 *  transmitter slot is empty (i.e. the ns16550 is ready to transmit).
80 	 */
81 	struct ns_data *d = (struct ns_data *) extra;
82 
83 	d->reg[com_iir] &= ~IIR_RXRDY;
84 	if (console_charavail(d->console_handle))
85 		d->reg[com_iir] |= IIR_RXRDY;
86 
87 	/*
88 	 *  If interrupts are enabled, and interrupts are pending, then
89 	 *  cause a CPU interrupt.
90  	 */
91 
92 	if (((d->reg[com_ier] & IER_ETXRDY) && (d->reg[com_iir] & IIR_TXRDY)) ||
93 	    ((d->reg[com_ier] & IER_ERXRDY) && (d->reg[com_iir] & IIR_RXRDY))) {
94 		d->reg[com_iir] &= ~IIR_NOPEND;
95 		if (d->reg[com_mcr] & MCR_IENABLE) {
96 			INTERRUPT_ASSERT(d->irq);
97 			d->int_asserted = 1;
98 		}
99 	} else {
100 		d->reg[com_iir] |= IIR_NOPEND;
101 		if (d->int_asserted)
102 			INTERRUPT_DEASSERT(d->irq);
103 		d->int_asserted = 0;
104 	}
105 }
106 
107 
DEVICE_ACCESS(ns16550)108 DEVICE_ACCESS(ns16550)
109 {
110 	uint64_t idata = 0, odata=0;
111 	size_t i;
112 	struct ns_data *d = (struct ns_data *) extra;
113 
114 	if (writeflag == MEM_WRITE)
115 		idata = memory_readmax64(cpu, data, len);
116 
117 #if 0
118 	/*  The NS16550 should be accessed using byte read/writes:  */
119 	if (len != 1)
120 		fatal("[ ns16550 (%s): len=%i, idata=0x%16llx! ]\n",
121 		    d->name, len, (long long)idata);
122 #endif
123 
124 	/*
125 	 *  Always ready to transmit:
126 	 */
127 	d->reg[com_lsr] |= LSR_TXRDY | LSR_TSRE;
128 	d->reg[com_msr] |= MSR_DCD | MSR_DSR | MSR_CTS;
129 
130 	d->reg[com_iir] &= ~0xf0;
131 	if (d->enable_fifo)
132 		d->reg[com_iir] |= ((d->fcr << 5) & 0xc0);
133 
134 	d->reg[com_lsr] &= ~LSR_RXRDY;
135 	if (console_charavail(d->console_handle))
136 		d->reg[com_lsr] |= LSR_RXRDY;
137 
138 	relative_addr /= d->addrmult;
139 
140 	if (relative_addr >= DEV_NS16550_LENGTH) {
141 		fatal("[ ns16550 (%s): outside register space? relative_addr="
142 		    "0x%llx. bad addrmult? bad device length? ]\n", d->name,
143 		    (long long)relative_addr);
144 		return 0;
145 	}
146 
147 	switch (relative_addr) {
148 
149 	case com_data:	/*  data AND low byte of the divisor  */
150 		/*  Read/write of the Divisor value:  */
151 		if (d->dlab) {
152 			/*  Write or read the low byte of the divisor:  */
153 			if (writeflag == MEM_WRITE)
154 				d->divisor = (d->divisor & 0xff00) | idata;
155 			else
156 				odata = d->divisor & 0xff;
157 			break;
158 		}
159 
160 		/*  Read/write of data:  */
161 		if (writeflag == MEM_WRITE) {
162 			if (d->reg[com_mcr] & MCR_LOOPBACK)
163 				console_makeavail(d->console_handle, idata);
164 			else
165 				console_putchar(d->console_handle, idata);
166 			d->reg[com_iir] |= IIR_TXRDY;
167 		} else {
168 			int x = console_readchar(d->console_handle);
169 			odata = x < 0? 0 : x;
170 		}
171 		dev_ns16550_tick(cpu, d);
172 		break;
173 
174 	case com_ier:	/*  interrupt enable AND high byte of the divisor  */
175 		/*  Read/write of the Divisor value:  */
176 		if (d->dlab) {
177 			if (writeflag == MEM_WRITE) {
178 				/*  Set the high byte of the divisor:  */
179 				d->divisor = (d->divisor & 0xff) | (idata << 8);
180 				debug("[ ns16550 (%s): speed set to %i bps ]\n",
181 				    d->name, (int)(115200 / d->divisor));
182 			} else
183 				odata = d->divisor >> 8;
184 			break;
185 		}
186 
187 		/*  IER:  */
188 		if (writeflag == MEM_WRITE) {
189 			/*  This is to supress Linux' behaviour  */
190 			if (idata != 0)
191 				debug("[ ns16550 (%s): write to ier: 0x%02x ]"
192 				    "\n", d->name, (int)idata);
193 
194 			/*  Needed for NetBSD 2.0.x, but not 1.6.2?  */
195 			if (!(d->reg[com_ier] & IER_ETXRDY)
196 			    && (idata & IER_ETXRDY))
197 				d->reg[com_iir] |= IIR_TXRDY;
198 
199 			d->reg[com_ier] = idata;
200 			dev_ns16550_tick(cpu, d);
201 		} else
202 			odata = d->reg[com_ier];
203 		break;
204 
205 	case com_iir:	/*  interrupt identification (r), fifo control (w)  */
206 		if (writeflag == MEM_WRITE) {
207 			debug("[ ns16550 (%s): write to fifo control: 0x%02x ]"
208 			    "\n", d->name, (int)idata);
209 			d->fcr = idata;
210 		} else {
211 			odata = d->reg[com_iir];
212 			if (d->reg[com_iir] & IIR_TXRDY)
213 				d->reg[com_iir] &= ~IIR_TXRDY;
214 			debug("[ ns16550 (%s): read from iir: 0x%02x ]\n",
215 			    d->name, (int)odata);
216 			dev_ns16550_tick(cpu, d);
217 		}
218 		break;
219 
220 	case com_lsr:
221 		if (writeflag == MEM_WRITE) {
222 			debug("[ ns16550 (%s): write to lsr: 0x%02x ]\n",
223 			    d->name, (int)idata);
224 			d->reg[com_lsr] = idata;
225 		} else {
226 			odata = d->reg[com_lsr];
227 			/*  debug("[ ns16550 (%s): read from lsr: 0x%02x ]\n",
228 			    d->name, (int)odata);  */
229 		}
230 		break;
231 
232 	case com_msr:
233 		if (writeflag == MEM_WRITE) {
234 			debug("[ ns16550 (%s): write to msr: 0x%02x ]\n",
235 			    d->name, (int)idata);
236 			d->reg[com_msr] = idata;
237 		} else {
238 			odata = d->reg[com_msr];
239 			debug("[ ns16550 (%s): read from msr: 0x%02x ]\n",
240 			    d->name, (int)odata);
241 		}
242 		break;
243 
244 	case com_lctl:
245 		if (writeflag == MEM_WRITE) {
246 			d->reg[com_lctl] = idata;
247 			switch (idata & 0x7) {
248 			case 0:	d->databits = 5; d->stopbits = "1"; break;
249 			case 1:	d->databits = 6; d->stopbits = "1"; break;
250 			case 2:	d->databits = 7; d->stopbits = "1"; break;
251 			case 3:	d->databits = 8; d->stopbits = "1"; break;
252 			case 4:	d->databits = 5; d->stopbits = "1.5"; break;
253 			case 5:	d->databits = 6; d->stopbits = "2"; break;
254 			case 6:	d->databits = 7; d->stopbits = "2"; break;
255 			case 7:	d->databits = 8; d->stopbits = "2"; break;
256 			}
257 			switch ((idata & 0x38) / 0x8) {
258 			case 0:	d->parity = 'N'; break;		/*  none  */
259 			case 1:	d->parity = 'O'; break;		/*  odd  */
260 			case 2:	d->parity = '?'; break;
261 			case 3:	d->parity = 'E'; break;		/*  even  */
262 			case 4:	d->parity = '?'; break;
263 			case 5:	d->parity = 'Z'; break;		/*  zero  */
264 			case 6:	d->parity = '?'; break;
265 			case 7:	d->parity = 'o'; break;		/*  one  */
266 			}
267 
268 			d->dlab = idata & 0x80? 1 : 0;
269 
270 			debug("[ ns16550 (%s): write to lctl: 0x%02x (%s%s"
271 			    "setting mode %i%c%s) ]\n", d->name, (int)idata,
272 			    d->dlab? "Divisor Latch access, " : "",
273 			    idata&0x40? "sending BREAK, " : "",
274 			    d->databits, d->parity, d->stopbits);
275 		} else {
276 			odata = d->reg[com_lctl];
277 			debug("[ ns16550 (%s): read from lctl: 0x%02x ]\n",
278 			    d->name, (int)odata);
279 		}
280 		break;
281 
282 	case com_mcr:
283 		if (writeflag == MEM_WRITE) {
284 			d->reg[com_mcr] = idata;
285 			debug("[ ns16550 (%s): write to mcr: 0x%02x ]\n",
286 			    d->name, (int)idata);
287 			if (!(d->reg[com_iir] & IIR_TXRDY)
288 			    && (idata & MCR_IENABLE))
289 				d->reg[com_iir] |= IIR_TXRDY;
290 			dev_ns16550_tick(cpu, d);
291 		} else {
292 			odata = d->reg[com_mcr];
293 			debug("[ ns16550 (%s): read from mcr: 0x%02x ]\n",
294 			    d->name, (int)odata);
295 		}
296 		break;
297 
298 	default:
299 		if (writeflag==MEM_READ) {
300 			debug("[ ns16550 (%s): read from reg %i ]\n",
301 			    d->name, (int)relative_addr);
302 			odata = d->reg[relative_addr];
303 		} else {
304 			debug("[ ns16550 (%s): write to reg %i:",
305 			    d->name, (int)relative_addr);
306 			for (i=0; i<len; i++)
307 				debug(" %02x", data[i]);
308 			debug(" ]\n");
309 			d->reg[relative_addr] = idata;
310 		}
311 	}
312 
313 	if (writeflag == MEM_READ)
314 		memory_writemax64(cpu, data, len, odata);
315 
316 	return 1;
317 }
318 
319 
DEVINIT(ns16550)320 DEVINIT(ns16550)
321 {
322 	struct ns_data *d;
323 	size_t nlen;
324 	char *name;
325 
326 	CHECK_ALLOCATION(d = (struct ns_data *) malloc(sizeof(struct ns_data)));
327 	memset(d, 0, sizeof(struct ns_data));
328 
329 	d->addrmult	= devinit->addr_mult;
330 	d->in_use	= devinit->in_use;
331 	d->enable_fifo	= 1;
332 	d->dlab		= 0;
333 	d->divisor	= 115200 / 9600;
334 	d->databits	= 8;
335 	d->parity	= 'N';
336 	d->stopbits	= "1";
337 	d->name		= devinit->name2 != NULL? devinit->name2 : "";
338 	d->console_handle =
339 	    console_start_slave(devinit->machine, devinit->name2 != NULL?
340 	    devinit->name2 : devinit->name, d->in_use);
341 
342 	INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
343 
344 	nlen = strlen(devinit->name) + 10;
345 	if (devinit->name2 != NULL)
346 		nlen += strlen(devinit->name2);
347 	CHECK_ALLOCATION(name = (char *) malloc(nlen));
348 	if (devinit->name2 != NULL && devinit->name2[0])
349 		snprintf(name, nlen, "%s [%s]", devinit->name, devinit->name2);
350 	else
351 		snprintf(name, nlen, "%s", devinit->name);
352 
353 	memory_device_register(devinit->machine->memory, name, devinit->addr,
354 	    DEV_NS16550_LENGTH * d->addrmult, dev_ns16550_access, d,
355 	    DM_DEFAULT, NULL);
356 	machine_add_tickfunction(devinit->machine,
357 	    dev_ns16550_tick, d, TICK_SHIFT);
358 
359 	/*
360 	 *  NOTE:  Ugly cast into a pointer, because this is a convenient way
361 	 *         to return the console handle to code in src/machines/.
362 	 */
363 	devinit->return_ptr = (void *)(size_t)d->console_handle;
364 
365 	return 1;
366 }
367 
368