diff options
Diffstat (limited to 'sim/mn10300/dv-mn103ser.c')
-rw-r--r-- | sim/mn10300/dv-mn103ser.c | 712 |
1 files changed, 712 insertions, 0 deletions
diff --git a/sim/mn10300/dv-mn103ser.c b/sim/mn10300/dv-mn103ser.c new file mode 100644 index 00000000000..7f20ed1f6b2 --- /dev/null +++ b/sim/mn10300/dv-mn103ser.c @@ -0,0 +1,712 @@ +/* This file is part of the program GDB, the GNU debugger. + + Copyright (C) 1998 Free Software Foundation, Inc. + Contributed by Cygnus Solutions. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + +#include "sim-main.h" +#include "hw-main.h" +#include "dv-sockser.h" + + +/* DEVICE + + + mn103ser - mn103002 serial devices 0, 1 and 2. + + + DESCRIPTION + + Implements the mn103002 serial interfaces as described in the + mn103002 user guide. + + + PROPERTIES + + reg = <serial-addr> <serial-size> + + + BUGS + + */ + + +/* The serial devices' registers' address block */ + +struct mn103ser_block { + unsigned_word base; + unsigned_word bound; +}; + + + +enum serial_register_types { + SC0CTR, + SC1CTR, + SC2CTR, + SC0ICR, + SC1ICR, + SC2ICR, + SC0TXB, + SC1TXB, + SC2TXB, + SC0RXB, + SC1RXB, + SC2RXB, + SC0STR, + SC1STR, + SC2STR, + SC2TIM, +}; + + +/* Access dv-sockser state */ +extern char* sockser_addr; +#define USE_SOCKSER_P (sockser_addr != NULL) + + +#define NR_SERIAL_DEVS 3 +#define SIO_STAT_RRDY 0x0010 + +typedef struct _mn10300_serial { + unsigned16 status, control; + unsigned8 txb, rxb, intmode; + struct hw_event *event; +} mn10300_serial; + + + +struct mn103ser { + struct mn103ser_block block; + mn10300_serial device[NR_SERIAL_DEVS]; + unsigned8 serial2_timer_reg; + do_hw_poll_read_method *reader; +}; + +/* output port ID's */ + +/* for mn103002 */ +enum { + SERIAL0_RECEIVE, + SERIAL1_RECEIVE, + SERIAL2_RECEIVE, + SERIAL0_SEND, + SERIAL1_SEND, + SERIAL2_SEND, +}; + + +static const struct hw_port_descriptor mn103ser_ports[] = { + + { "serial-0-receive", SERIAL0_RECEIVE, 0, output_port, }, + { "serial-1-receive", SERIAL1_RECEIVE, 0, output_port, }, + { "serial-2-receive", SERIAL2_RECEIVE, 0, output_port, }, + { "serial-0-transmit", SERIAL0_SEND, 0, output_port, }, + { "serial-1-transmit", SERIAL1_SEND, 0, output_port, }, + { "serial-2-transmit", SERIAL2_SEND, 0, output_port, }, + + { NULL, }, +}; + + + +/* Finish off the partially created hw device. Attach our local + callbacks. Wire up our port names etc */ + +static hw_io_read_buffer_method mn103ser_io_read_buffer; +static hw_io_write_buffer_method mn103ser_io_write_buffer; + +static void +attach_mn103ser_regs (struct hw *me, + struct mn103ser *serial) +{ + unsigned_word attach_address; + int attach_space; + unsigned attach_size; + reg_property_spec reg; + + if (hw_find_property (me, "reg") == NULL) + hw_abort (me, "Missing \"reg\" property"); + + if (!hw_find_reg_array_property (me, "reg", 0, ®)) + hw_abort (me, "\"reg\" property must contain three addr/size entries"); + hw_unit_address_to_attach_address (hw_parent (me), + ®.address, + &attach_space, + &attach_address, + me); + serial->block.base = attach_address; + hw_unit_size_to_attach_size (hw_parent (me), + ®.size, + &attach_size, me); + serial->block.bound = attach_address + (attach_size - 1); + hw_attach_address (hw_parent (me), + 0, + attach_space, attach_address, attach_size, + me); +} + +static void +mn103ser_finish (struct hw *me) +{ + struct mn103ser *serial; + int i; + + serial = HW_ZALLOC (me, struct mn103ser); + set_hw_data (me, serial); + set_hw_io_read_buffer (me, mn103ser_io_read_buffer); + set_hw_io_write_buffer (me, mn103ser_io_write_buffer); + set_hw_ports (me, mn103ser_ports); + + /* Attach ourself to our parent bus */ + attach_mn103ser_regs (me, serial); + + /* If so configured, enable polled input */ + if (hw_find_property (me, "poll?") != NULL + && hw_find_boolean_property (me, "poll?")) + { + serial->reader = sim_io_poll_read; + } + else + { + serial->reader = sim_io_read; + } + + /* Initialize the serial device registers. */ + for ( i=0; i<NR_SERIAL_DEVS; ++i ) + { + serial->device[i].txb = 0; + serial->device[i].rxb = 0; + serial->device[i].status = 0; + serial->device[i].control = 0; + serial->device[i].intmode = 0; + serial->device[i].event = NULL; + } +} + + +/* read and write */ + +static int +decode_addr (struct hw *me, + struct mn103ser *serial, + unsigned_word address) +{ + unsigned_word offset; + offset = address - serial->block.base; + switch (offset) + { + case 0x00: return SC0CTR; + case 0x04: return SC0ICR; + case 0x08: return SC0TXB; + case 0x09: return SC0RXB; + case 0x0C: return SC0STR; + case 0x10: return SC1CTR; + case 0x14: return SC1ICR; + case 0x18: return SC1TXB; + case 0x19: return SC1RXB; + case 0x1C: return SC1STR; + case 0x20: return SC2CTR; + case 0x24: return SC2ICR; + case 0x28: return SC2TXB; + case 0x29: return SC2RXB; + case 0x2C: return SC2STR; + case 0x2D: return SC2TIM; + default: + { + hw_abort (me, "bad address"); + return -1; + } + } +} + +static void +do_polling_event (struct hw *me, + void *data) +{ + struct mn103ser *serial = hw_data(me); + int serial_reg = (int) data; + char c; + int count; + + if(USE_SOCKSER_P) + { + int rd; + rd = dv_sockser_read (hw_system (me)); + if(rd != -1) + { + c = (char) rd; + count = 1; + } + else + { + count = HW_IO_NOT_READY; + } + } + else + { + count = do_hw_poll_read (me, serial->reader, + 0/*STDIN*/, &c, sizeof(c)); + } + + + switch (count) + { + case HW_IO_NOT_READY: + case HW_IO_EOF: + serial->device[serial_reg].rxb = 0; + serial->device[serial_reg].status &= ~SIO_STAT_RRDY; + break; + default: + serial->device[serial_reg].rxb = c; + serial->device[serial_reg].status |= SIO_STAT_RRDY; + hw_port_event (me, serial_reg+SERIAL0_RECEIVE, 1); + } + + /* Schedule next polling event */ + serial->device[serial_reg].event + = hw_event_queue_schedule (me, 1000, + do_polling_event, (void *)serial_reg); + +} + +static void +read_control_reg (struct hw *me, + struct mn103ser *serial, + unsigned_word serial_reg, + void *dest, + unsigned nr_bytes) +{ + /* really allow 1 byte read, too */ + if ( nr_bytes == 2 ) + { + *(unsigned16 *)dest = H2LE_2 (serial->device[serial_reg].control); + } + else + { + hw_abort (me, "bad read size of %d bytes from SC%dCTR.", nr_bytes, + serial_reg); + } +} + + +static void +read_intmode_reg (struct hw *me, + struct mn103ser *serial, + unsigned_word serial_reg, + void *dest, + unsigned nr_bytes) +{ + if ( nr_bytes == 1 ) + { + *(unsigned8 *)dest = serial->device[serial_reg].intmode; + } + else + { + hw_abort (me, "bad read size of %d bytes from SC%dICR.", nr_bytes, + serial_reg); + } +} + + +static void +read_txb (struct hw *me, + struct mn103ser *serial, + unsigned_word serial_reg, + void *dest, + unsigned nr_bytes) +{ + if ( nr_bytes == 1 ) + { + *(unsigned8 *)dest = serial->device[serial_reg].txb; + } + else + { + hw_abort (me, "bad read size of %d bytes from SC%dTXB.", nr_bytes, + serial_reg); + } +} + + +static void +read_rxb (struct hw *me, + struct mn103ser *serial, + unsigned_word serial_reg, + void *dest, + unsigned nr_bytes) +{ + if ( nr_bytes == 1 ) + { + *(unsigned8 *)dest = serial->device[serial_reg].rxb; + /* Reception buffer is now empty. */ + serial->device[serial_reg].status &= ~SIO_STAT_RRDY; + } + else + { + hw_abort (me, "bad read size of %d bytes from SC%dRXB.", nr_bytes, + serial_reg); + } +} + + +static void +read_status_reg (struct hw *me, + struct mn103ser *serial, + unsigned_word serial_reg, + void *dest, + unsigned nr_bytes) +{ + char c; + int count; + + if ( (serial->device[serial_reg].status & SIO_STAT_RRDY) == 0 ) + { + /* FIFO is empty */ + /* Kill current poll event */ + if ( NULL != serial->device[serial_reg].event ) + { + hw_event_queue_deschedule (me, serial->device[serial_reg].event); + serial->device[serial_reg].event = NULL; + } + + if(USE_SOCKSER_P) + { + int rd; + rd = dv_sockser_read (hw_system (me)); + if(rd != -1) + { + c = (char) rd; + count = 1; + } + else + { + count = HW_IO_NOT_READY; + } + } + else + { + count = do_hw_poll_read (me, serial->reader, + 0/*STDIN*/, &c, sizeof(c)); + } + + switch (count) + { + case HW_IO_NOT_READY: + case HW_IO_EOF: + serial->device[serial_reg].rxb = 0; + serial->device[serial_reg].status &= ~SIO_STAT_RRDY; + break; + default: + serial->device[serial_reg].rxb = c; + serial->device[serial_reg].status |= SIO_STAT_RRDY; + hw_port_event (me, serial_reg+SERIAL0_RECEIVE, 1); + } + + /* schedule polling event */ + serial->device[serial_reg].event + = hw_event_queue_schedule (me, 1000, + do_polling_event, + (void *)serial_reg); + } + + if ( nr_bytes == 1 ) + { + *(unsigned8 *)dest = (unsigned8)serial->device[serial_reg].status; + } + else if ( nr_bytes == 2 && serial_reg != SC2STR ) + { + *(unsigned16 *)dest = H2LE_2 (serial->device[serial_reg].status); + } + else + { + hw_abort (me, "bad read size of %d bytes from SC%dSTR.", nr_bytes, + serial_reg); + } +} + + +static void +read_serial2_timer_reg (struct hw *me, + struct mn103ser *serial, + void *dest, + unsigned nr_bytes) +{ + if ( nr_bytes == 1 ) + { + * (unsigned8 *) dest = (unsigned8) serial->serial2_timer_reg; + } + else + { + hw_abort (me, "bad read size of %d bytes to SC2TIM.", nr_bytes); + } +} + + +static unsigned +mn103ser_io_read_buffer (struct hw *me, + void *dest, + int space, + unsigned_word base, + unsigned nr_bytes) +{ + struct mn103ser *serial = hw_data (me); + enum serial_register_types serial_reg; + HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes)); + + serial_reg = decode_addr (me, serial, base); + switch (serial_reg) + { + /* control registers */ + case SC0CTR: + case SC1CTR: + case SC2CTR: + read_control_reg(me, serial, serial_reg-SC0CTR, dest, nr_bytes); + HW_TRACE ((me, "read - ctrl reg%d has 0x%x\n", serial_reg-SC0CTR, + *(unsigned8 *)dest)); + break; + + /* interrupt mode registers */ + case SC0ICR: + case SC1ICR: + case SC2ICR: + read_intmode_reg(me, serial, serial_reg-SC0ICR, dest, nr_bytes); + HW_TRACE ((me, "read - intmode reg%d has 0x%x\n", serial_reg-SC0ICR, + *(unsigned8 *)dest)); + break; + + /* transmission buffers */ + case SC0TXB: + case SC1TXB: + case SC2TXB: + read_txb(me, serial, serial_reg-SC0TXB, dest, nr_bytes); + HW_TRACE ((me, "read - txb%d has %c\n", serial_reg-SC0TXB, + *(char *)dest)); + break; + + /* reception buffers */ + case SC0RXB: + case SC1RXB: + case SC2RXB: + read_rxb(me, serial, serial_reg-SC0RXB, dest, nr_bytes); + HW_TRACE ((me, "read - rxb%d has %c\n", serial_reg-SC0RXB, + *(char *)dest)); + break; + + /* status registers */ + case SC0STR: + case SC1STR: + case SC2STR: + read_status_reg(me, serial, serial_reg-SC0STR, dest, nr_bytes); + HW_TRACE ((me, "read - status reg%d has 0x%x\n", serial_reg-SC0STR, + *(unsigned8 *)dest)); + break; + + case SC2TIM: + read_serial2_timer_reg(me, serial, dest, nr_bytes); + HW_TRACE ((me, "read - serial2 timer reg %d\n", *(unsigned8 *)dest)); + break; + + default: + hw_abort(me, "invalid address"); + } + + return nr_bytes; +} + + +static void +write_control_reg (struct hw *me, + struct mn103ser *serial, + unsigned_word serial_reg, + const void *source, + unsigned nr_bytes) +{ + unsigned16 val = LE2H_2 (*(unsigned16 *)source); + + /* really allow 1 byte write, too */ + if ( nr_bytes == 2 ) + { + if ( serial_reg == 2 && (val & 0x0C04) != 0 ) + { + hw_abort(me, "Cannot write to read-only bits of SC2CTR."); + } + else + { + serial->device[serial_reg].control = val; + } + } + else + { + hw_abort (me, "bad read size of %d bytes from SC%dSTR.", nr_bytes, + serial_reg); + } +} + + +static void +write_intmode_reg (struct hw *me, + struct mn103ser *serial, + unsigned_word serial_reg, + const void *source, + unsigned nr_bytes) +{ +unsigned8 val = *(unsigned8 *)source; + + if ( nr_bytes == 1 ) + { + /* Check for attempt to write to read-only bits of register. */ + if ( ( serial_reg == 2 && (val & 0xCA) != 0 ) + || ( serial_reg != 2 && (val & 0x4A) != 0 ) ) + { + hw_abort(me, "Cannot write to read-only bits of SC%dICR.", + serial_reg); + } + else + { + serial->device[serial_reg].intmode = val; + } + } + else + { + hw_abort (me, "bad write size of %d bytes to SC%dICR.", nr_bytes, + serial_reg); + } +} + + +static void +write_txb (struct hw *me, + struct mn103ser *serial, + unsigned_word serial_reg, + const void *source, + unsigned nr_bytes) +{ + if ( nr_bytes == 1 ) + { + serial->device[serial_reg].txb = *(unsigned8 *)source; + + if(USE_SOCKSER_P) + { + dv_sockser_write(hw_system (me), * (char*) source); + } + else + { + sim_io_write_stdout(hw_system (me), (char *)source, 1); + sim_io_flush_stdout(hw_system (me)); + } + + hw_port_event (me, serial_reg+SERIAL0_SEND, 1); + } + else + { + hw_abort (me, "bad write size of %d bytes to SC%dTXB.", nr_bytes, + serial_reg); + } +} + + +static void +write_serial2_timer_reg (struct hw *me, + struct mn103ser *serial, + const void *source, + unsigned nr_bytes) +{ + if ( nr_bytes == 1 ) + { + serial->serial2_timer_reg = *(unsigned8 *)source; + } + else + { + hw_abort (me, "bad write size of %d bytes to SC2TIM.", nr_bytes); + } +} + + +static unsigned +mn103ser_io_write_buffer (struct hw *me, + const void *source, + int space, + unsigned_word base, + unsigned nr_bytes) +{ + struct mn103ser *serial = hw_data (me); + enum serial_register_types serial_reg; + HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes)); + + serial_reg = decode_addr (me, serial, base); + switch (serial_reg) + { + /* control registers */ + case SC0CTR: + case SC1CTR: + case SC2CTR: + HW_TRACE ((me, "write - ctrl reg%d has 0x%x, nrbytes=%d.\n", + serial_reg-SC0CTR, *(unsigned8 *)source, nr_bytes)); + write_control_reg(me, serial, serial_reg-SC0CTR, source, nr_bytes); + break; + + /* interrupt mode registers */ + case SC0ICR: + case SC1ICR: + case SC2ICR: + HW_TRACE ((me, "write - intmode reg%d has 0x%x, nrbytes=%d.\n", + serial_reg-SC0ICR, *(unsigned8 *)source, nr_bytes)); + write_intmode_reg(me, serial, serial_reg-SC0ICR, source, nr_bytes); + break; + + /* transmission buffers */ + case SC0TXB: + case SC1TXB: + case SC2TXB: + HW_TRACE ((me, "write - txb%d has %c, nrbytes=%d.\n", + serial_reg-SC0TXB, *(char *)source, nr_bytes)); + write_txb(me, serial, serial_reg-SC0TXB, source, nr_bytes); + break; + + /* reception buffers */ + case SC0RXB: + case SC1RXB: + case SC2RXB: + hw_abort(me, "Cannot write to reception buffer."); + break; + + /* status registers */ + case SC0STR: + case SC1STR: + case SC2STR: + hw_abort(me, "Cannot write to status register."); + break; + + case SC2TIM: + HW_TRACE ((me, "read - serial2 timer reg %d (nrbytes=%d)\n", + *(unsigned8 *)source, nr_bytes)); + write_serial2_timer_reg(me, serial, source, nr_bytes); + break; + + default: + hw_abort(me, "invalid address"); + } + + return nr_bytes; +} + + +const struct hw_descriptor dv_mn103ser_descriptor[] = { + { "mn103ser", mn103ser_finish, }, + { NULL }, +}; |