--- trunk/src/devices/dev_ns16550.c 2007/10/08 16:18:31 11 +++ trunk/src/devices/dev_ns16550.c 2007/10/08 16:18:38 12 @@ -25,11 +25,12 @@ * SUCH DAMAGE. * * - * $Id: dev_ns16550.c,v 1.33 2005/06/26 11:43:48 debug Exp $ + * $Id: dev_ns16550.c,v 1.40 2005/08/12 06:15:31 debug Exp $ * * NS16550 serial controller. * - * TODO: actually implement the fifo :) + * + * TODO: Implement the FIFO. */ #include @@ -38,7 +39,7 @@ #include "console.h" #include "cpu.h" -#include "devices.h" +#include "device.h" #include "machine.h" #include "memory.h" #include "misc.h" @@ -46,24 +47,24 @@ #include "comreg.h" -/* #define debug fatal */ - -#define NS16550_TICK_SHIFT 14 - -/* #define DISABLE_FIFO */ +/* #define debug fatal */ +#define TICK_SHIFT 14 +#define DEV_NS16550_LENGTH 8 struct ns_data { - int reg[8]; - + int addrmult; + int in_use; int irqnr; int console_handle; + int enable_fifo; + + unsigned char reg[DEV_NS16550_LENGTH]; + unsigned char fcr; /* FIFO control register */ - int irq_enable; - int addrmult; - int in_use; int dlab; /* Divisor Latch Access bit */ int divisor; + int databits; char parity; const char *stopbits; @@ -72,25 +73,31 @@ /* * dev_ns16550_tick(): + * + * This function is called at regular intervals. An interrupt is caused to the + * CPU if there is a character available for reading, or if the transmitter + * slot is empty (i.e. the ns16550 is ready to transmit). */ void dev_ns16550_tick(struct cpu *cpu, void *extra) { struct ns_data *d = extra; - d->reg[com_iir] |= IIR_NOPEND; - cpu_interrupt_ack(cpu, d->irqnr); - d->reg[com_iir] &= ~IIR_RXRDY; - if (d->in_use) { - if (console_charavail(d->console_handle)) - d->reg[com_iir] |= IIR_RXRDY; - } + if (d->in_use && console_charavail(d->console_handle)) + d->reg[com_iir] |= IIR_RXRDY; - if ((d->irq_enable & IER_ETXRDY && d->reg[com_iir] & IIR_TXRDY) || - (d->irq_enable & IER_ERXRDY && d->reg[com_iir] & IIR_RXRDY)) { + /* + * If interrupts are enabled, and interrupts are pending, then + * cause a CPU interrupt. + */ + if (((d->reg[com_ier] & IER_ETXRDY) && (d->reg[com_iir] & IIR_TXRDY)) || + ((d->reg[com_ier] & IER_ERXRDY) && (d->reg[com_iir] & IIR_RXRDY))) { d->reg[com_iir] &= ~IIR_NOPEND; if (d->reg[com_mcr] & MCR_IENABLE) cpu_interrupt(cpu, d->irqnr); + } else { + d->reg[com_iir] |= IIR_NOPEND; + cpu_interrupt_ack(cpu, d->irqnr); } } @@ -108,73 +115,73 @@ idata = memory_readmax64(cpu, data, len); - /* Always ready to transmit: */ + /* The NS16550 should be accessed using byte read/writes: */ + if (len != 1) + fatal("[ ns16550: len=%i, idata=0x%16llx! ]\n", + len, (long long)idata); + + /* + * Always ready to transmit: + */ d->reg[com_lsr] |= LSR_TXRDY | LSR_TSRE; - 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; -#ifdef DISABLE_FIFO - /* FIFO turned off: */ - d->reg[com_iir] &= 0x0f; -#endif - - if (d->in_use) { - if (console_charavail(d->console_handle)) { - d->reg[com_lsr] |= LSR_RXRDY; - } - } + d->reg[com_iir] &= ~0xf0; + if (d->enable_fifo) + d->reg[com_iir] |= ((d->fcr << 5) & 0xc0); + + d->reg[com_lsr] &= ~LSR_RXRDY; + if (d->in_use && console_charavail(d->console_handle)) + d->reg[com_lsr] |= LSR_RXRDY; relative_addr /= d->addrmult; + if (relative_addr >= DEV_NS16550_LENGTH) { + fatal("[ ns16550: outside register space? relative_addr=" + "0x%llx. bad addrmult? bad device length? ]\n", + (long long)relative_addr); + return 0; + } + switch (relative_addr) { - case com_data: /* com_data or com_dlbl */ + + case com_data: /* data AND low byte of the divisor */ /* Read/write of the Divisor value: */ if (d->dlab) { - if (writeflag == MEM_WRITE) { - /* Set the low byte of the divisor: */ - d->divisor &= ~0xff; - d->divisor |= (idata & 0xff); - } else { + /* Write or read the low byte of the divisor: */ + if (writeflag == MEM_WRITE) + d->divisor = (d->divisor & 0xff00) | idata; + else odata = d->divisor & 0xff; - } break; } - /* Read write of data: */ + /* Read/write of data: */ if (writeflag == MEM_WRITE) { - if (d->reg[com_mcr] & MCR_LOOPBACK) { + if (d->reg[com_mcr] & MCR_LOOPBACK) console_makeavail(d->console_handle, idata); - } else { -#if 0 - /* Ugly hack: don't show form feeds: */ - if (idata != 12) -#endif + else console_putchar(d->console_handle, idata); - } - d->reg[com_iir] |= IIR_TXRDY; - dev_ns16550_tick(cpu, d); - return 1; } else { if (d->in_use) odata = console_readchar(d->console_handle); else odata = 0; - dev_ns16550_tick(cpu, d); } + dev_ns16550_tick(cpu, d); break; + case com_ier: /* interrupt enable AND high byte of the divisor */ /* Read/write of the Divisor value: */ if (d->dlab) { if (writeflag == MEM_WRITE) { /* Set the high byte of the divisor: */ - d->divisor &= ~0xff00; - d->divisor |= ((idata & 0xff) << 8); - debug("[ ns16550 speed set to %i bps ]\n", - 115200 / d->divisor); - } else { - odata = (d->divisor & 0xff00) >> 8; - } + d->divisor = (d->divisor & 0xff) | (idata << 8); + debug("[ ns16550: speed set to %i bps ]\n", + (int)(115200 / d->divisor)); + } else + odata = d->divisor >> 8; break; } @@ -182,49 +189,60 @@ if (writeflag == MEM_WRITE) { /* This is to supress Linux' behaviour */ if (idata != 0) - debug("[ ns16550 write to ier: 0x%02x ]\n", - idata); + debug("[ ns16550: write to ier: 0x%02x ]\n", + (int)idata); - /* Needed for NetBSD 2.0, but not 1.6.2? */ - if (!(d->irq_enable & IER_ETXRDY) + /* Needed for NetBSD 2.0.x, but not 1.6.2? */ + if (!(d->reg[com_ier] & IER_ETXRDY) && (idata & IER_ETXRDY)) d->reg[com_iir] |= IIR_TXRDY; - d->irq_enable = idata; + d->reg[com_ier] = idata; dev_ns16550_tick(cpu, d); - } else { - odata = d->reg[relative_addr]; - } + } else + odata = d->reg[com_ier]; break; + case com_iir: /* interrupt identification (r), fifo control (w) */ if (writeflag == MEM_WRITE) { - debug("[ ns16550 write to fifo control ]\n"); - d->reg[relative_addr] = idata; + debug("[ ns16550: write to fifo control: 0x%02x ]\n", + (int)idata); + d->fcr = idata; } else { - odata = d->reg[relative_addr]; - debug("[ ns16550 read from iir: 0x%02x ]\n", odata); + odata = d->reg[com_iir]; + debug("[ ns16550: read from iir: 0x%02x ]\n", + (int)odata); dev_ns16550_tick(cpu, d); } break; + case com_lsr: if (writeflag == MEM_WRITE) { - debug("[ ns16550 write to lsr ]\n"); - d->reg[relative_addr] = idata; + debug("[ ns16550: write to lsr: 0x%02x ]\n", + (int)idata); + d->reg[com_lsr] = idata; } else { - odata = d->reg[relative_addr]; + odata = d->reg[com_lsr]; + /* debug("[ ns16550: read from lsr: 0x%02x ]\n", + (int)odata); */ } break; + case com_msr: if (writeflag == MEM_WRITE) { - debug("[ ns16550 write to msr ]\n"); - d->reg[relative_addr] = idata; + debug("[ ns16550: write to msr: 0x%02x ]\n", + (int)idata); + d->reg[com_msr] = idata; } else { - odata = d->reg[relative_addr]; + odata = d->reg[com_msr]; + debug("[ ns16550: read from msr: 0x%02x ]\n", + (int)odata); } break; + case com_lctl: if (writeflag == MEM_WRITE) { - d->reg[relative_addr] = idata; + d->reg[com_lctl] = idata; switch (idata & 0x7) { case 0: d->databits = 5; d->stopbits = "1"; break; case 1: d->databits = 6; d->stopbits = "1"; break; @@ -248,33 +266,38 @@ d->dlab = idata & 0x80? 1 : 0; - debug("[ ns16550 write to lctl: 0x%02x (%s%s" + debug("[ ns16550: write to lctl: 0x%02x (%s%s" "setting mode %i%c%s) ]\n", (int)idata, d->dlab? "Divisor Latch access, " : "", idata&0x40? "sending BREAK, " : "", d->databits, d->parity, d->stopbits); } else { - odata = d->reg[relative_addr]; - debug("[ ns16550 read from lctl: 0x%02x ]\n", odata); + odata = d->reg[com_lctl]; + debug("[ ns16550: read from lctl: 0x%02x ]\n", + (int)odata); } break; + case com_mcr: if (writeflag == MEM_WRITE) { - d->reg[relative_addr] = idata; - debug("[ ns16550 write to mcr: 0x%02x ]\n", idata); + d->reg[com_mcr] = idata; + debug("[ ns16550: write to mcr: 0x%02x ]\n", + (int)idata); } else { - odata = d->reg[relative_addr]; - debug("[ ns16550 read from mcr: 0x%02x ]\n", odata); + odata = d->reg[com_mcr]; + debug("[ ns16550: read from mcr: 0x%02x ]\n", + (int)odata); } break; + default: if (writeflag==MEM_READ) { - debug("[ ns16550 read from reg %i ]\n", + debug("[ ns16550: read from reg %i ]\n", (int)relative_addr); odata = d->reg[relative_addr]; } else { - debug("[ ns16550 write to reg %i:", + debug("[ ns16550: write to reg %i:", (int)relative_addr); for (i=0; iirqnr = irq_nr; - d->addrmult = addrmult; - d->in_use = in_use; - d->dlab = 0; - d->divisor = 115200 / 9600; - d->databits = 8; - d->parity = 'N'; - d->stopbits = "1"; - d->console_handle = console_start_slave(machine, name); - - nlen = strlen(name) + 20; - name2 = malloc(nlen); - if (name2 == NULL) { - fprintf(stderr, "out of memory in dev_ns16550_init()\n"); + d->irqnr = devinit->irq_nr; + d->addrmult = devinit->addr_mult; + d->in_use = devinit->in_use; + d->enable_fifo = 1; + d->dlab = 0; + d->divisor = 115200 / 9600; + d->databits = 8; + d->parity = 'N'; + d->stopbits = "1"; + d->console_handle = + console_start_slave(devinit->machine, devinit->name); + + nlen = strlen(devinit->name) + 10; + if (devinit->name2 != NULL) + nlen += strlen(devinit->name2); + name = malloc(nlen); + if (name == NULL) { + fprintf(stderr, "out of memory\n"); exit(1); } - if (name != NULL && name[0]) - snprintf(name2, nlen, "ns16550 [%s]", name); + if (devinit->name2 != NULL && devinit->name2[0]) + snprintf(name, nlen, "%s [%s]", devinit->name, devinit->name2); else - snprintf(name2, nlen, "ns16550"); + snprintf(name, nlen, "%s", devinit->name); - memory_device_register(mem, name2, baseaddr, - DEV_NS16550_LENGTH * addrmult, dev_ns16550_access, d, + memory_device_register(devinit->machine->memory, name, devinit->addr, + DEV_NS16550_LENGTH * d->addrmult, dev_ns16550_access, d, MEM_DEFAULT, NULL); - machine_add_tickfunction(machine, dev_ns16550_tick, - d, NS16550_TICK_SHIFT); + machine_add_tickfunction(devinit->machine, + dev_ns16550_tick, d, TICK_SHIFT); + + /* + * NOTE: Ugly cast into a pointer, because this is a convenient way + * to return the console handle to code in src/machine.c. + */ + devinit->return_ptr = (void *)(size_t)d->console_handle; - return d->console_handle; + return 1; }