/[gxemul]/upstream/0.3.1/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.1/devices/dev_ns16550.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (show annotations)
Mon Oct 8 16:17:52 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 8914 byte(s)
0.3.1
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.32 2005/02/19 11:51:33 debug Exp $
29 *
30 * NS16550 serial controller.
31 *
32 * TODO: actually implement the fifo :)
33 */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include "console.h"
40 #include "cpu.h"
41 #include "devices.h"
42 #include "machine.h"
43 #include "memory.h"
44 #include "misc.h"
45
46 #include "comreg.h"
47
48
49 /* #define debug fatal */
50
51 #define NS16550_TICK_SHIFT 14
52
53 /* #define DISABLE_FIFO */
54
55
56 struct ns_data {
57 int reg[8];
58
59 int irqnr;
60 int console_handle;
61
62 int irq_enable;
63 int addrmult;
64 int in_use;
65 int dlab; /* Divisor Latch Access bit */
66 int divisor;
67 int databits;
68 char parity;
69 const char *stopbits;
70 };
71
72
73 /*
74 * dev_ns16550_tick():
75 */
76 void dev_ns16550_tick(struct cpu *cpu, void *extra)
77 {
78 struct ns_data *d = extra;
79
80 d->reg[com_iir] |= IIR_NOPEND;
81 cpu_interrupt_ack(cpu, d->irqnr);
82
83 d->reg[com_iir] &= ~IIR_RXRDY;
84 if (d->in_use) {
85 if (console_charavail(d->console_handle))
86 d->reg[com_iir] |= IIR_RXRDY;
87 }
88
89 if ((d->irq_enable & IER_ETXRDY && d->reg[com_iir] & IIR_TXRDY) ||
90 (d->irq_enable & IER_ERXRDY && d->reg[com_iir] & IIR_RXRDY)) {
91 d->reg[com_iir] &= ~IIR_NOPEND;
92 if (d->reg[com_mcr] & MCR_IENABLE)
93 cpu_interrupt(cpu, d->irqnr);
94 }
95 }
96
97
98 /*
99 * dev_ns16550_access():
100 */
101 int dev_ns16550_access(struct cpu *cpu, struct memory *mem,
102 uint64_t relative_addr, unsigned char *data, size_t len,
103 int writeflag, void *extra)
104 {
105 uint64_t idata = 0, odata=0;
106 int i;
107 struct ns_data *d = extra;
108
109 idata = memory_readmax64(cpu, data, len);
110
111 /* Always ready to transmit: */
112 d->reg[com_lsr] |= LSR_TXRDY | LSR_TSRE;
113 d->reg[com_lsr] &= ~LSR_RXRDY;
114 d->reg[com_msr] = MSR_DCD | MSR_DSR | MSR_CTS;
115
116 #ifdef DISABLE_FIFO
117 /* FIFO turned off: */
118 d->reg[com_iir] &= 0x0f;
119 #endif
120
121 if (d->in_use) {
122 if (console_charavail(d->console_handle)) {
123 d->reg[com_lsr] |= LSR_RXRDY;
124 }
125 }
126
127 relative_addr /= d->addrmult;
128
129 switch (relative_addr) {
130 case com_data: /* com_data or com_dlbl */
131 /* Read/write of the Divisor value: */
132 if (d->dlab) {
133 if (writeflag == MEM_WRITE) {
134 /* Set the low byte of the divisor: */
135 d->divisor &= ~0xff;
136 d->divisor |= (idata & 0xff);
137 } else {
138 odata = d->divisor & 0xff;
139 }
140 break;
141 }
142
143 /* Read write of data: */
144 if (writeflag == MEM_WRITE) {
145 if (d->reg[com_mcr] & MCR_LOOPBACK) {
146 console_makeavail(d->console_handle, idata);
147 } else {
148 #if 0
149 /* Ugly hack: don't show form feeds: */
150 if (idata != 12)
151 #endif
152 console_putchar(d->console_handle, idata);
153 }
154
155 d->reg[com_iir] |= IIR_TXRDY;
156 dev_ns16550_tick(cpu, d);
157 return 1;
158 } else {
159 if (d->in_use)
160 odata = console_readchar(d->console_handle);
161 else
162 odata = 0;
163 dev_ns16550_tick(cpu, d);
164 }
165 break;
166 case com_ier: /* interrupt enable AND high byte of the divisor */
167 /* Read/write of the Divisor value: */
168 if (d->dlab) {
169 if (writeflag == MEM_WRITE) {
170 /* Set the high byte of the divisor: */
171 d->divisor &= ~0xff00;
172 d->divisor |= ((idata & 0xff) << 8);
173 debug("[ ns16550 speed set to %i bps ]\n",
174 115200 / d->divisor);
175 } else {
176 odata = (d->divisor & 0xff00) >> 8;
177 }
178 break;
179 }
180
181 /* IER: */
182 if (writeflag == MEM_WRITE) {
183 /* This is to supress Linux' behaviour */
184 if (idata != 0)
185 debug("[ ns16550 write to ier: 0x%02x ]\n",
186 idata);
187
188 /* Needed for NetBSD 2.0, but not 1.6.2? */
189 if (!(d->irq_enable & IER_ETXRDY)
190 && (idata & IER_ETXRDY))
191 d->reg[com_iir] |= IIR_TXRDY;
192
193 d->irq_enable = idata;
194 dev_ns16550_tick(cpu, d);
195 } else {
196 odata = d->reg[relative_addr];
197 }
198 break;
199 case com_iir: /* interrupt identification (r), fifo control (w) */
200 if (writeflag == MEM_WRITE) {
201 debug("[ ns16550 write to fifo control ]\n");
202 d->reg[relative_addr] = idata;
203 } else {
204 odata = d->reg[relative_addr];
205 debug("[ ns16550 read from iir: 0x%02x ]\n", odata);
206 dev_ns16550_tick(cpu, d);
207 }
208 break;
209 case com_lsr:
210 if (writeflag == MEM_WRITE) {
211 debug("[ ns16550 write to lsr ]\n");
212 d->reg[relative_addr] = idata;
213 } else {
214 odata = d->reg[relative_addr];
215 }
216 break;
217 case com_msr:
218 if (writeflag == MEM_WRITE) {
219 debug("[ ns16550 write to msr ]\n");
220 d->reg[relative_addr] = idata;
221 } else {
222 odata = d->reg[relative_addr];
223 }
224 break;
225 case com_lctl:
226 if (writeflag == MEM_WRITE) {
227 d->reg[relative_addr] = idata;
228 switch (idata & 0x7) {
229 case 0: d->databits = 5; d->stopbits = "1"; break;
230 case 1: d->databits = 6; d->stopbits = "1"; break;
231 case 2: d->databits = 7; d->stopbits = "1"; break;
232 case 3: d->databits = 8; d->stopbits = "1"; break;
233 case 4: d->databits = 5; d->stopbits = "1.5"; break;
234 case 5: d->databits = 6; d->stopbits = "2"; break;
235 case 6: d->databits = 7; d->stopbits = "2"; break;
236 case 7: d->databits = 8; d->stopbits = "2"; break;
237 }
238 switch ((idata & 0x38) / 0x8) {
239 case 0: d->parity = 'N'; break; /* none */
240 case 1: d->parity = 'O'; break; /* odd */
241 case 2: d->parity = '?'; break;
242 case 3: d->parity = 'E'; break; /* even */
243 case 4: d->parity = '?'; break;
244 case 5: d->parity = 'Z'; break; /* zero */
245 case 6: d->parity = '?'; break;
246 case 7: d->parity = 'o'; break; /* one */
247 }
248
249 d->dlab = idata & 0x80? 1 : 0;
250
251 debug("[ ns16550 write to lctl: 0x%02x (%s%s"
252 "setting mode %i%c%s) ]\n",
253 (int)idata,
254 d->dlab? "Divisor Latch access, " : "",
255 idata&0x40? "sending BREAK, " : "",
256 d->databits, d->parity, d->stopbits);
257 } else {
258 odata = d->reg[relative_addr];
259 debug("[ ns16550 read from lctl: 0x%02x ]\n", odata);
260 }
261 break;
262 case com_mcr:
263 if (writeflag == MEM_WRITE) {
264 d->reg[relative_addr] = idata;
265 debug("[ ns16550 write to mcr: 0x%02x ]\n", idata);
266 } else {
267 odata = d->reg[relative_addr];
268 debug("[ ns16550 read from mcr: 0x%02x ]\n", odata);
269 }
270 break;
271 default:
272 if (writeflag==MEM_READ) {
273 debug("[ ns16550 read from reg %i ]\n",
274 (int)relative_addr);
275 odata = d->reg[relative_addr];
276 } else {
277 debug("[ ns16550 write to reg %i:",
278 (int)relative_addr);
279 for (i=0; i<len; i++)
280 debug(" %02x", data[i]);
281 debug(" ]\n");
282 d->reg[relative_addr] = idata;
283 }
284 }
285
286 if (writeflag == MEM_READ)
287 memory_writemax64(cpu, data, len, odata);
288
289 return 1;
290 }
291
292
293 /*
294 * dev_ns16550_init():
295 */
296 int dev_ns16550_init(struct machine *machine, struct memory *mem,
297 uint64_t baseaddr, int irq_nr, int addrmult, int in_use,
298 char *name)
299 {
300 struct ns_data *d;
301 char *name2;
302
303 d = malloc(sizeof(struct ns_data));
304 if (d == NULL) {
305 fprintf(stderr, "out of memory\n");
306 exit(1);
307 }
308 memset(d, 0, sizeof(struct ns_data));
309 d->irqnr = irq_nr;
310 d->addrmult = addrmult;
311 d->in_use = in_use;
312 d->dlab = 0;
313 d->divisor = 115200 / 9600;
314 d->databits = 8;
315 d->parity = 'N';
316 d->stopbits = "1";
317 d->console_handle = console_start_slave(machine, name);
318
319 name2 = malloc(strlen(name) + 20);
320 if (name2 == NULL) {
321 fprintf(stderr, "out of memory in dev_ns16550_init()\n");
322 exit(1);
323 }
324 if (name != NULL && name[0])
325 sprintf(name2, "ns16550 [%s]", name);
326 else
327 sprintf(name2, "ns16550");
328
329 memory_device_register(mem, name2, baseaddr,
330 DEV_NS16550_LENGTH * addrmult, dev_ns16550_access, d,
331 MEM_DEFAULT, NULL);
332 machine_add_tickfunction(machine, dev_ns16550_tick,
333 d, NS16550_TICK_SHIFT);
334
335 return d->console_handle;
336 }
337

  ViewVC Help
Powered by ViewVC 1.1.26