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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.4  
changed lines
  Added in v.24

  ViewVC Help
Powered by ViewVC 1.1.26