On Sun, Jul 5, 2015 at 11:14 PM, Serge Vakulenko <serge.vakule...@gmail.com> wrote: > Implement pic32 UART peripheral interface. > > Signed-off-by: Serge Vakulenko <serge.vakule...@gmail.com> > --- > hw/mips/pic32_uart.c | 228 > +++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 228 insertions(+) > create mode 100644 hw/mips/pic32_uart.c > > diff --git a/hw/mips/pic32_uart.c b/hw/mips/pic32_uart.c > new file mode 100644 > index 0000000..34056cf > --- /dev/null > +++ b/hw/mips/pic32_uart.c > @@ -0,0 +1,228 @@ > +/* > + * UART ports. > + * > + * Copyright (C) 2014-2015 Serge Vakulenko > + * > + * Permission to use, copy, modify, and distribute this software > + * and its documentation for any purpose and without fee is hereby > + * granted, provided that the above copyright notice appear in all > + * copies and that both that the copyright notice and this > + * permission notice and warranty disclaimer appear in supporting > + * documentation, and that the name of the author not be used in > + * advertising or publicity pertaining to distribution of the > + * software without specific, written prior permission. > + * > + * The author disclaim all warranties with regard to this > + * software, including all implied warranties of merchantability > + * and fitness. In no event shall the author be liable for any > + * special, indirect or consequential damages or any damages > + * whatsoever resulting from loss of use, data or profits, whether > + * in an action of contract, negligence or other tortious action, > + * arising out of or in connection with the use or performance of > + * this software. > + */ > +#include "hw/hw.h" > +#include "hw/char/serial.h" > +#include "sysemu/char.h" > +#include "pic32_peripherals.h" > + > +#include "pic32mz.h" > + > +#define UART_IRQ_ERR 0 /* error irq offset */ > +#define UART_IRQ_RX 1 /* receiver irq offset */ > +#define UART_IRQ_TX 2 /* transmitter irq offset */ > + > +/* > + * Read of UxRXREG register. > + */ > +unsigned pic32_uart_get_char(pic32_t *s, int unit) > +{ > + uart_t *u = &s->uart[unit]; > + unsigned value; > + > + /* Read a byte from input queue. */ > + value = u->rxbyte; > + VALUE(u->sta) &= ~PIC32_USTA_URXDA; > + > + s->irq_clear(s, u->irq + UART_IRQ_RX); > + return value; > +} > + > +/* > + * Write to UxTXREG register. > + */ > +void pic32_uart_put_char(pic32_t *s, int unit, unsigned char byte) > +{ > + uart_t *u = &s->uart[unit]; > + > + if (!u->chr) { > + printf("--- %s(unit = %u) serial port not configured\n", > + __func__, unit);
helpful messages should use qemu_log. I would wrap this conditional compile macro so it can be turned off. > + return; > + } > + > + /* Send the byte. */ > + if (qemu_chr_fe_write(u->chr, &byte, 1) != 1) { qemu_chr_fe_write_all will brute force it for you with a busy-wait which is probably better than dropping the char. > + /* TODO: suspend simulation until serial port ready. */ > + printf("--- %s(unit = %u) failed\n", __func__, unit); > + return; > + } > + > + if ((VALUE(u->mode) & PIC32_UMODE_ON) && > + (VALUE(u->sta) & PIC32_USTA_UTXEN)) > + { > + VALUE(u->sta) |= PIC32_USTA_UTXBF; > + VALUE(u->sta) &= ~PIC32_USTA_TRMT; > + > + /* Generate TX interrupt with some delay. */ Why? generally we don't try and emulate such delays. Do you have guests that depend on this delay? Regards, Peter > + timer_mod(u->transmit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + > + get_ticks_per_sec() / 5000); > + u->oactive = 1; > + } > +} > + > +/* > + * Called before reading a value of UxBRG, UxMODE or UxSTA registers. > + */ > +void pic32_uart_poll_status(pic32_t *s, int unit) > +{ > + uart_t *u = &s->uart[unit]; > + > + /* Keep receiver idle, transmit shift register always empty. */ > + VALUE(u->sta) |= PIC32_USTA_RIDLE; > + > + /*printf("<%x>", VALUE(u->sta)); fflush(stdout);*/ > +} > + > +/* > + * Write to UxMODE register. > + */ > +void pic32_uart_update_mode(pic32_t *s, int unit) > +{ > + uart_t *u = &s->uart[unit]; > + > + if (!(VALUE(u->mode) & PIC32_UMODE_ON)) { > + s->irq_clear(s, u->irq + UART_IRQ_RX); > + s->irq_clear(s, u->irq + UART_IRQ_TX); > + VALUE(u->sta) &= ~(PIC32_USTA_URXDA | PIC32_USTA_FERR | > + PIC32_USTA_PERR | PIC32_USTA_UTXBF); > + VALUE(u->sta) |= PIC32_USTA_RIDLE | PIC32_USTA_TRMT; > + } > +} > + > +/* > + * Write to UxSTA register. > + */ > +void pic32_uart_update_status(pic32_t *s, int unit) > +{ > + uart_t *u = &s->uart[unit]; > + > + if (!(VALUE(u->sta) & PIC32_USTA_URXEN)) { > + s->irq_clear(s, u->irq + UART_IRQ_RX); > + VALUE(u->sta) &= ~(PIC32_USTA_URXDA | PIC32_USTA_FERR | > + PIC32_USTA_PERR); > + } > + if (!(VALUE(u->sta) & PIC32_USTA_UTXEN)) { > + s->irq_clear(s, u->irq + UART_IRQ_TX); > + VALUE(u->sta) &= ~PIC32_USTA_UTXBF; > + VALUE(u->sta) |= PIC32_USTA_TRMT; > + } > +} > + > +/* > + * Return a number of free bytes in the receive FIFO. > + */ > +static int uart_can_receive(void *opaque) > +{ > + uart_t *u = opaque; > + pic32_t *s = u->mcu; /* used in VALUE() */ > + > + /*printf("--- %s(%p) called\n", __func__, u);*/ > + > + if (!(VALUE(u->mode) & PIC32_UMODE_ON) || > + !(VALUE(u->sta) & PIC32_USTA_URXEN)) { > + /* UART disabled. */ > + return 0; > + } > + > + if (VALUE(u->sta) & PIC32_USTA_URXDA) { > + /* Receive buffer full. */ > + return 0; > + } > + return 1; > +} > + > +/* > + * Process the received data. > + */ > +static void uart_receive(void *opaque, const uint8_t *buf, int size) > +{ > + uart_t *u = opaque; > + pic32_t *s = u->mcu; /* used in VALUE() */ > + > + /*printf("--- %s(%p) called\n", __func__, u);*/ > + if (!(VALUE(u->mode) & PIC32_UMODE_ON) || > + !(VALUE(u->sta) & PIC32_USTA_URXEN)) { > + /* UART disabled. */ > + return; > + } > + > + if (VALUE(u->sta) & PIC32_USTA_URXDA) { > + /* Receive buffer full. */ > + return; > + } > + > + u->rxbyte = buf[0]; > + VALUE(u->sta) |= PIC32_USTA_URXDA; > + > + /* Activate receive interrupt. */ > + s->irq_raise(s, u->irq + UART_IRQ_RX); > +} > + > +/* > + * Activate the transmit interrupt. > + */ > +static void uart_timeout(void *opaque) > +{ > + uart_t *u = opaque; > + pic32_t *s = u->mcu; /* used in VALUE() */ > + > + /*printf("--- %s() called\n", __func__);*/ > + if (u->oactive) { > + /* Activate transmit interrupt. */ > + /*printf("uart%u: raise tx irq %u\n", unit, u->irq + UART_IRQ_TX);*/ > + if ((VALUE(u->mode) & PIC32_UMODE_ON) && > + (VALUE(u->sta) & PIC32_USTA_UTXEN)) { > + s->irq_raise(s, u->irq + UART_IRQ_TX); > + } > + VALUE(u->sta) &= ~PIC32_USTA_UTXBF; > + VALUE(u->sta) |= PIC32_USTA_TRMT; > + u->oactive = 0; > + } > +} > + > +/* > + * Initialize the UART data structure. > + */ > +void pic32_uart_init(pic32_t *s, int unit, int irq, int sta, int mode) > +{ > + uart_t *u = &s->uart[unit]; > + > + u->mcu = s; > + u->irq = irq; > + u->sta = sta; > + u->mode = mode; > + u->transmit_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, uart_timeout, u); > + > + if (unit >= MAX_SERIAL_PORTS) { > + /* Cannot instantiate so many serial ports. */ > + u->chr = 0; > + return; > + } > + u->chr = serial_hds[unit]; > + > + /* Setup callback functions. */ > + if (u->chr) { > + qemu_chr_add_handlers(u->chr, uart_can_receive, uart_receive, NULL, > u); > + } > +} > -- > 2.2.2 > >