/[gxemul]/upstream/0.3.6/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

Contents of /upstream/0.3.6/src/devices/dev_ns16550.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 15 - (show annotations)
Mon Oct 8 16:18:56 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 10358 byte(s)
0.3.6
1 /*
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 * $Id: dev_ns16550.c,v 1.41 2005/09/30 13:33:01 debug Exp $
29 *
30 * NS16550 serial controller.
31 *
32 *
33 * TODO: Implement the FIFO.
34 */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "console.h"
41 #include "cpu.h"
42 #include "device.h"
43 #include "machine.h"
44 #include "memory.h"
45 #include "misc.h"
46
47 #include "comreg.h"
48
49
50 /* #define debug fatal */
51
52 #define TICK_SHIFT 14
53 #define DEV_NS16550_LENGTH 8
54
55 struct ns_data {
56 int addrmult;
57 int in_use;
58 int irqnr;
59 char *name;
60 int console_handle;
61 int enable_fifo;
62
63 unsigned char reg[DEV_NS16550_LENGTH];
64 unsigned char fcr; /* FIFO control register */
65
66 int dlab; /* Divisor Latch Access bit */
67 int divisor;
68
69 int databits;
70 char parity;
71 const char *stopbits;
72 };
73
74
75 /*
76 * dev_ns16550_tick():
77 *
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 */
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 if (d->in_use && console_charavail(d->console_handle))
88 d->reg[com_iir] |= IIR_RXRDY;
89
90 /*
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 d->reg[com_iir] &= ~IIR_NOPEND;
97 if (d->reg[com_mcr] & MCR_IENABLE)
98 cpu_interrupt(cpu, d->irqnr);
99 } else {
100 d->reg[com_iir] |= IIR_NOPEND;
101 cpu_interrupt_ack(cpu, d->irqnr);
102 }
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 idata = memory_readmax64(cpu, data, len);
118
119 /* The NS16550 should be accessed using byte read/writes: */
120 if (len != 1)
121 fatal("[ ns16550 (%s): len=%i, idata=0x%16llx! ]\n",
122 d->name, len, (long long)idata);
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 (d->in_use && 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 if (d->in_use)
169 odata = console_readchar(d->console_handle);
170 else
171 odata = 0;
172 }
173 dev_ns16550_tick(cpu, d);
174 break;
175
176 case com_ier: /* interrupt enable AND high byte of the divisor */
177 /* Read/write of the Divisor value: */
178 if (d->dlab) {
179 if (writeflag == MEM_WRITE) {
180 /* Set the high byte of the divisor: */
181 d->divisor = (d->divisor & 0xff) | (idata << 8);
182 debug("[ ns16550 (%s): speed set to %i bps ]\n",
183 d->name, (int)(115200 / d->divisor));
184 } else
185 odata = d->divisor >> 8;
186 break;
187 }
188
189 /* IER: */
190 if (writeflag == MEM_WRITE) {
191 /* This is to supress Linux' behaviour */
192 if (idata != 0)
193 debug("[ ns16550 (%s): write to ier: 0x%02x ]"
194 "\n", d->name, (int)idata);
195
196 /* Needed for NetBSD 2.0.x, but not 1.6.2? */
197 if (!(d->reg[com_ier] & IER_ETXRDY)
198 && (idata & IER_ETXRDY))
199 d->reg[com_iir] |= IIR_TXRDY;
200
201 d->reg[com_ier] = idata;
202 dev_ns16550_tick(cpu, d);
203 } else
204 odata = d->reg[com_ier];
205 break;
206
207 case com_iir: /* interrupt identification (r), fifo control (w) */
208 if (writeflag == MEM_WRITE) {
209 debug("[ ns16550 (%s): write to fifo control: 0x%02x ]"
210 "\n", d->name, (int)idata);
211 d->fcr = idata;
212 } else {
213 odata = d->reg[com_iir];
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 } else {
288 odata = d->reg[com_mcr];
289 debug("[ ns16550 (%s): read from mcr: 0x%02x ]\n",
290 d->name, (int)odata);
291 }
292 break;
293
294 default:
295 if (writeflag==MEM_READ) {
296 debug("[ ns16550 (%s): read from reg %i ]\n",
297 d->name, (int)relative_addr);
298 odata = d->reg[relative_addr];
299 } else {
300 debug("[ ns16550 (%s): write to reg %i:",
301 d->name, (int)relative_addr);
302 for (i=0; i<len; i++)
303 debug(" %02x", data[i]);
304 debug(" ]\n");
305 d->reg[relative_addr] = idata;
306 }
307 }
308
309 if (writeflag == MEM_READ)
310 memory_writemax64(cpu, data, len, odata);
311
312 return 1;
313 }
314
315
316 /*
317 * devinit_ns16550():
318 */
319 int devinit_ns16550(struct devinit *devinit)
320 {
321 struct ns_data *d = malloc(sizeof(struct ns_data));
322 size_t nlen;
323 char *name;
324
325 if (d == NULL) {
326 fprintf(stderr, "out of memory\n");
327 exit(1);
328 }
329 memset(d, 0, sizeof(struct ns_data));
330 d->irqnr = devinit->irq_nr;
331 d->addrmult = devinit->addr_mult;
332 d->in_use = devinit->in_use;
333 d->enable_fifo = 1;
334 d->dlab = 0;
335 d->divisor = 115200 / 9600;
336 d->databits = 8;
337 d->parity = 'N';
338 d->stopbits = "1";
339 d->name = devinit->name2 != NULL? devinit->name2 : "";
340 d->console_handle =
341 console_start_slave(devinit->machine, devinit->name);
342
343 nlen = strlen(devinit->name) + 10;
344 if (devinit->name2 != NULL)
345 nlen += strlen(devinit->name2);
346 name = malloc(nlen);
347 if (name == NULL) {
348 fprintf(stderr, "out of memory\n");
349 exit(1);
350 }
351 if (devinit->name2 != NULL && devinit->name2[0])
352 snprintf(name, nlen, "%s [%s]", devinit->name, devinit->name2);
353 else
354 snprintf(name, nlen, "%s", devinit->name);
355
356 memory_device_register(devinit->machine->memory, name, devinit->addr,
357 DEV_NS16550_LENGTH * d->addrmult, dev_ns16550_access, d,
358 MEM_DEFAULT, NULL);
359 machine_add_tickfunction(devinit->machine,
360 dev_ns16550_tick, d, TICK_SHIFT);
361
362 /*
363 * NOTE: Ugly cast into a pointer, because this is a convenient way
364 * to return the console handle to code in src/machine.c.
365 */
366 devinit->return_ptr = (void *)(size_t)d->console_handle;
367
368 return 1;
369 }
370

  ViewVC Help
Powered by ViewVC 1.1.26