/* This file is part of the program GDB, the GNU debugger. Copyright (C) 1998-2020 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 3 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, see . */ #include "sim-main.h" #include "hw-main.h" /* DEVICE mn103iop - mn103002 I/O ports 0-3. DESCRIPTION Implements the mn103002 i/o ports as described in the mn103002 user guide. PROPERTIES reg = ... BUGS */ /* The I/O ports' registers' address block */ struct mn103iop_block { unsigned_word base; unsigned_word bound; }; enum io_port_register_types { P0OUT, P1OUT, P2OUT, P3OUT, P0MD, P1MD, P2MD, P3MD, P2SS, P4SS, P0DIR, P1DIR, P2DIR, P3DIR, P0IN, P1IN, P2IN, P3IN, }; #define NR_PORTS 4 enum { OUTPUT_BLOCK, MODE_BLOCK, DED_CTRL_BLOCK, CTRL_BLOCK, PIN_BLOCK, NR_BLOCKS }; typedef struct _mn10300_ioport { unsigned8 output, output_mode, control, pin; struct hw_event *event; } mn10300_ioport; struct mn103iop { struct mn103iop_block block[NR_BLOCKS]; mn10300_ioport port[NR_PORTS]; unsigned8 p2ss, p4ss; }; /* Finish off the partially created hw device. Attach our local callbacks. Wire up our port names etc */ static hw_io_read_buffer_method mn103iop_io_read_buffer; static hw_io_write_buffer_method mn103iop_io_write_buffer; static void attach_mn103iop_regs (struct hw *me, struct mn103iop *io_port) { int i; 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"); for (i=0; i < NR_BLOCKS; ++i ) { if (!hw_find_reg_array_property (me, "reg", i, ®)) hw_abort (me, "\"reg\" property must contain five addr/size entries"); hw_unit_address_to_attach_address (hw_parent (me), ®.address, &attach_space, &attach_address, me); io_port->block[i].base = attach_address; hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); io_port->block[i].bound = attach_address + (attach_size - 1); hw_attach_address (hw_parent (me), 0, attach_space, attach_address, attach_size, me); } } static void mn103iop_finish (struct hw *me) { struct mn103iop *io_port; int i; io_port = HW_ZALLOC (me, struct mn103iop); set_hw_data (me, io_port); set_hw_io_read_buffer (me, mn103iop_io_read_buffer); set_hw_io_write_buffer (me, mn103iop_io_write_buffer); /* Attach ourself to our parent bus */ attach_mn103iop_regs (me, io_port); /* Initialize the i/o port registers. */ for ( i=0; iport[i].output = 0; io_port->port[i].output_mode = 0; io_port->port[i].control = 0; io_port->port[i].pin = 0; } io_port->port[2].output_mode = 0xff; io_port->p2ss = 0; io_port->p4ss = 0x0f; } /* read and write */ static int decode_addr (struct hw *me, struct mn103iop *io_port, unsigned_word address) { unsigned_word offset; offset = address - io_port->block[0].base; switch (offset) { case 0x00: return P0OUT; case 0x01: return P1OUT; case 0x04: return P2OUT; case 0x05: return P3OUT; case 0x20: return P0MD; case 0x21: return P1MD; case 0x24: return P2MD; case 0x25: return P3MD; case 0x44: return P2SS; case 0x48: return P4SS; case 0x60: return P0DIR; case 0x61: return P1DIR; case 0x64: return P2DIR; case 0x65: return P3DIR; case 0x80: return P0IN; case 0x81: return P1IN; case 0x84: return P2IN; case 0x85: return P3IN; default: { hw_abort (me, "bad address"); return -1; } } } static void read_output_reg (struct hw *me, struct mn103iop *io_port, unsigned_word io_port_reg, const void *dest, unsigned nr_bytes) { if ( nr_bytes == 1 ) { *(unsigned8 *)dest = io_port->port[io_port_reg].output; } else { hw_abort (me, "bad read size of %d bytes from P%dOUT.", nr_bytes, io_port_reg); } } static void read_output_mode_reg (struct hw *me, struct mn103iop *io_port, unsigned_word io_port_reg, const void *dest, unsigned nr_bytes) { if ( nr_bytes == 1 ) { /* check if there are fields which can't be written and take appropriate action depending what bits are set */ *(unsigned8 *)dest = io_port->port[io_port_reg].output_mode; } else { hw_abort (me, "bad read size of %d bytes to P%dMD.", nr_bytes, io_port_reg); } } static void read_control_reg (struct hw *me, struct mn103iop *io_port, unsigned_word io_port_reg, const void *dest, unsigned nr_bytes) { if ( nr_bytes == 1 ) { *(unsigned8 *)dest = io_port->port[io_port_reg].control; } else { hw_abort (me, "bad read size of %d bytes to P%dDIR.", nr_bytes, io_port_reg); } } static void read_pin_reg (struct hw *me, struct mn103iop *io_port, unsigned_word io_port_reg, const void *dest, unsigned nr_bytes) { if ( nr_bytes == 1 ) { *(unsigned8 *)dest = io_port->port[io_port_reg].pin; } else { hw_abort (me, "bad read size of %d bytes to P%dIN.", nr_bytes, io_port_reg); } } static void read_dedicated_control_reg (struct hw *me, struct mn103iop *io_port, unsigned_word io_port_reg, const void *dest, unsigned nr_bytes) { if ( nr_bytes == 1 ) { /* select on io_port_reg: */ if ( io_port_reg == P2SS ) { *(unsigned8 *)dest = io_port->p2ss; } else { *(unsigned8 *)dest = io_port->p4ss; } } else { hw_abort (me, "bad read size of %d bytes to PSS.", nr_bytes); } } static unsigned mn103iop_io_read_buffer (struct hw *me, void *dest, int space, unsigned_word base, unsigned nr_bytes) { struct mn103iop *io_port = hw_data (me); enum io_port_register_types io_port_reg; HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes)); io_port_reg = decode_addr (me, io_port, base); switch (io_port_reg) { /* Port output registers */ case P0OUT: case P1OUT: case P2OUT: case P3OUT: read_output_reg(me, io_port, io_port_reg-P0OUT, dest, nr_bytes); break; /* Port output mode registers */ case P0MD: case P1MD: case P2MD: case P3MD: read_output_mode_reg(me, io_port, io_port_reg-P0MD, dest, nr_bytes); break; /* Port control registers */ case P0DIR: case P1DIR: case P2DIR: case P3DIR: read_control_reg(me, io_port, io_port_reg-P0DIR, dest, nr_bytes); break; /* Port pin registers */ case P0IN: case P1IN: case P2IN: read_pin_reg(me, io_port, io_port_reg-P0IN, dest, nr_bytes); break; case P2SS: case P4SS: read_dedicated_control_reg(me, io_port, io_port_reg, dest, nr_bytes); break; default: hw_abort(me, "invalid address"); } return nr_bytes; } static void write_output_reg (struct hw *me, struct mn103iop *io_port, unsigned_word io_port_reg, const void *source, unsigned nr_bytes) { unsigned8 buf = *(unsigned8 *)source; if ( nr_bytes == 1 ) { if ( io_port_reg == 3 && (buf & 0xfc) != 0 ) { hw_abort(me, "Cannot write to read-only bits of P3OUT."); } else { io_port->port[io_port_reg].output = buf; } } else { hw_abort (me, "bad read size of %d bytes from P%dOUT.", nr_bytes, io_port_reg); } } static void write_output_mode_reg (struct hw *me, struct mn103iop *io_port, unsigned_word io_port_reg, const void *source, unsigned nr_bytes) { unsigned8 buf = *(unsigned8 *)source; if ( nr_bytes == 1 ) { /* check if there are fields which can't be written and take appropriate action depending what bits are set */ if ( ( io_port_reg == 3 && (buf & 0xfc) != 0 ) || ( (io_port_reg == 0 || io_port_reg == 1) && (buf & 0xfe) != 0 ) ) { hw_abort(me, "Cannot write to read-only bits of output mode register."); } else { io_port->port[io_port_reg].output_mode = buf; } } else { hw_abort (me, "bad write size of %d bytes to P%dMD.", nr_bytes, io_port_reg); } } static void write_control_reg (struct hw *me, struct mn103iop *io_port, unsigned_word io_port_reg, const void *source, unsigned nr_bytes) { unsigned8 buf = *(unsigned8 *)source; if ( nr_bytes == 1 ) { if ( io_port_reg == 3 && (buf & 0xfc) != 0 ) { hw_abort(me, "Cannot write to read-only bits of P3DIR."); } else { io_port->port[io_port_reg].control = buf; } } else { hw_abort (me, "bad write size of %d bytes to P%dDIR.", nr_bytes, io_port_reg); } } static void write_dedicated_control_reg (struct hw *me, struct mn103iop *io_port, unsigned_word io_port_reg, const void *source, unsigned nr_bytes) { unsigned8 buf = *(unsigned8 *)source; if ( nr_bytes == 1 ) { /* select on io_port_reg: */ if ( io_port_reg == P2SS ) { if ( (buf & 0xfc) != 0 ) { hw_abort(me, "Cannot write to read-only bits in p2ss."); } else { io_port->p2ss = buf; } } else { if ( (buf & 0xf0) != 0 ) { hw_abort(me, "Cannot write to read-only bits in p4ss."); } else { io_port->p4ss = buf; } } } else { hw_abort (me, "bad write size of %d bytes to PSS.", nr_bytes); } } static unsigned mn103iop_io_write_buffer (struct hw *me, const void *source, int space, unsigned_word base, unsigned nr_bytes) { struct mn103iop *io_port = hw_data (me); enum io_port_register_types io_port_reg; HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes)); io_port_reg = decode_addr (me, io_port, base); switch (io_port_reg) { /* Port output registers */ case P0OUT: case P1OUT: case P2OUT: case P3OUT: write_output_reg(me, io_port, io_port_reg-P0OUT, source, nr_bytes); break; /* Port output mode registers */ case P0MD: case P1MD: case P2MD: case P3MD: write_output_mode_reg(me, io_port, io_port_reg-P0MD, source, nr_bytes); break; /* Port control registers */ case P0DIR: case P1DIR: case P2DIR: case P3DIR: write_control_reg(me, io_port, io_port_reg-P0DIR, source, nr_bytes); break; /* Port pin registers */ case P0IN: case P1IN: case P2IN: hw_abort(me, "Cannot write to pin register."); break; case P2SS: case P4SS: write_dedicated_control_reg(me, io_port, io_port_reg, source, nr_bytes); break; default: hw_abort(me, "invalid address"); } return nr_bytes; } const struct hw_descriptor dv_mn103iop_descriptor[] = { { "mn103iop", mn103iop_finish, }, { NULL }, };