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