xref: /openbmc/qemu/hw/char/ibex_uart.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
1a7d2d98cSAlistair Francis /*
2a7d2d98cSAlistair Francis  * QEMU lowRISC Ibex UART device
3a7d2d98cSAlistair Francis  *
4a7d2d98cSAlistair Francis  * Copyright (c) 2020 Western Digital
5a7d2d98cSAlistair Francis  *
6a7d2d98cSAlistair Francis  * For details check the documentation here:
7a7d2d98cSAlistair Francis  *    https://docs.opentitan.org/hw/ip/uart/doc/
8a7d2d98cSAlistair Francis  *
9a7d2d98cSAlistair Francis  * Permission is hereby granted, free of charge, to any person obtaining a copy
10a7d2d98cSAlistair Francis  * of this software and associated documentation files (the "Software"), to deal
11a7d2d98cSAlistair Francis  * in the Software without restriction, including without limitation the rights
12a7d2d98cSAlistair Francis  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13a7d2d98cSAlistair Francis  * copies of the Software, and to permit persons to whom the Software is
14a7d2d98cSAlistair Francis  * furnished to do so, subject to the following conditions:
15a7d2d98cSAlistair Francis  *
16a7d2d98cSAlistair Francis  * The above copyright notice and this permission notice shall be included in
17a7d2d98cSAlistair Francis  * all copies or substantial portions of the Software.
18a7d2d98cSAlistair Francis  *
19a7d2d98cSAlistair Francis  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20a7d2d98cSAlistair Francis  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21a7d2d98cSAlistair Francis  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22a7d2d98cSAlistair Francis  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23a7d2d98cSAlistair Francis  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24a7d2d98cSAlistair Francis  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25a7d2d98cSAlistair Francis  * THE SOFTWARE.
26a7d2d98cSAlistair Francis  */
27a7d2d98cSAlistair Francis 
28a7d2d98cSAlistair Francis #include "qemu/osdep.h"
29a7d2d98cSAlistair Francis #include "hw/char/ibex_uart.h"
30a7d2d98cSAlistair Francis #include "hw/irq.h"
31940aabb9SAlistair Francis #include "hw/qdev-clock.h"
32a7d2d98cSAlistair Francis #include "hw/qdev-properties.h"
33ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
348c6631e6SThomas Huth #include "hw/registerfields.h"
35a7d2d98cSAlistair Francis #include "migration/vmstate.h"
36a7d2d98cSAlistair Francis #include "qemu/log.h"
37a7d2d98cSAlistair Francis #include "qemu/module.h"
38a7d2d98cSAlistair Francis 
39bdc36ce6SAlistair Francis REG32(INTR_STATE, 0x00)
40bdc36ce6SAlistair Francis     FIELD(INTR_STATE, TX_WATERMARK, 0, 1)
41bdc36ce6SAlistair Francis     FIELD(INTR_STATE, RX_WATERMARK, 1, 1)
42bdc36ce6SAlistair Francis     FIELD(INTR_STATE, TX_EMPTY, 2, 1)
43bdc36ce6SAlistair Francis     FIELD(INTR_STATE, RX_OVERFLOW, 3, 1)
44bdc36ce6SAlistair Francis REG32(INTR_ENABLE, 0x04)
45bdc36ce6SAlistair Francis REG32(INTR_TEST, 0x08)
4624bfb98dSAlistair Francis REG32(ALERT_TEST, 0x0C)
4724bfb98dSAlistair Francis REG32(CTRL, 0x10)
48bdc36ce6SAlistair Francis     FIELD(CTRL, TX_ENABLE, 0, 1)
49bdc36ce6SAlistair Francis     FIELD(CTRL, RX_ENABLE, 1, 1)
50bdc36ce6SAlistair Francis     FIELD(CTRL, NF, 2, 1)
51bdc36ce6SAlistair Francis     FIELD(CTRL, SLPBK, 4, 1)
52bdc36ce6SAlistair Francis     FIELD(CTRL, LLPBK, 5, 1)
53bdc36ce6SAlistair Francis     FIELD(CTRL, PARITY_EN, 6, 1)
54bdc36ce6SAlistair Francis     FIELD(CTRL, PARITY_ODD, 7, 1)
55bdc36ce6SAlistair Francis     FIELD(CTRL, RXBLVL, 8, 2)
56bdc36ce6SAlistair Francis     FIELD(CTRL, NCO, 16, 16)
5724bfb98dSAlistair Francis REG32(STATUS, 0x14)
58bdc36ce6SAlistair Francis     FIELD(STATUS, TXFULL, 0, 1)
59bdc36ce6SAlistair Francis     FIELD(STATUS, RXFULL, 1, 1)
60bdc36ce6SAlistair Francis     FIELD(STATUS, TXEMPTY, 2, 1)
61bdc36ce6SAlistair Francis     FIELD(STATUS, RXIDLE, 4, 1)
62bdc36ce6SAlistair Francis     FIELD(STATUS, RXEMPTY, 5, 1)
6324bfb98dSAlistair Francis REG32(RDATA, 0x18)
6424bfb98dSAlistair Francis REG32(WDATA, 0x1C)
6524bfb98dSAlistair Francis REG32(FIFO_CTRL, 0x20)
66bdc36ce6SAlistair Francis     FIELD(FIFO_CTRL, RXRST, 0, 1)
67bdc36ce6SAlistair Francis     FIELD(FIFO_CTRL, TXRST, 1, 1)
68bdc36ce6SAlistair Francis     FIELD(FIFO_CTRL, RXILVL, 2, 3)
69bdc36ce6SAlistair Francis     FIELD(FIFO_CTRL, TXILVL, 5, 2)
7024bfb98dSAlistair Francis REG32(FIFO_STATUS, 0x24)
71bdc36ce6SAlistair Francis     FIELD(FIFO_STATUS, TXLVL, 0, 5)
72bdc36ce6SAlistair Francis     FIELD(FIFO_STATUS, RXLVL, 16, 5)
7324bfb98dSAlistair Francis REG32(OVRD, 0x28)
7424bfb98dSAlistair Francis REG32(VAL, 0x2C)
7524bfb98dSAlistair Francis REG32(TIMEOUT_CTRL, 0x30)
76bdc36ce6SAlistair Francis 
ibex_uart_update_irqs(IbexUartState * s)77a7d2d98cSAlistair Francis static void ibex_uart_update_irqs(IbexUartState *s)
78a7d2d98cSAlistair Francis {
7959093cc4SAlistair Francis     if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_WATERMARK_MASK) {
80a7d2d98cSAlistair Francis         qemu_set_irq(s->tx_watermark, 1);
81a7d2d98cSAlistair Francis     } else {
82a7d2d98cSAlistair Francis         qemu_set_irq(s->tx_watermark, 0);
83a7d2d98cSAlistair Francis     }
84a7d2d98cSAlistair Francis 
8559093cc4SAlistair Francis     if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_WATERMARK_MASK) {
86a7d2d98cSAlistair Francis         qemu_set_irq(s->rx_watermark, 1);
87a7d2d98cSAlistair Francis     } else {
88a7d2d98cSAlistair Francis         qemu_set_irq(s->rx_watermark, 0);
89a7d2d98cSAlistair Francis     }
90a7d2d98cSAlistair Francis 
9159093cc4SAlistair Francis     if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_EMPTY_MASK) {
92a7d2d98cSAlistair Francis         qemu_set_irq(s->tx_empty, 1);
93a7d2d98cSAlistair Francis     } else {
94a7d2d98cSAlistair Francis         qemu_set_irq(s->tx_empty, 0);
95a7d2d98cSAlistair Francis     }
96a7d2d98cSAlistair Francis 
9759093cc4SAlistair Francis     if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_OVERFLOW_MASK) {
98a7d2d98cSAlistair Francis         qemu_set_irq(s->rx_overflow, 1);
99a7d2d98cSAlistair Francis     } else {
100a7d2d98cSAlistair Francis         qemu_set_irq(s->rx_overflow, 0);
101a7d2d98cSAlistair Francis     }
102a7d2d98cSAlistair Francis }
103a7d2d98cSAlistair Francis 
ibex_uart_can_receive(void * opaque)104a7d2d98cSAlistair Francis static int ibex_uart_can_receive(void *opaque)
105a7d2d98cSAlistair Francis {
106a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
107a7d2d98cSAlistair Francis 
10882a4ed8eSAlexander Wagner     if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK)
10982a4ed8eSAlexander Wagner            && !(s->uart_status & R_STATUS_RXFULL_MASK)) {
110a7d2d98cSAlistair Francis         return 1;
111a7d2d98cSAlistair Francis     }
112a7d2d98cSAlistair Francis 
113a7d2d98cSAlistair Francis     return 0;
114a7d2d98cSAlistair Francis }
115a7d2d98cSAlistair Francis 
ibex_uart_receive(void * opaque,const uint8_t * buf,int size)116a7d2d98cSAlistair Francis static void ibex_uart_receive(void *opaque, const uint8_t *buf, int size)
117a7d2d98cSAlistair Francis {
118a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
11959093cc4SAlistair Francis     uint8_t rx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_RXILVL_MASK)
12059093cc4SAlistair Francis                             >> R_FIFO_CTRL_RXILVL_SHIFT;
121a7d2d98cSAlistair Francis 
122a7d2d98cSAlistair Francis     s->uart_rdata = *buf;
123a7d2d98cSAlistair Francis 
12459093cc4SAlistair Francis     s->uart_status &= ~R_STATUS_RXIDLE_MASK;
12559093cc4SAlistair Francis     s->uart_status &= ~R_STATUS_RXEMPTY_MASK;
12682a4ed8eSAlexander Wagner     /* The RXFULL is set after receiving a single byte
12782a4ed8eSAlexander Wagner      * as the FIFO buffers are not yet implemented.
12882a4ed8eSAlexander Wagner      */
12982a4ed8eSAlexander Wagner     s->uart_status |= R_STATUS_RXFULL_MASK;
13082a4ed8eSAlexander Wagner     s->rx_level += 1;
131a7d2d98cSAlistair Francis 
132a7d2d98cSAlistair Francis     if (size > rx_fifo_level) {
13359093cc4SAlistair Francis         s->uart_intr_state |= R_INTR_STATE_RX_WATERMARK_MASK;
134a7d2d98cSAlistair Francis     }
135a7d2d98cSAlistair Francis 
136a7d2d98cSAlistair Francis     ibex_uart_update_irqs(s);
137a7d2d98cSAlistair Francis }
138a7d2d98cSAlistair Francis 
ibex_uart_xmit(void * do_not_use,GIOCondition cond,void * opaque)139bf7b1eabSMarc-André Lureau static gboolean ibex_uart_xmit(void *do_not_use, GIOCondition cond,
140a7d2d98cSAlistair Francis                                void *opaque)
141a7d2d98cSAlistair Francis {
142a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
14359093cc4SAlistair Francis     uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
14459093cc4SAlistair Francis                             >> R_FIFO_CTRL_TXILVL_SHIFT;
145a7d2d98cSAlistair Francis     int ret;
146a7d2d98cSAlistair Francis 
147a7d2d98cSAlistair Francis     /* instant drain the fifo when there's no back-end */
148a7d2d98cSAlistair Francis     if (!qemu_chr_fe_backend_connected(&s->chr)) {
149a7d2d98cSAlistair Francis         s->tx_level = 0;
15053c7c924SPhilippe Mathieu-Daudé         return G_SOURCE_REMOVE;
151a7d2d98cSAlistair Francis     }
152a7d2d98cSAlistair Francis 
153a7d2d98cSAlistair Francis     if (!s->tx_level) {
15459093cc4SAlistair Francis         s->uart_status &= ~R_STATUS_TXFULL_MASK;
15559093cc4SAlistair Francis         s->uart_status |= R_STATUS_TXEMPTY_MASK;
15659093cc4SAlistair Francis         s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
15759093cc4SAlistair Francis         s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
158a7d2d98cSAlistair Francis         ibex_uart_update_irqs(s);
15953c7c924SPhilippe Mathieu-Daudé         return G_SOURCE_REMOVE;
160a7d2d98cSAlistair Francis     }
161a7d2d98cSAlistair Francis 
162a7d2d98cSAlistair Francis     ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level);
163a7d2d98cSAlistair Francis 
164a7d2d98cSAlistair Francis     if (ret >= 0) {
165a7d2d98cSAlistair Francis         s->tx_level -= ret;
166a7d2d98cSAlistair Francis         memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_level);
167a7d2d98cSAlistair Francis     }
168a7d2d98cSAlistair Francis 
169a7d2d98cSAlistair Francis     if (s->tx_level) {
170a7d2d98cSAlistair Francis         guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
171a7d2d98cSAlistair Francis                                         ibex_uart_xmit, s);
172a7d2d98cSAlistair Francis         if (!r) {
173a7d2d98cSAlistair Francis             s->tx_level = 0;
17453c7c924SPhilippe Mathieu-Daudé             return G_SOURCE_REMOVE;
175a7d2d98cSAlistair Francis         }
176a7d2d98cSAlistair Francis     }
177a7d2d98cSAlistair Francis 
178a7d2d98cSAlistair Francis     /* Clear the TX Full bit */
179a7d2d98cSAlistair Francis     if (s->tx_level != IBEX_UART_TX_FIFO_SIZE) {
18059093cc4SAlistair Francis         s->uart_status &= ~R_STATUS_TXFULL_MASK;
181a7d2d98cSAlistair Francis     }
182a7d2d98cSAlistair Francis 
183a7d2d98cSAlistair Francis     /* Disable the TX_WATERMARK IRQ */
184a7d2d98cSAlistair Francis     if (s->tx_level < tx_fifo_level) {
18559093cc4SAlistair Francis         s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
186a7d2d98cSAlistair Francis     }
187a7d2d98cSAlistair Francis 
188a7d2d98cSAlistair Francis     /* Set TX empty */
189a7d2d98cSAlistair Francis     if (s->tx_level == 0) {
19059093cc4SAlistair Francis         s->uart_status |= R_STATUS_TXEMPTY_MASK;
19159093cc4SAlistair Francis         s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
192a7d2d98cSAlistair Francis     }
193a7d2d98cSAlistair Francis 
194a7d2d98cSAlistair Francis     ibex_uart_update_irqs(s);
19553c7c924SPhilippe Mathieu-Daudé     return G_SOURCE_REMOVE;
196a7d2d98cSAlistair Francis }
197a7d2d98cSAlistair Francis 
uart_write_tx_fifo(IbexUartState * s,const uint8_t * buf,int size)198a7d2d98cSAlistair Francis static void uart_write_tx_fifo(IbexUartState *s, const uint8_t *buf,
199a7d2d98cSAlistair Francis                                int size)
200a7d2d98cSAlistair Francis {
201a7d2d98cSAlistair Francis     uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
20259093cc4SAlistair Francis     uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
20359093cc4SAlistair Francis                             >> R_FIFO_CTRL_TXILVL_SHIFT;
204a7d2d98cSAlistair Francis 
205a7d2d98cSAlistair Francis     if (size > IBEX_UART_TX_FIFO_SIZE - s->tx_level) {
206a7d2d98cSAlistair Francis         size = IBEX_UART_TX_FIFO_SIZE - s->tx_level;
207a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR, "ibex_uart: TX FIFO overflow");
208a7d2d98cSAlistair Francis     }
209a7d2d98cSAlistair Francis 
210a7d2d98cSAlistair Francis     memcpy(s->tx_fifo + s->tx_level, buf, size);
211a7d2d98cSAlistair Francis     s->tx_level += size;
212a7d2d98cSAlistair Francis 
213a7d2d98cSAlistair Francis     if (s->tx_level > 0) {
21459093cc4SAlistair Francis         s->uart_status &= ~R_STATUS_TXEMPTY_MASK;
215a7d2d98cSAlistair Francis     }
216a7d2d98cSAlistair Francis 
217a7d2d98cSAlistair Francis     if (s->tx_level >= tx_fifo_level) {
21859093cc4SAlistair Francis         s->uart_intr_state |= R_INTR_STATE_TX_WATERMARK_MASK;
219a7d2d98cSAlistair Francis         ibex_uart_update_irqs(s);
220a7d2d98cSAlistair Francis     }
221a7d2d98cSAlistair Francis 
222a7d2d98cSAlistair Francis     if (s->tx_level == IBEX_UART_TX_FIFO_SIZE) {
22359093cc4SAlistair Francis         s->uart_status |= R_STATUS_TXFULL_MASK;
224a7d2d98cSAlistair Francis     }
225a7d2d98cSAlistair Francis 
226a7d2d98cSAlistair Francis     timer_mod(s->fifo_trigger_handle, current_time +
227a7d2d98cSAlistair Francis               (s->char_tx_time * 4));
228a7d2d98cSAlistair Francis }
229a7d2d98cSAlistair Francis 
ibex_uart_reset(DeviceState * dev)230a7d2d98cSAlistair Francis static void ibex_uart_reset(DeviceState *dev)
231a7d2d98cSAlistair Francis {
232a7d2d98cSAlistair Francis     IbexUartState *s = IBEX_UART(dev);
233a7d2d98cSAlistair Francis 
234a7d2d98cSAlistair Francis     s->uart_intr_state = 0x00000000;
235a7d2d98cSAlistair Francis     s->uart_intr_state = 0x00000000;
236a7d2d98cSAlistair Francis     s->uart_intr_enable = 0x00000000;
237a7d2d98cSAlistair Francis     s->uart_ctrl = 0x00000000;
238a7d2d98cSAlistair Francis     s->uart_status = 0x0000003c;
239a7d2d98cSAlistair Francis     s->uart_rdata = 0x00000000;
240a7d2d98cSAlistair Francis     s->uart_fifo_ctrl = 0x00000000;
241a7d2d98cSAlistair Francis     s->uart_fifo_status = 0x00000000;
242a7d2d98cSAlistair Francis     s->uart_ovrd = 0x00000000;
243a7d2d98cSAlistair Francis     s->uart_val = 0x00000000;
244a7d2d98cSAlistair Francis     s->uart_timeout_ctrl = 0x00000000;
245a7d2d98cSAlistair Francis 
246a7d2d98cSAlistair Francis     s->tx_level = 0;
24782a4ed8eSAlexander Wagner     s->rx_level = 0;
248a7d2d98cSAlistair Francis 
249a7d2d98cSAlistair Francis     s->char_tx_time = (NANOSECONDS_PER_SECOND / 230400) * 10;
250a7d2d98cSAlistair Francis 
251a7d2d98cSAlistair Francis     ibex_uart_update_irqs(s);
252a7d2d98cSAlistair Francis }
253a7d2d98cSAlistair Francis 
ibex_uart_get_baud(IbexUartState * s)254940aabb9SAlistair Francis static uint64_t ibex_uart_get_baud(IbexUartState *s)
255940aabb9SAlistair Francis {
256940aabb9SAlistair Francis     uint64_t baud;
257940aabb9SAlistair Francis 
25859093cc4SAlistair Francis     baud = ((s->uart_ctrl & R_CTRL_NCO_MASK) >> 16);
259940aabb9SAlistair Francis     baud *= clock_get_hz(s->f_clk);
260940aabb9SAlistair Francis     baud >>= 20;
261940aabb9SAlistair Francis 
262940aabb9SAlistair Francis     return baud;
263940aabb9SAlistair Francis }
264940aabb9SAlistair Francis 
ibex_uart_read(void * opaque,hwaddr addr,unsigned int size)265a7d2d98cSAlistair Francis static uint64_t ibex_uart_read(void *opaque, hwaddr addr,
266a7d2d98cSAlistair Francis                                        unsigned int size)
267a7d2d98cSAlistair Francis {
268a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
269a7d2d98cSAlistair Francis     uint64_t retvalue = 0;
270a7d2d98cSAlistair Francis 
27159093cc4SAlistair Francis     switch (addr >> 2) {
27259093cc4SAlistair Francis     case R_INTR_STATE:
273a7d2d98cSAlistair Francis         retvalue = s->uart_intr_state;
274a7d2d98cSAlistair Francis         break;
27559093cc4SAlistair Francis     case R_INTR_ENABLE:
276a7d2d98cSAlistair Francis         retvalue = s->uart_intr_enable;
277a7d2d98cSAlistair Francis         break;
27859093cc4SAlistair Francis     case R_INTR_TEST:
279a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
280a7d2d98cSAlistair Francis                       "%s: wdata is write only\n", __func__);
281a7d2d98cSAlistair Francis         break;
282a7d2d98cSAlistair Francis 
28359093cc4SAlistair Francis     case R_CTRL:
284a7d2d98cSAlistair Francis         retvalue = s->uart_ctrl;
285a7d2d98cSAlistair Francis         break;
28659093cc4SAlistair Francis     case R_STATUS:
287a7d2d98cSAlistair Francis         retvalue = s->uart_status;
288a7d2d98cSAlistair Francis         break;
289a7d2d98cSAlistair Francis 
29059093cc4SAlistair Francis     case R_RDATA:
291a7d2d98cSAlistair Francis         retvalue = s->uart_rdata;
29282a4ed8eSAlexander Wagner         if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) && (s->rx_level > 0)) {
293a7d2d98cSAlistair Francis             qemu_chr_fe_accept_input(&s->chr);
294a7d2d98cSAlistair Francis 
29582a4ed8eSAlexander Wagner             s->rx_level -= 1;
29682a4ed8eSAlexander Wagner             s->uart_status &= ~R_STATUS_RXFULL_MASK;
29782a4ed8eSAlexander Wagner             if (s->rx_level == 0) {
29859093cc4SAlistair Francis                 s->uart_status |= R_STATUS_RXIDLE_MASK;
29959093cc4SAlistair Francis                 s->uart_status |= R_STATUS_RXEMPTY_MASK;
300a7d2d98cSAlistair Francis             }
30182a4ed8eSAlexander Wagner         }
302a7d2d98cSAlistair Francis         break;
30359093cc4SAlistair Francis     case R_WDATA:
304a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
305a7d2d98cSAlistair Francis                       "%s: wdata is write only\n", __func__);
306a7d2d98cSAlistair Francis         break;
307a7d2d98cSAlistair Francis 
30859093cc4SAlistair Francis     case R_FIFO_CTRL:
309a7d2d98cSAlistair Francis         retvalue = s->uart_fifo_ctrl;
310a7d2d98cSAlistair Francis         break;
31159093cc4SAlistair Francis     case R_FIFO_STATUS:
312a7d2d98cSAlistair Francis         retvalue = s->uart_fifo_status;
313a7d2d98cSAlistair Francis 
31482a4ed8eSAlexander Wagner         retvalue |= (s->rx_level & 0x1F) << R_FIFO_STATUS_RXLVL_SHIFT;
31582a4ed8eSAlexander Wagner         retvalue |= (s->tx_level & 0x1F) << R_FIFO_STATUS_TXLVL_SHIFT;
316a7d2d98cSAlistair Francis 
317a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
318a7d2d98cSAlistair Francis                       "%s: RX fifos are not supported\n", __func__);
319a7d2d98cSAlistair Francis         break;
320a7d2d98cSAlistair Francis 
32159093cc4SAlistair Francis     case R_OVRD:
322a7d2d98cSAlistair Francis         retvalue = s->uart_ovrd;
323a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
324a7d2d98cSAlistair Francis                       "%s: ovrd is not supported\n", __func__);
325a7d2d98cSAlistair Francis         break;
32659093cc4SAlistair Francis     case R_VAL:
327a7d2d98cSAlistair Francis         retvalue = s->uart_val;
328a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
329a7d2d98cSAlistair Francis                       "%s: val is not supported\n", __func__);
330a7d2d98cSAlistair Francis         break;
33159093cc4SAlistair Francis     case R_TIMEOUT_CTRL:
332a7d2d98cSAlistair Francis         retvalue = s->uart_timeout_ctrl;
333a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
334a7d2d98cSAlistair Francis                       "%s: timeout_ctrl is not supported\n", __func__);
335a7d2d98cSAlistair Francis         break;
336a7d2d98cSAlistair Francis     default:
337a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
338a7d2d98cSAlistair Francis                       "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
339a7d2d98cSAlistair Francis         return 0;
340a7d2d98cSAlistair Francis     }
341a7d2d98cSAlistair Francis 
342a7d2d98cSAlistair Francis     return retvalue;
343a7d2d98cSAlistair Francis }
344a7d2d98cSAlistair Francis 
ibex_uart_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)345a7d2d98cSAlistair Francis static void ibex_uart_write(void *opaque, hwaddr addr,
346a7d2d98cSAlistair Francis                                   uint64_t val64, unsigned int size)
347a7d2d98cSAlistair Francis {
348a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
349a7d2d98cSAlistair Francis     uint32_t value = val64;
350a7d2d98cSAlistair Francis 
35159093cc4SAlistair Francis     switch (addr >> 2) {
35259093cc4SAlistair Francis     case R_INTR_STATE:
353a7d2d98cSAlistair Francis         /* Write 1 clear */
354a7d2d98cSAlistair Francis         s->uart_intr_state &= ~value;
355a7d2d98cSAlistair Francis         ibex_uart_update_irqs(s);
356a7d2d98cSAlistair Francis         break;
35759093cc4SAlistair Francis     case R_INTR_ENABLE:
358a7d2d98cSAlistair Francis         s->uart_intr_enable = value;
359a7d2d98cSAlistair Francis         ibex_uart_update_irqs(s);
360a7d2d98cSAlistair Francis         break;
36159093cc4SAlistair Francis     case R_INTR_TEST:
362a7d2d98cSAlistair Francis         s->uart_intr_state |= value;
363a7d2d98cSAlistair Francis         ibex_uart_update_irqs(s);
364a7d2d98cSAlistair Francis         break;
365a7d2d98cSAlistair Francis 
36659093cc4SAlistair Francis     case R_CTRL:
367a7d2d98cSAlistair Francis         s->uart_ctrl = value;
368a7d2d98cSAlistair Francis 
36959093cc4SAlistair Francis         if (value & R_CTRL_NF_MASK) {
370a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
371a7d2d98cSAlistair Francis                           "%s: UART_CTRL_NF is not supported\n", __func__);
372a7d2d98cSAlistair Francis         }
37359093cc4SAlistair Francis         if (value & R_CTRL_SLPBK_MASK) {
374a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
375a7d2d98cSAlistair Francis                           "%s: UART_CTRL_SLPBK is not supported\n", __func__);
376a7d2d98cSAlistair Francis         }
37759093cc4SAlistair Francis         if (value & R_CTRL_LLPBK_MASK) {
378a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
379a7d2d98cSAlistair Francis                           "%s: UART_CTRL_LLPBK is not supported\n", __func__);
380a7d2d98cSAlistair Francis         }
38159093cc4SAlistair Francis         if (value & R_CTRL_PARITY_EN_MASK) {
382a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
383a7d2d98cSAlistair Francis                           "%s: UART_CTRL_PARITY_EN is not supported\n",
384a7d2d98cSAlistair Francis                           __func__);
385a7d2d98cSAlistair Francis         }
38659093cc4SAlistair Francis         if (value & R_CTRL_PARITY_ODD_MASK) {
387a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
388a7d2d98cSAlistair Francis                           "%s: UART_CTRL_PARITY_ODD is not supported\n",
389a7d2d98cSAlistair Francis                           __func__);
390a7d2d98cSAlistair Francis         }
39159093cc4SAlistair Francis         if (value & R_CTRL_RXBLVL_MASK) {
392a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
393a7d2d98cSAlistair Francis                           "%s: UART_CTRL_RXBLVL is not supported\n", __func__);
394a7d2d98cSAlistair Francis         }
39559093cc4SAlistair Francis         if (value & R_CTRL_NCO_MASK) {
396940aabb9SAlistair Francis             uint64_t baud = ibex_uart_get_baud(s);
397a7d2d98cSAlistair Francis 
398a7d2d98cSAlistair Francis             s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10;
399a7d2d98cSAlistair Francis         }
400a7d2d98cSAlistair Francis         break;
40159093cc4SAlistair Francis     case R_STATUS:
402a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
403a7d2d98cSAlistair Francis                       "%s: status is read only\n", __func__);
404a7d2d98cSAlistair Francis         break;
405a7d2d98cSAlistair Francis 
40659093cc4SAlistair Francis     case R_RDATA:
407a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
408a7d2d98cSAlistair Francis                       "%s: rdata is read only\n", __func__);
409a7d2d98cSAlistair Francis         break;
41059093cc4SAlistair Francis     case R_WDATA:
411a7d2d98cSAlistair Francis         uart_write_tx_fifo(s, (uint8_t *) &value, 1);
412a7d2d98cSAlistair Francis         break;
413a7d2d98cSAlistair Francis 
41459093cc4SAlistair Francis     case R_FIFO_CTRL:
415a7d2d98cSAlistair Francis         s->uart_fifo_ctrl = value;
416a7d2d98cSAlistair Francis 
41759093cc4SAlistair Francis         if (value & R_FIFO_CTRL_RXRST_MASK) {
41882a4ed8eSAlexander Wagner             s->rx_level = 0;
419a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
420a7d2d98cSAlistair Francis                           "%s: RX fifos are not supported\n", __func__);
421a7d2d98cSAlistair Francis         }
42259093cc4SAlistair Francis         if (value & R_FIFO_CTRL_TXRST_MASK) {
423a7d2d98cSAlistair Francis             s->tx_level = 0;
424a7d2d98cSAlistair Francis         }
425a7d2d98cSAlistair Francis         break;
42659093cc4SAlistair Francis     case R_FIFO_STATUS:
427a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
428a7d2d98cSAlistair Francis                       "%s: fifo_status is read only\n", __func__);
429a7d2d98cSAlistair Francis         break;
430a7d2d98cSAlistair Francis 
43159093cc4SAlistair Francis     case R_OVRD:
432a7d2d98cSAlistair Francis         s->uart_ovrd = value;
433a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
434a7d2d98cSAlistair Francis                       "%s: ovrd is not supported\n", __func__);
435a7d2d98cSAlistair Francis         break;
43659093cc4SAlistair Francis     case R_VAL:
437a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
438a7d2d98cSAlistair Francis                       "%s: val is read only\n", __func__);
439a7d2d98cSAlistair Francis         break;
44059093cc4SAlistair Francis     case R_TIMEOUT_CTRL:
441a7d2d98cSAlistair Francis         s->uart_timeout_ctrl = value;
442a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
443a7d2d98cSAlistair Francis                       "%s: timeout_ctrl is not supported\n", __func__);
444a7d2d98cSAlistair Francis         break;
445a7d2d98cSAlistair Francis     default:
446a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
447a7d2d98cSAlistair Francis                       "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
448a7d2d98cSAlistair Francis     }
449a7d2d98cSAlistair Francis }
450a7d2d98cSAlistair Francis 
ibex_uart_clk_update(void * opaque,ClockEvent event)4515ee0abedSPeter Maydell static void ibex_uart_clk_update(void *opaque, ClockEvent event)
452940aabb9SAlistair Francis {
453940aabb9SAlistair Francis     IbexUartState *s = opaque;
454940aabb9SAlistair Francis 
455940aabb9SAlistair Francis     /* recompute uart's speed on clock change */
456940aabb9SAlistair Francis     uint64_t baud = ibex_uart_get_baud(s);
457940aabb9SAlistair Francis 
458940aabb9SAlistair Francis     s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10;
459940aabb9SAlistair Francis }
460940aabb9SAlistair Francis 
fifo_trigger_update(void * opaque)461a7d2d98cSAlistair Francis static void fifo_trigger_update(void *opaque)
462a7d2d98cSAlistair Francis {
463a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
464a7d2d98cSAlistair Francis 
46559093cc4SAlistair Francis     if (s->uart_ctrl & R_CTRL_TX_ENABLE_MASK) {
466a7d2d98cSAlistair Francis         ibex_uart_xmit(NULL, G_IO_OUT, s);
467a7d2d98cSAlistair Francis     }
468a7d2d98cSAlistair Francis }
469a7d2d98cSAlistair Francis 
470a7d2d98cSAlistair Francis static const MemoryRegionOps ibex_uart_ops = {
471a7d2d98cSAlistair Francis     .read = ibex_uart_read,
472a7d2d98cSAlistair Francis     .write = ibex_uart_write,
473a7d2d98cSAlistair Francis     .endianness = DEVICE_NATIVE_ENDIAN,
474a7d2d98cSAlistair Francis     .impl.min_access_size = 4,
475a7d2d98cSAlistair Francis     .impl.max_access_size = 4,
476a7d2d98cSAlistair Francis };
477a7d2d98cSAlistair Francis 
ibex_uart_post_load(void * opaque,int version_id)478a7d2d98cSAlistair Francis static int ibex_uart_post_load(void *opaque, int version_id)
479a7d2d98cSAlistair Francis {
480a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
481a7d2d98cSAlistair Francis 
482a7d2d98cSAlistair Francis     ibex_uart_update_irqs(s);
483a7d2d98cSAlistair Francis     return 0;
484a7d2d98cSAlistair Francis }
485a7d2d98cSAlistair Francis 
486a7d2d98cSAlistair Francis static const VMStateDescription vmstate_ibex_uart = {
487a7d2d98cSAlistair Francis     .name = TYPE_IBEX_UART,
488a7d2d98cSAlistair Francis     .version_id = 1,
489a7d2d98cSAlistair Francis     .minimum_version_id = 1,
490a7d2d98cSAlistair Francis     .post_load = ibex_uart_post_load,
4912f6cab05SRichard Henderson     .fields = (const VMStateField[]) {
492a7d2d98cSAlistair Francis         VMSTATE_UINT8_ARRAY(tx_fifo, IbexUartState,
493a7d2d98cSAlistair Francis                             IBEX_UART_TX_FIFO_SIZE),
494a7d2d98cSAlistair Francis         VMSTATE_UINT32(tx_level, IbexUartState),
495a7d2d98cSAlistair Francis         VMSTATE_UINT64(char_tx_time, IbexUartState),
496a7d2d98cSAlistair Francis         VMSTATE_TIMER_PTR(fifo_trigger_handle, IbexUartState),
497a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_intr_state, IbexUartState),
498a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_intr_enable, IbexUartState),
499a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_ctrl, IbexUartState),
500a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_status, IbexUartState),
501a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_rdata, IbexUartState),
502a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_fifo_ctrl, IbexUartState),
503a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_fifo_status, IbexUartState),
504a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_ovrd, IbexUartState),
505a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_val, IbexUartState),
506a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_timeout_ctrl, IbexUartState),
507a7d2d98cSAlistair Francis         VMSTATE_END_OF_LIST()
508a7d2d98cSAlistair Francis     }
509a7d2d98cSAlistair Francis };
510a7d2d98cSAlistair Francis 
511a7d2d98cSAlistair Francis static Property ibex_uart_properties[] = {
512a7d2d98cSAlistair Francis     DEFINE_PROP_CHR("chardev", IbexUartState, chr),
513a7d2d98cSAlistair Francis     DEFINE_PROP_END_OF_LIST(),
514a7d2d98cSAlistair Francis };
515a7d2d98cSAlistair Francis 
ibex_uart_init(Object * obj)516a7d2d98cSAlistair Francis static void ibex_uart_init(Object *obj)
517a7d2d98cSAlistair Francis {
518a7d2d98cSAlistair Francis     IbexUartState *s = IBEX_UART(obj);
519a7d2d98cSAlistair Francis 
520940aabb9SAlistair Francis     s->f_clk = qdev_init_clock_in(DEVICE(obj), "f_clock",
5215ee0abedSPeter Maydell                                   ibex_uart_clk_update, s, ClockUpdate);
522940aabb9SAlistair Francis     clock_set_hz(s->f_clk, IBEX_UART_CLOCK);
523940aabb9SAlistair Francis 
524a7d2d98cSAlistair Francis     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_watermark);
525a7d2d98cSAlistair Francis     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_watermark);
526a7d2d98cSAlistair Francis     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_empty);
527a7d2d98cSAlistair Francis     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_overflow);
528a7d2d98cSAlistair Francis 
529a7d2d98cSAlistair Francis     memory_region_init_io(&s->mmio, obj, &ibex_uart_ops, s,
530a7d2d98cSAlistair Francis                           TYPE_IBEX_UART, 0x400);
531a7d2d98cSAlistair Francis     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
532a7d2d98cSAlistair Francis }
533a7d2d98cSAlistair Francis 
ibex_uart_realize(DeviceState * dev,Error ** errp)534a7d2d98cSAlistair Francis static void ibex_uart_realize(DeviceState *dev, Error **errp)
535a7d2d98cSAlistair Francis {
536a7d2d98cSAlistair Francis     IbexUartState *s = IBEX_UART(dev);
537a7d2d98cSAlistair Francis 
538a7d2d98cSAlistair Francis     s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
539a7d2d98cSAlistair Francis                                           fifo_trigger_update, s);
540a7d2d98cSAlistair Francis 
541a7d2d98cSAlistair Francis     qemu_chr_fe_set_handlers(&s->chr, ibex_uart_can_receive,
542a7d2d98cSAlistair Francis                              ibex_uart_receive, NULL, NULL,
543a7d2d98cSAlistair Francis                              s, NULL, true);
544a7d2d98cSAlistair Francis }
545a7d2d98cSAlistair Francis 
ibex_uart_class_init(ObjectClass * klass,void * data)546a7d2d98cSAlistair Francis static void ibex_uart_class_init(ObjectClass *klass, void *data)
547a7d2d98cSAlistair Francis {
548a7d2d98cSAlistair Francis     DeviceClass *dc = DEVICE_CLASS(klass);
549a7d2d98cSAlistair Francis 
550*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, ibex_uart_reset);
551a7d2d98cSAlistair Francis     dc->realize = ibex_uart_realize;
552a7d2d98cSAlistair Francis     dc->vmsd = &vmstate_ibex_uart;
553a7d2d98cSAlistair Francis     device_class_set_props(dc, ibex_uart_properties);
55434229c46SBin Meng     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
555a7d2d98cSAlistair Francis }
556a7d2d98cSAlistair Francis 
557a7d2d98cSAlistair Francis static const TypeInfo ibex_uart_info = {
558a7d2d98cSAlistair Francis     .name          = TYPE_IBEX_UART,
559a7d2d98cSAlistair Francis     .parent        = TYPE_SYS_BUS_DEVICE,
560a7d2d98cSAlistair Francis     .instance_size = sizeof(IbexUartState),
561a7d2d98cSAlistair Francis     .instance_init = ibex_uart_init,
562a7d2d98cSAlistair Francis     .class_init    = ibex_uart_class_init,
563a7d2d98cSAlistair Francis };
564a7d2d98cSAlistair Francis 
ibex_uart_register_types(void)565a7d2d98cSAlistair Francis static void ibex_uart_register_types(void)
566a7d2d98cSAlistair Francis {
567a7d2d98cSAlistair Francis     type_register_static(&ibex_uart_info);
568a7d2d98cSAlistair Francis }
569a7d2d98cSAlistair Francis 
570a7d2d98cSAlistair Francis type_init(ibex_uart_register_types)
571