xref: /netbsd-src/external/gpl3/gdb/dist/sim/lm32/dv-lm32uart.c (revision 71f621822dbfd5073a314948bec169b7bb05f7be)
14e98e3e1Schristos /*  Lattice Mico32 UART model.
24e98e3e1Schristos     Contributed by Jon Beniston <jon@beniston.com>
34e98e3e1Schristos 
4*71f62182Schristos    Copyright (C) 2009-2024 Free Software Foundation, Inc.
54e98e3e1Schristos 
64e98e3e1Schristos    This file is part of GDB.
74e98e3e1Schristos 
84e98e3e1Schristos    This program is free software; you can redistribute it and/or modify
94e98e3e1Schristos    it under the terms of the GNU General Public License as published by
104e98e3e1Schristos    the Free Software Foundation; either version 3 of the License, or
114e98e3e1Schristos    (at your option) any later version.
124e98e3e1Schristos 
134e98e3e1Schristos    This program is distributed in the hope that it will be useful,
144e98e3e1Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
154e98e3e1Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
164e98e3e1Schristos    GNU General Public License for more details.
174e98e3e1Schristos 
184e98e3e1Schristos    You should have received a copy of the GNU General Public License
194e98e3e1Schristos    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
204e98e3e1Schristos 
214b169a6bSchristos /* This must come before any other includes.  */
224b169a6bSchristos #include "defs.h"
234b169a6bSchristos 
244e98e3e1Schristos #include "sim-main.h"
254e98e3e1Schristos #include "hw-main.h"
264e98e3e1Schristos #include "sim-assert.h"
274e98e3e1Schristos 
284e98e3e1Schristos #include <stdio.h>
294b169a6bSchristos #include <sys/select.h>
304e98e3e1Schristos #include <sys/time.h>
314e98e3e1Schristos 
324e98e3e1Schristos struct lm32uart
334e98e3e1Schristos {
344e98e3e1Schristos   unsigned base;		/* Base address of this UART.  */
354e98e3e1Schristos   unsigned limit;		/* Limit address of this UART.  */
364e98e3e1Schristos   unsigned char rbr;
374e98e3e1Schristos   unsigned char thr;
384e98e3e1Schristos   unsigned char ier;
394e98e3e1Schristos   unsigned char iir;
404e98e3e1Schristos   unsigned char lcr;
414e98e3e1Schristos   unsigned char mcr;
424e98e3e1Schristos   unsigned char lsr;
434e98e3e1Schristos   unsigned char msr;
444e98e3e1Schristos   unsigned char div;
454e98e3e1Schristos   struct hw_event *event;
464e98e3e1Schristos };
474e98e3e1Schristos 
484e98e3e1Schristos /* UART registers.  */
494e98e3e1Schristos 
504e98e3e1Schristos #define LM32_UART_RBR           0x0
514e98e3e1Schristos #define LM32_UART_THR           0x0
524e98e3e1Schristos #define LM32_UART_IER           0x4
534e98e3e1Schristos #define LM32_UART_IIR           0x8
544e98e3e1Schristos #define LM32_UART_LCR           0xc
554e98e3e1Schristos #define LM32_UART_MCR           0x10
564e98e3e1Schristos #define LM32_UART_LSR           0x14
574e98e3e1Schristos #define LM32_UART_MSR           0x18
584e98e3e1Schristos #define LM32_UART_DIV           0x1c
594e98e3e1Schristos 
604e98e3e1Schristos #define LM32_UART_IER_RX_INT    0x1
614e98e3e1Schristos #define LM32_UART_IER_TX_INT    0x2
624e98e3e1Schristos 
634e98e3e1Schristos #define MICOUART_IIR_TXRDY      0x2
644e98e3e1Schristos #define MICOUART_IIR_RXRDY      0x4
654e98e3e1Schristos 
664e98e3e1Schristos #define LM32_UART_LSR_RX_RDY    0x01
674e98e3e1Schristos #define LM32_UART_LSR_TX_RDY    0x20
684e98e3e1Schristos 
694e98e3e1Schristos #define LM32_UART_LCR_WLS_MASK  0x3
704e98e3e1Schristos #define LM32_UART_LCR_WLS_5     0x0
714e98e3e1Schristos #define LM32_UART_LCR_WLS_6     0x1
724e98e3e1Schristos #define LM32_UART_LCR_WLS_7     0x2
734e98e3e1Schristos #define LM32_UART_LCR_WLS_8     0x3
744e98e3e1Schristos 
754e98e3e1Schristos /* UART ports.  */
764e98e3e1Schristos 
774e98e3e1Schristos enum
784e98e3e1Schristos {
794e98e3e1Schristos   INT_PORT
804e98e3e1Schristos };
814e98e3e1Schristos 
824e98e3e1Schristos static const struct hw_port_descriptor lm32uart_ports[] = {
834e98e3e1Schristos   {"int", INT_PORT, 0, output_port},
844e98e3e1Schristos   {}
854e98e3e1Schristos };
864e98e3e1Schristos 
874e98e3e1Schristos static void
884e98e3e1Schristos do_uart_tx_event (struct hw *me, void *data)
894e98e3e1Schristos {
904e98e3e1Schristos   struct lm32uart *uart = hw_data (me);
914e98e3e1Schristos   char c;
924e98e3e1Schristos 
934e98e3e1Schristos   /* Generate interrupt when transmission is complete.  */
944e98e3e1Schristos   if (uart->ier & LM32_UART_IER_TX_INT)
954e98e3e1Schristos     {
964e98e3e1Schristos       /* Generate interrupt */
974e98e3e1Schristos       hw_port_event (me, INT_PORT, 1);
984e98e3e1Schristos     }
994e98e3e1Schristos 
1004e98e3e1Schristos   /* Indicate which interrupt has occured.  */
1014e98e3e1Schristos   uart->iir = MICOUART_IIR_TXRDY;
1024e98e3e1Schristos 
1034e98e3e1Schristos   /* Indicate THR is empty.  */
1044e98e3e1Schristos   uart->lsr |= LM32_UART_LSR_TX_RDY;
1054e98e3e1Schristos 
1064e98e3e1Schristos   /* Output the character in the THR.  */
1074e98e3e1Schristos   c = (char) uart->thr;
1084e98e3e1Schristos 
1094e98e3e1Schristos   /* WLS field in LCR register specifies the number of bits to output.  */
1104e98e3e1Schristos   switch (uart->lcr & LM32_UART_LCR_WLS_MASK)
1114e98e3e1Schristos     {
1124e98e3e1Schristos     case LM32_UART_LCR_WLS_5:
1134e98e3e1Schristos       c &= 0x1f;
1144e98e3e1Schristos       break;
1154e98e3e1Schristos     case LM32_UART_LCR_WLS_6:
1164e98e3e1Schristos       c &= 0x3f;
1174e98e3e1Schristos       break;
1184e98e3e1Schristos     case LM32_UART_LCR_WLS_7:
1194e98e3e1Schristos       c &= 0x7f;
1204e98e3e1Schristos       break;
1214e98e3e1Schristos     }
1224e98e3e1Schristos   printf ("%c", c);
1234e98e3e1Schristos }
1244e98e3e1Schristos 
1254e98e3e1Schristos static unsigned
1264e98e3e1Schristos lm32uart_io_write_buffer (struct hw *me,
1274e98e3e1Schristos 			  const void *source,
1284e98e3e1Schristos 			  int space, unsigned_word base, unsigned nr_bytes)
1294e98e3e1Schristos {
1304e98e3e1Schristos   struct lm32uart *uart = hw_data (me);
1314e98e3e1Schristos   int uart_reg;
1324e98e3e1Schristos   const unsigned char *source_bytes = source;
1334e98e3e1Schristos   int value = 0;
1344e98e3e1Schristos 
1354e98e3e1Schristos   HW_TRACE ((me, "write to 0x%08lx length %d with 0x%x", (long) base,
1364e98e3e1Schristos 	     (int) nr_bytes, value));
1374e98e3e1Schristos 
1384e98e3e1Schristos   if (nr_bytes == 4)
1394e98e3e1Schristos     value = (source_bytes[0] << 24)
1404e98e3e1Schristos       | (source_bytes[1] << 16) | (source_bytes[2] << 8) | (source_bytes[3]);
1414e98e3e1Schristos   else
1424e98e3e1Schristos     hw_abort (me, "write of unsupported number of bytes: %d.", nr_bytes);
1434e98e3e1Schristos 
1444e98e3e1Schristos   uart_reg = base - uart->base;
1454e98e3e1Schristos 
1464e98e3e1Schristos   switch (uart_reg)
1474e98e3e1Schristos     {
1484e98e3e1Schristos     case LM32_UART_THR:
1494e98e3e1Schristos       /* Buffer the character to output.  */
1504e98e3e1Schristos       uart->thr = value;
1514e98e3e1Schristos 
1524e98e3e1Schristos       /* Indicate the THR is full.  */
1534e98e3e1Schristos       uart->lsr &= ~LM32_UART_LSR_TX_RDY;
1544e98e3e1Schristos 
1554e98e3e1Schristos       /* deassert interrupt when IER is loaded.  */
1564e98e3e1Schristos       uart->iir &= ~MICOUART_IIR_TXRDY;
1574e98e3e1Schristos 
1584e98e3e1Schristos       /* schedule an event to output the character.  */
1594e98e3e1Schristos       hw_event_queue_schedule (me, 1, do_uart_tx_event, 0);
1604e98e3e1Schristos 
1614e98e3e1Schristos       break;
1624e98e3e1Schristos     case LM32_UART_IER:
1634e98e3e1Schristos       uart->ier = value;
1644e98e3e1Schristos       if ((value & LM32_UART_IER_TX_INT)
1654e98e3e1Schristos 	  && (uart->lsr & LM32_UART_LSR_TX_RDY))
1664e98e3e1Schristos 	{
1674e98e3e1Schristos 	  /* hw_event_queue_schedule (me, 1, do_uart_tx_event, 0); */
1684e98e3e1Schristos 	  uart->lsr |= LM32_UART_LSR_TX_RDY;
1694e98e3e1Schristos 	  uart->iir |= MICOUART_IIR_TXRDY;
1704e98e3e1Schristos 	  hw_port_event (me, INT_PORT, 1);
1714e98e3e1Schristos 	}
1724e98e3e1Schristos       else if ((value & LM32_UART_IER_TX_INT) == 0)
1734e98e3e1Schristos 	{
1744e98e3e1Schristos 	  hw_port_event (me, INT_PORT, 0);
1754e98e3e1Schristos 	}
1764e98e3e1Schristos       break;
1774e98e3e1Schristos     case LM32_UART_IIR:
1784e98e3e1Schristos       uart->iir = value;
1794e98e3e1Schristos       break;
1804e98e3e1Schristos     case LM32_UART_LCR:
1814e98e3e1Schristos       uart->lcr = value;
1824e98e3e1Schristos       break;
1834e98e3e1Schristos     case LM32_UART_MCR:
1844e98e3e1Schristos       uart->mcr = value;
1854e98e3e1Schristos       break;
1864e98e3e1Schristos     case LM32_UART_LSR:
1874e98e3e1Schristos       uart->lsr = value;
1884e98e3e1Schristos       break;
1894e98e3e1Schristos     case LM32_UART_MSR:
1904e98e3e1Schristos       uart->msr = value;
1914e98e3e1Schristos       break;
1924e98e3e1Schristos     case LM32_UART_DIV:
1934e98e3e1Schristos       uart->div = value;
1944e98e3e1Schristos       break;
1954e98e3e1Schristos     default:
1964e98e3e1Schristos       hw_abort (me, "write to invalid register address: 0x%x.", uart_reg);
1974e98e3e1Schristos     }
1984e98e3e1Schristos 
1994e98e3e1Schristos   return nr_bytes;
2004e98e3e1Schristos }
2014e98e3e1Schristos 
2024e98e3e1Schristos static unsigned
2034e98e3e1Schristos lm32uart_io_read_buffer (struct hw *me,
2044e98e3e1Schristos 			 void *dest,
2054e98e3e1Schristos 			 int space, unsigned_word base, unsigned nr_bytes)
2064e98e3e1Schristos {
2074e98e3e1Schristos   struct lm32uart *uart = hw_data (me);
2084e98e3e1Schristos   int uart_reg;
2094e98e3e1Schristos   int value;
2104e98e3e1Schristos   unsigned char *dest_bytes = dest;
2114e98e3e1Schristos   fd_set fd;
2124e98e3e1Schristos   struct timeval tv;
2134e98e3e1Schristos 
2144e98e3e1Schristos   HW_TRACE ((me, "read 0x%08lx length %d", (long) base, (int) nr_bytes));
2154e98e3e1Schristos 
2164e98e3e1Schristos   uart_reg = base - uart->base;
2174e98e3e1Schristos 
2184e98e3e1Schristos   switch (uart_reg)
2194e98e3e1Schristos     {
2204e98e3e1Schristos     case LM32_UART_RBR:
2214e98e3e1Schristos       value = getchar ();
2224e98e3e1Schristos       uart->lsr &= ~LM32_UART_LSR_RX_RDY;
2234e98e3e1Schristos       break;
2244e98e3e1Schristos     case LM32_UART_IER:
2254e98e3e1Schristos       value = uart->ier;
2264e98e3e1Schristos       break;
2274e98e3e1Schristos     case LM32_UART_IIR:
2284e98e3e1Schristos       value = uart->iir;
2294e98e3e1Schristos       break;
2304e98e3e1Schristos     case LM32_UART_LCR:
2314e98e3e1Schristos       value = uart->lcr;
2324e98e3e1Schristos       break;
2334e98e3e1Schristos     case LM32_UART_MCR:
2344e98e3e1Schristos       value = uart->mcr;
2354e98e3e1Schristos       break;
2364e98e3e1Schristos     case LM32_UART_LSR:
2374e98e3e1Schristos       /* Check to see if any data waiting in stdin.  */
2384e98e3e1Schristos       FD_ZERO (&fd);
2394e98e3e1Schristos       FD_SET (fileno (stdin), &fd);
2404e98e3e1Schristos       tv.tv_sec = 0;
2414e98e3e1Schristos       tv.tv_usec = 1;
2424e98e3e1Schristos       if (select (fileno (stdin) + 1, &fd, NULL, NULL, &tv))
2434e98e3e1Schristos 	uart->lsr |= LM32_UART_LSR_RX_RDY;
2444e98e3e1Schristos       value = uart->lsr;
2454e98e3e1Schristos       break;
2464e98e3e1Schristos     case LM32_UART_MSR:
2474e98e3e1Schristos       value = uart->msr;
2484e98e3e1Schristos       break;
2494e98e3e1Schristos     case LM32_UART_DIV:
2504e98e3e1Schristos       value = uart->div;
2514e98e3e1Schristos       break;
2524e98e3e1Schristos     default:
2534e98e3e1Schristos       hw_abort (me, "read from invalid register address: 0x%x.", uart_reg);
2544e98e3e1Schristos     }
2554e98e3e1Schristos 
2564e98e3e1Schristos   if (nr_bytes == 4)
2574e98e3e1Schristos     {
2584e98e3e1Schristos       dest_bytes[0] = value >> 24;
2594e98e3e1Schristos       dest_bytes[1] = value >> 16;
2604e98e3e1Schristos       dest_bytes[2] = value >> 8;
2614e98e3e1Schristos       dest_bytes[3] = value;
2624e98e3e1Schristos     }
2634e98e3e1Schristos   else
2644e98e3e1Schristos     hw_abort (me, "read of unsupported number of bytes: %d", nr_bytes);
2654e98e3e1Schristos 
2664e98e3e1Schristos   return nr_bytes;
2674e98e3e1Schristos }
2684e98e3e1Schristos 
2694e98e3e1Schristos static void
2704e98e3e1Schristos attach_lm32uart_regs (struct hw *me, struct lm32uart *uart)
2714e98e3e1Schristos {
2724e98e3e1Schristos   unsigned_word attach_address;
2734e98e3e1Schristos   int attach_space;
2744e98e3e1Schristos   unsigned attach_size;
2754e98e3e1Schristos   reg_property_spec reg;
2764e98e3e1Schristos 
2774e98e3e1Schristos   if (hw_find_property (me, "reg") == NULL)
2784e98e3e1Schristos     hw_abort (me, "Missing \"reg\" property");
2794e98e3e1Schristos   if (!hw_find_reg_array_property (me, "reg", 0, &reg))
2804e98e3e1Schristos     hw_abort (me, "\"reg\" property must contain three addr/size entries");
2814e98e3e1Schristos   hw_unit_address_to_attach_address (hw_parent (me),
2824e98e3e1Schristos 				     &reg.address,
2834e98e3e1Schristos 				     &attach_space, &attach_address, me);
2844e98e3e1Schristos   uart->base = attach_address;
2854e98e3e1Schristos   hw_unit_size_to_attach_size (hw_parent (me), &reg.size, &attach_size, me);
2864e98e3e1Schristos   uart->limit = attach_address + (attach_size - 1);
2874e98e3e1Schristos   hw_attach_address (hw_parent (me),
2884e98e3e1Schristos 		     0, attach_space, attach_address, attach_size, me);
2894e98e3e1Schristos }
2904e98e3e1Schristos 
2914e98e3e1Schristos static void
2924e98e3e1Schristos lm32uart_finish (struct hw *me)
2934e98e3e1Schristos {
2944e98e3e1Schristos   struct lm32uart *uart;
2954e98e3e1Schristos 
2964e98e3e1Schristos   uart = HW_ZALLOC (me, struct lm32uart);
2974e98e3e1Schristos   set_hw_data (me, uart);
2984e98e3e1Schristos   set_hw_io_read_buffer (me, lm32uart_io_read_buffer);
2994e98e3e1Schristos   set_hw_io_write_buffer (me, lm32uart_io_write_buffer);
3004e98e3e1Schristos   set_hw_ports (me, lm32uart_ports);
3014e98e3e1Schristos 
3024e98e3e1Schristos   /* Attach ourself to our parent bus.  */
3034e98e3e1Schristos   attach_lm32uart_regs (me, uart);
3044e98e3e1Schristos 
3054e98e3e1Schristos   /* Initialize the UART.  */
3064e98e3e1Schristos   uart->rbr = 0;
3074e98e3e1Schristos   uart->thr = 0;
3084e98e3e1Schristos   uart->ier = 0;
3094e98e3e1Schristos   uart->iir = 0;
3104e98e3e1Schristos   uart->lcr = 0;
3114e98e3e1Schristos   uart->mcr = 0;
3124e98e3e1Schristos   uart->lsr = LM32_UART_LSR_TX_RDY;
3134e98e3e1Schristos   uart->msr = 0;
3144e98e3e1Schristos   uart->div = 0;		/* By setting to zero, characters are output immediately.  */
3154e98e3e1Schristos }
3164e98e3e1Schristos 
3174e98e3e1Schristos const struct hw_descriptor dv_lm32uart_descriptor[] = {
3184e98e3e1Schristos   {"lm32uart", lm32uart_finish,},
3194e98e3e1Schristos   {NULL},
3204e98e3e1Schristos };
321