/[gxemul]/trunk/src/devices/dev_ns16550.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/src/devices/dev_ns16550.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 18 - (hide annotations)
Mon Oct 8 16:19:11 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 10388 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1004 2005/10/27 14:01:10 debug Exp $
20051011        Passing -A as the default boot arg for CATS (works fine with
                OpenBSD/cats).
20051012	Fixing the VGA cursor offset bug, and speeding up framebuffer
		redraws if character cells contain the same thing as during
		the last redraw.
20051013	Adding a slow strd ARM instruction hack.
20051017	Minor updates: Adding a dummy i80321 Verde controller (for
		XScale emulation), fixing the disassembly of the ARM "ldrd"
		instruction, adding "support" for less-than-4KB pages for ARM
		(by not adding them to translation tables).
20051020	Continuing on some HPCarm stuff. A NetBSD/hpcarm kernel prints
		some boot messages on an emulated Jornada 720.
		Making dev_ram work better with dyntrans (speeds up some things
		quite a bit).
20051021	Automatically generating some of the most common ARM load/store
		multiple instructions.
20051022	Better statistics gathering for the ARM load/store multiple.
		Various other dyntrans and device updates.
20051023	Various minor updates.
20051024	Continuing; minor device and dyntrans fine-tuning. Adding the
		first "reasonable" instruction combination hacks for ARM (the
		cores of NetBSD/cats' memset and memcpy).
20051025	Fixing a dyntrans-related bug in dev_vga. Also changing the
		dyntrans low/high access notification to only be updated on
		writes, not reads. Hopefully it will be enough. (dev_vga in
		charcell mode now seems to work correctly with both reads and
		writes.)
		Experimenting with gathering dyntrans statistics (which parts
		of emulated RAM that are actually executed), and adding
		instruction combination hacks for cache cleaning and a part of
		NetBSD's scanc() function.
20051026	Adding a bitmap for ARM emulation which indicates if a page is
		(specifically) user accessible; loads and stores with the t-
		flag set can now use the translation arrays, which results in
		a measurable speedup.
20051027	Dyntrans updates; adding an extra bitmap array for 32-bit
		emulation modes, speeding up the check whether a physical page
		has any code translations or not (O(n) -> O(1)). Doing a
		similar reduction of O(n) to O(1) by avoiding the scan through
		the translation entries on a translation update (32-bit mode
		only).
		Various other minor hacks.
20051029	Quick release, without any testing at all.

==============  RELEASE 0.3.6.2  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26