/[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

Annotation of /upstream/0.3.1/devices/dev_ns16550.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (hide annotations)
Mon Oct 8 16:17:52 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 8914 byte(s)
0.3.1
1 dpavlin 2 /*
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