xref: /qemu/hw/char/ibex_uart.c (revision bf7b1eab252bc56b6bbb12a8909eae738435d6ae)
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"
34a7d2d98cSAlistair Francis #include "migration/vmstate.h"
35a7d2d98cSAlistair Francis #include "qemu/log.h"
36a7d2d98cSAlistair Francis #include "qemu/module.h"
37a7d2d98cSAlistair Francis 
38bdc36ce6SAlistair Francis REG32(INTR_STATE, 0x00)
39bdc36ce6SAlistair Francis     FIELD(INTR_STATE, TX_WATERMARK, 0, 1)
40bdc36ce6SAlistair Francis     FIELD(INTR_STATE, RX_WATERMARK, 1, 1)
41bdc36ce6SAlistair Francis     FIELD(INTR_STATE, TX_EMPTY, 2, 1)
42bdc36ce6SAlistair Francis     FIELD(INTR_STATE, RX_OVERFLOW, 3, 1)
43bdc36ce6SAlistair Francis REG32(INTR_ENABLE, 0x04)
44bdc36ce6SAlistair Francis REG32(INTR_TEST, 0x08)
4524bfb98dSAlistair Francis REG32(ALERT_TEST, 0x0C)
4624bfb98dSAlistair Francis REG32(CTRL, 0x10)
47bdc36ce6SAlistair Francis     FIELD(CTRL, TX_ENABLE, 0, 1)
48bdc36ce6SAlistair Francis     FIELD(CTRL, RX_ENABLE, 1, 1)
49bdc36ce6SAlistair Francis     FIELD(CTRL, NF, 2, 1)
50bdc36ce6SAlistair Francis     FIELD(CTRL, SLPBK, 4, 1)
51bdc36ce6SAlistair Francis     FIELD(CTRL, LLPBK, 5, 1)
52bdc36ce6SAlistair Francis     FIELD(CTRL, PARITY_EN, 6, 1)
53bdc36ce6SAlistair Francis     FIELD(CTRL, PARITY_ODD, 7, 1)
54bdc36ce6SAlistair Francis     FIELD(CTRL, RXBLVL, 8, 2)
55bdc36ce6SAlistair Francis     FIELD(CTRL, NCO, 16, 16)
5624bfb98dSAlistair Francis REG32(STATUS, 0x14)
57bdc36ce6SAlistair Francis     FIELD(STATUS, TXFULL, 0, 1)
58bdc36ce6SAlistair Francis     FIELD(STATUS, RXFULL, 1, 1)
59bdc36ce6SAlistair Francis     FIELD(STATUS, TXEMPTY, 2, 1)
60bdc36ce6SAlistair Francis     FIELD(STATUS, RXIDLE, 4, 1)
61bdc36ce6SAlistair Francis     FIELD(STATUS, RXEMPTY, 5, 1)
6224bfb98dSAlistair Francis REG32(RDATA, 0x18)
6324bfb98dSAlistair Francis REG32(WDATA, 0x1C)
6424bfb98dSAlistair Francis REG32(FIFO_CTRL, 0x20)
65bdc36ce6SAlistair Francis     FIELD(FIFO_CTRL, RXRST, 0, 1)
66bdc36ce6SAlistair Francis     FIELD(FIFO_CTRL, TXRST, 1, 1)
67bdc36ce6SAlistair Francis     FIELD(FIFO_CTRL, RXILVL, 2, 3)
68bdc36ce6SAlistair Francis     FIELD(FIFO_CTRL, TXILVL, 5, 2)
6924bfb98dSAlistair Francis REG32(FIFO_STATUS, 0x24)
70bdc36ce6SAlistair Francis     FIELD(FIFO_STATUS, TXLVL, 0, 5)
71bdc36ce6SAlistair Francis     FIELD(FIFO_STATUS, RXLVL, 16, 5)
7224bfb98dSAlistair Francis REG32(OVRD, 0x28)
7324bfb98dSAlistair Francis REG32(VAL, 0x2C)
7424bfb98dSAlistair Francis REG32(TIMEOUT_CTRL, 0x30)
75bdc36ce6SAlistair Francis 
76a7d2d98cSAlistair Francis static void ibex_uart_update_irqs(IbexUartState *s)
77a7d2d98cSAlistair Francis {
7859093cc4SAlistair Francis     if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_WATERMARK_MASK) {
79a7d2d98cSAlistair Francis         qemu_set_irq(s->tx_watermark, 1);
80a7d2d98cSAlistair Francis     } else {
81a7d2d98cSAlistair Francis         qemu_set_irq(s->tx_watermark, 0);
82a7d2d98cSAlistair Francis     }
83a7d2d98cSAlistair Francis 
8459093cc4SAlistair Francis     if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_WATERMARK_MASK) {
85a7d2d98cSAlistair Francis         qemu_set_irq(s->rx_watermark, 1);
86a7d2d98cSAlistair Francis     } else {
87a7d2d98cSAlistair Francis         qemu_set_irq(s->rx_watermark, 0);
88a7d2d98cSAlistair Francis     }
89a7d2d98cSAlistair Francis 
9059093cc4SAlistair Francis     if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_EMPTY_MASK) {
91a7d2d98cSAlistair Francis         qemu_set_irq(s->tx_empty, 1);
92a7d2d98cSAlistair Francis     } else {
93a7d2d98cSAlistair Francis         qemu_set_irq(s->tx_empty, 0);
94a7d2d98cSAlistair Francis     }
95a7d2d98cSAlistair Francis 
9659093cc4SAlistair Francis     if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_OVERFLOW_MASK) {
97a7d2d98cSAlistair Francis         qemu_set_irq(s->rx_overflow, 1);
98a7d2d98cSAlistair Francis     } else {
99a7d2d98cSAlistair Francis         qemu_set_irq(s->rx_overflow, 0);
100a7d2d98cSAlistair Francis     }
101a7d2d98cSAlistair Francis }
102a7d2d98cSAlistair Francis 
103a7d2d98cSAlistair Francis static int ibex_uart_can_receive(void *opaque)
104a7d2d98cSAlistair Francis {
105a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
106a7d2d98cSAlistair Francis 
10782a4ed8eSAlexander Wagner     if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK)
10882a4ed8eSAlexander Wagner            && !(s->uart_status & R_STATUS_RXFULL_MASK)) {
109a7d2d98cSAlistair Francis         return 1;
110a7d2d98cSAlistair Francis     }
111a7d2d98cSAlistair Francis 
112a7d2d98cSAlistair Francis     return 0;
113a7d2d98cSAlistair Francis }
114a7d2d98cSAlistair Francis 
115a7d2d98cSAlistair Francis static void ibex_uart_receive(void *opaque, const uint8_t *buf, int size)
116a7d2d98cSAlistair Francis {
117a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
11859093cc4SAlistair Francis     uint8_t rx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_RXILVL_MASK)
11959093cc4SAlistair Francis                             >> R_FIFO_CTRL_RXILVL_SHIFT;
120a7d2d98cSAlistair Francis 
121a7d2d98cSAlistair Francis     s->uart_rdata = *buf;
122a7d2d98cSAlistair Francis 
12359093cc4SAlistair Francis     s->uart_status &= ~R_STATUS_RXIDLE_MASK;
12459093cc4SAlistair Francis     s->uart_status &= ~R_STATUS_RXEMPTY_MASK;
12582a4ed8eSAlexander Wagner     /* The RXFULL is set after receiving a single byte
12682a4ed8eSAlexander Wagner      * as the FIFO buffers are not yet implemented.
12782a4ed8eSAlexander Wagner      */
12882a4ed8eSAlexander Wagner     s->uart_status |= R_STATUS_RXFULL_MASK;
12982a4ed8eSAlexander Wagner     s->rx_level += 1;
130a7d2d98cSAlistair Francis 
131a7d2d98cSAlistair Francis     if (size > rx_fifo_level) {
13259093cc4SAlistair Francis         s->uart_intr_state |= R_INTR_STATE_RX_WATERMARK_MASK;
133a7d2d98cSAlistair Francis     }
134a7d2d98cSAlistair Francis 
135a7d2d98cSAlistair Francis     ibex_uart_update_irqs(s);
136a7d2d98cSAlistair Francis }
137a7d2d98cSAlistair Francis 
138*bf7b1eabSMarc-André Lureau static gboolean ibex_uart_xmit(void *do_not_use, GIOCondition cond,
139a7d2d98cSAlistair Francis                                void *opaque)
140a7d2d98cSAlistair Francis {
141a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
14259093cc4SAlistair Francis     uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
14359093cc4SAlistair Francis                             >> R_FIFO_CTRL_TXILVL_SHIFT;
144a7d2d98cSAlistair Francis     int ret;
145a7d2d98cSAlistair Francis 
146a7d2d98cSAlistair Francis     /* instant drain the fifo when there's no back-end */
147a7d2d98cSAlistair Francis     if (!qemu_chr_fe_backend_connected(&s->chr)) {
148a7d2d98cSAlistair Francis         s->tx_level = 0;
149a7d2d98cSAlistair Francis         return FALSE;
150a7d2d98cSAlistair Francis     }
151a7d2d98cSAlistair Francis 
152a7d2d98cSAlistair Francis     if (!s->tx_level) {
15359093cc4SAlistair Francis         s->uart_status &= ~R_STATUS_TXFULL_MASK;
15459093cc4SAlistair Francis         s->uart_status |= R_STATUS_TXEMPTY_MASK;
15559093cc4SAlistair Francis         s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
15659093cc4SAlistair Francis         s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
157a7d2d98cSAlistair Francis         ibex_uart_update_irqs(s);
158a7d2d98cSAlistair Francis         return FALSE;
159a7d2d98cSAlistair Francis     }
160a7d2d98cSAlistair Francis 
161a7d2d98cSAlistair Francis     ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level);
162a7d2d98cSAlistair Francis 
163a7d2d98cSAlistair Francis     if (ret >= 0) {
164a7d2d98cSAlistair Francis         s->tx_level -= ret;
165a7d2d98cSAlistair Francis         memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_level);
166a7d2d98cSAlistair Francis     }
167a7d2d98cSAlistair Francis 
168a7d2d98cSAlistair Francis     if (s->tx_level) {
169a7d2d98cSAlistair Francis         guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
170a7d2d98cSAlistair Francis                                         ibex_uart_xmit, s);
171a7d2d98cSAlistair Francis         if (!r) {
172a7d2d98cSAlistair Francis             s->tx_level = 0;
173a7d2d98cSAlistair Francis             return FALSE;
174a7d2d98cSAlistair Francis         }
175a7d2d98cSAlistair Francis     }
176a7d2d98cSAlistair Francis 
177a7d2d98cSAlistair Francis     /* Clear the TX Full bit */
178a7d2d98cSAlistair Francis     if (s->tx_level != IBEX_UART_TX_FIFO_SIZE) {
17959093cc4SAlistair Francis         s->uart_status &= ~R_STATUS_TXFULL_MASK;
180a7d2d98cSAlistair Francis     }
181a7d2d98cSAlistair Francis 
182a7d2d98cSAlistair Francis     /* Disable the TX_WATERMARK IRQ */
183a7d2d98cSAlistair Francis     if (s->tx_level < tx_fifo_level) {
18459093cc4SAlistair Francis         s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
185a7d2d98cSAlistair Francis     }
186a7d2d98cSAlistair Francis 
187a7d2d98cSAlistair Francis     /* Set TX empty */
188a7d2d98cSAlistair Francis     if (s->tx_level == 0) {
18959093cc4SAlistair Francis         s->uart_status |= R_STATUS_TXEMPTY_MASK;
19059093cc4SAlistair Francis         s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
191a7d2d98cSAlistair Francis     }
192a7d2d98cSAlistair Francis 
193a7d2d98cSAlistair Francis     ibex_uart_update_irqs(s);
194a7d2d98cSAlistair Francis     return FALSE;
195a7d2d98cSAlistair Francis }
196a7d2d98cSAlistair Francis 
197a7d2d98cSAlistair Francis static void uart_write_tx_fifo(IbexUartState *s, const uint8_t *buf,
198a7d2d98cSAlistair Francis                                int size)
199a7d2d98cSAlistair Francis {
200a7d2d98cSAlistair Francis     uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
20159093cc4SAlistair Francis     uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
20259093cc4SAlistair Francis                             >> R_FIFO_CTRL_TXILVL_SHIFT;
203a7d2d98cSAlistair Francis 
204a7d2d98cSAlistair Francis     if (size > IBEX_UART_TX_FIFO_SIZE - s->tx_level) {
205a7d2d98cSAlistair Francis         size = IBEX_UART_TX_FIFO_SIZE - s->tx_level;
206a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR, "ibex_uart: TX FIFO overflow");
207a7d2d98cSAlistair Francis     }
208a7d2d98cSAlistair Francis 
209a7d2d98cSAlistair Francis     memcpy(s->tx_fifo + s->tx_level, buf, size);
210a7d2d98cSAlistair Francis     s->tx_level += size;
211a7d2d98cSAlistair Francis 
212a7d2d98cSAlistair Francis     if (s->tx_level > 0) {
21359093cc4SAlistair Francis         s->uart_status &= ~R_STATUS_TXEMPTY_MASK;
214a7d2d98cSAlistair Francis     }
215a7d2d98cSAlistair Francis 
216a7d2d98cSAlistair Francis     if (s->tx_level >= tx_fifo_level) {
21759093cc4SAlistair Francis         s->uart_intr_state |= R_INTR_STATE_TX_WATERMARK_MASK;
218a7d2d98cSAlistair Francis         ibex_uart_update_irqs(s);
219a7d2d98cSAlistair Francis     }
220a7d2d98cSAlistair Francis 
221a7d2d98cSAlistair Francis     if (s->tx_level == IBEX_UART_TX_FIFO_SIZE) {
22259093cc4SAlistair Francis         s->uart_status |= R_STATUS_TXFULL_MASK;
223a7d2d98cSAlistair Francis     }
224a7d2d98cSAlistair Francis 
225a7d2d98cSAlistair Francis     timer_mod(s->fifo_trigger_handle, current_time +
226a7d2d98cSAlistair Francis               (s->char_tx_time * 4));
227a7d2d98cSAlistair Francis }
228a7d2d98cSAlistair Francis 
229a7d2d98cSAlistair Francis static void ibex_uart_reset(DeviceState *dev)
230a7d2d98cSAlistair Francis {
231a7d2d98cSAlistair Francis     IbexUartState *s = IBEX_UART(dev);
232a7d2d98cSAlistair Francis 
233a7d2d98cSAlistair Francis     s->uart_intr_state = 0x00000000;
234a7d2d98cSAlistair Francis     s->uart_intr_state = 0x00000000;
235a7d2d98cSAlistair Francis     s->uart_intr_enable = 0x00000000;
236a7d2d98cSAlistair Francis     s->uart_ctrl = 0x00000000;
237a7d2d98cSAlistair Francis     s->uart_status = 0x0000003c;
238a7d2d98cSAlistair Francis     s->uart_rdata = 0x00000000;
239a7d2d98cSAlistair Francis     s->uart_fifo_ctrl = 0x00000000;
240a7d2d98cSAlistair Francis     s->uart_fifo_status = 0x00000000;
241a7d2d98cSAlistair Francis     s->uart_ovrd = 0x00000000;
242a7d2d98cSAlistair Francis     s->uart_val = 0x00000000;
243a7d2d98cSAlistair Francis     s->uart_timeout_ctrl = 0x00000000;
244a7d2d98cSAlistair Francis 
245a7d2d98cSAlistair Francis     s->tx_level = 0;
24682a4ed8eSAlexander Wagner     s->rx_level = 0;
247a7d2d98cSAlistair Francis 
248a7d2d98cSAlistair Francis     s->char_tx_time = (NANOSECONDS_PER_SECOND / 230400) * 10;
249a7d2d98cSAlistair Francis 
250a7d2d98cSAlistair Francis     ibex_uart_update_irqs(s);
251a7d2d98cSAlistair Francis }
252a7d2d98cSAlistair Francis 
253940aabb9SAlistair Francis static uint64_t ibex_uart_get_baud(IbexUartState *s)
254940aabb9SAlistair Francis {
255940aabb9SAlistair Francis     uint64_t baud;
256940aabb9SAlistair Francis 
25759093cc4SAlistair Francis     baud = ((s->uart_ctrl & R_CTRL_NCO_MASK) >> 16);
258940aabb9SAlistair Francis     baud *= clock_get_hz(s->f_clk);
259940aabb9SAlistair Francis     baud >>= 20;
260940aabb9SAlistair Francis 
261940aabb9SAlistair Francis     return baud;
262940aabb9SAlistair Francis }
263940aabb9SAlistair Francis 
264a7d2d98cSAlistair Francis static uint64_t ibex_uart_read(void *opaque, hwaddr addr,
265a7d2d98cSAlistair Francis                                        unsigned int size)
266a7d2d98cSAlistair Francis {
267a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
268a7d2d98cSAlistair Francis     uint64_t retvalue = 0;
269a7d2d98cSAlistair Francis 
27059093cc4SAlistair Francis     switch (addr >> 2) {
27159093cc4SAlistair Francis     case R_INTR_STATE:
272a7d2d98cSAlistair Francis         retvalue = s->uart_intr_state;
273a7d2d98cSAlistair Francis         break;
27459093cc4SAlistair Francis     case R_INTR_ENABLE:
275a7d2d98cSAlistair Francis         retvalue = s->uart_intr_enable;
276a7d2d98cSAlistair Francis         break;
27759093cc4SAlistair Francis     case R_INTR_TEST:
278a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
279a7d2d98cSAlistair Francis                       "%s: wdata is write only\n", __func__);
280a7d2d98cSAlistair Francis         break;
281a7d2d98cSAlistair Francis 
28259093cc4SAlistair Francis     case R_CTRL:
283a7d2d98cSAlistair Francis         retvalue = s->uart_ctrl;
284a7d2d98cSAlistair Francis         break;
28559093cc4SAlistair Francis     case R_STATUS:
286a7d2d98cSAlistair Francis         retvalue = s->uart_status;
287a7d2d98cSAlistair Francis         break;
288a7d2d98cSAlistair Francis 
28959093cc4SAlistair Francis     case R_RDATA:
290a7d2d98cSAlistair Francis         retvalue = s->uart_rdata;
29182a4ed8eSAlexander Wagner         if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) && (s->rx_level > 0)) {
292a7d2d98cSAlistair Francis             qemu_chr_fe_accept_input(&s->chr);
293a7d2d98cSAlistair Francis 
29482a4ed8eSAlexander Wagner             s->rx_level -= 1;
29582a4ed8eSAlexander Wagner             s->uart_status &= ~R_STATUS_RXFULL_MASK;
29682a4ed8eSAlexander Wagner             if (s->rx_level == 0) {
29759093cc4SAlistair Francis                 s->uart_status |= R_STATUS_RXIDLE_MASK;
29859093cc4SAlistair Francis                 s->uart_status |= R_STATUS_RXEMPTY_MASK;
299a7d2d98cSAlistair Francis             }
30082a4ed8eSAlexander Wagner         }
301a7d2d98cSAlistair Francis         break;
30259093cc4SAlistair Francis     case R_WDATA:
303a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
304a7d2d98cSAlistair Francis                       "%s: wdata is write only\n", __func__);
305a7d2d98cSAlistair Francis         break;
306a7d2d98cSAlistair Francis 
30759093cc4SAlistair Francis     case R_FIFO_CTRL:
308a7d2d98cSAlistair Francis         retvalue = s->uart_fifo_ctrl;
309a7d2d98cSAlistair Francis         break;
31059093cc4SAlistair Francis     case R_FIFO_STATUS:
311a7d2d98cSAlistair Francis         retvalue = s->uart_fifo_status;
312a7d2d98cSAlistair Francis 
31382a4ed8eSAlexander Wagner         retvalue |= (s->rx_level & 0x1F) << R_FIFO_STATUS_RXLVL_SHIFT;
31482a4ed8eSAlexander Wagner         retvalue |= (s->tx_level & 0x1F) << R_FIFO_STATUS_TXLVL_SHIFT;
315a7d2d98cSAlistair Francis 
316a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
317a7d2d98cSAlistair Francis                       "%s: RX fifos are not supported\n", __func__);
318a7d2d98cSAlistair Francis         break;
319a7d2d98cSAlistair Francis 
32059093cc4SAlistair Francis     case R_OVRD:
321a7d2d98cSAlistair Francis         retvalue = s->uart_ovrd;
322a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
323a7d2d98cSAlistair Francis                       "%s: ovrd is not supported\n", __func__);
324a7d2d98cSAlistair Francis         break;
32559093cc4SAlistair Francis     case R_VAL:
326a7d2d98cSAlistair Francis         retvalue = s->uart_val;
327a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
328a7d2d98cSAlistair Francis                       "%s: val is not supported\n", __func__);
329a7d2d98cSAlistair Francis         break;
33059093cc4SAlistair Francis     case R_TIMEOUT_CTRL:
331a7d2d98cSAlistair Francis         retvalue = s->uart_timeout_ctrl;
332a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
333a7d2d98cSAlistair Francis                       "%s: timeout_ctrl is not supported\n", __func__);
334a7d2d98cSAlistair Francis         break;
335a7d2d98cSAlistair Francis     default:
336a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
337a7d2d98cSAlistair Francis                       "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
338a7d2d98cSAlistair Francis         return 0;
339a7d2d98cSAlistair Francis     }
340a7d2d98cSAlistair Francis 
341a7d2d98cSAlistair Francis     return retvalue;
342a7d2d98cSAlistair Francis }
343a7d2d98cSAlistair Francis 
344a7d2d98cSAlistair Francis static void ibex_uart_write(void *opaque, hwaddr addr,
345a7d2d98cSAlistair Francis                                   uint64_t val64, unsigned int size)
346a7d2d98cSAlistair Francis {
347a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
348a7d2d98cSAlistair Francis     uint32_t value = val64;
349a7d2d98cSAlistair Francis 
35059093cc4SAlistair Francis     switch (addr >> 2) {
35159093cc4SAlistair Francis     case R_INTR_STATE:
352a7d2d98cSAlistair Francis         /* Write 1 clear */
353a7d2d98cSAlistair Francis         s->uart_intr_state &= ~value;
354a7d2d98cSAlistair Francis         ibex_uart_update_irqs(s);
355a7d2d98cSAlistair Francis         break;
35659093cc4SAlistair Francis     case R_INTR_ENABLE:
357a7d2d98cSAlistair Francis         s->uart_intr_enable = value;
358a7d2d98cSAlistair Francis         ibex_uart_update_irqs(s);
359a7d2d98cSAlistair Francis         break;
36059093cc4SAlistair Francis     case R_INTR_TEST:
361a7d2d98cSAlistair Francis         s->uart_intr_state |= value;
362a7d2d98cSAlistair Francis         ibex_uart_update_irqs(s);
363a7d2d98cSAlistair Francis         break;
364a7d2d98cSAlistair Francis 
36559093cc4SAlistair Francis     case R_CTRL:
366a7d2d98cSAlistair Francis         s->uart_ctrl = value;
367a7d2d98cSAlistair Francis 
36859093cc4SAlistair Francis         if (value & R_CTRL_NF_MASK) {
369a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
370a7d2d98cSAlistair Francis                           "%s: UART_CTRL_NF is not supported\n", __func__);
371a7d2d98cSAlistair Francis         }
37259093cc4SAlistair Francis         if (value & R_CTRL_SLPBK_MASK) {
373a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
374a7d2d98cSAlistair Francis                           "%s: UART_CTRL_SLPBK is not supported\n", __func__);
375a7d2d98cSAlistair Francis         }
37659093cc4SAlistair Francis         if (value & R_CTRL_LLPBK_MASK) {
377a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
378a7d2d98cSAlistair Francis                           "%s: UART_CTRL_LLPBK is not supported\n", __func__);
379a7d2d98cSAlistair Francis         }
38059093cc4SAlistair Francis         if (value & R_CTRL_PARITY_EN_MASK) {
381a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
382a7d2d98cSAlistair Francis                           "%s: UART_CTRL_PARITY_EN is not supported\n",
383a7d2d98cSAlistair Francis                           __func__);
384a7d2d98cSAlistair Francis         }
38559093cc4SAlistair Francis         if (value & R_CTRL_PARITY_ODD_MASK) {
386a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
387a7d2d98cSAlistair Francis                           "%s: UART_CTRL_PARITY_ODD is not supported\n",
388a7d2d98cSAlistair Francis                           __func__);
389a7d2d98cSAlistair Francis         }
39059093cc4SAlistair Francis         if (value & R_CTRL_RXBLVL_MASK) {
391a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
392a7d2d98cSAlistair Francis                           "%s: UART_CTRL_RXBLVL is not supported\n", __func__);
393a7d2d98cSAlistair Francis         }
39459093cc4SAlistair Francis         if (value & R_CTRL_NCO_MASK) {
395940aabb9SAlistair Francis             uint64_t baud = ibex_uart_get_baud(s);
396a7d2d98cSAlistair Francis 
397a7d2d98cSAlistair Francis             s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10;
398a7d2d98cSAlistair Francis         }
399a7d2d98cSAlistair Francis         break;
40059093cc4SAlistair Francis     case R_STATUS:
401a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
402a7d2d98cSAlistair Francis                       "%s: status is read only\n", __func__);
403a7d2d98cSAlistair Francis         break;
404a7d2d98cSAlistair Francis 
40559093cc4SAlistair Francis     case R_RDATA:
406a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
407a7d2d98cSAlistair Francis                       "%s: rdata is read only\n", __func__);
408a7d2d98cSAlistair Francis         break;
40959093cc4SAlistair Francis     case R_WDATA:
410a7d2d98cSAlistair Francis         uart_write_tx_fifo(s, (uint8_t *) &value, 1);
411a7d2d98cSAlistair Francis         break;
412a7d2d98cSAlistair Francis 
41359093cc4SAlistair Francis     case R_FIFO_CTRL:
414a7d2d98cSAlistair Francis         s->uart_fifo_ctrl = value;
415a7d2d98cSAlistair Francis 
41659093cc4SAlistair Francis         if (value & R_FIFO_CTRL_RXRST_MASK) {
41782a4ed8eSAlexander Wagner             s->rx_level = 0;
418a7d2d98cSAlistair Francis             qemu_log_mask(LOG_UNIMP,
419a7d2d98cSAlistair Francis                           "%s: RX fifos are not supported\n", __func__);
420a7d2d98cSAlistair Francis         }
42159093cc4SAlistair Francis         if (value & R_FIFO_CTRL_TXRST_MASK) {
422a7d2d98cSAlistair Francis             s->tx_level = 0;
423a7d2d98cSAlistair Francis         }
424a7d2d98cSAlistair Francis         break;
42559093cc4SAlistair Francis     case R_FIFO_STATUS:
426a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
427a7d2d98cSAlistair Francis                       "%s: fifo_status is read only\n", __func__);
428a7d2d98cSAlistair Francis         break;
429a7d2d98cSAlistair Francis 
43059093cc4SAlistair Francis     case R_OVRD:
431a7d2d98cSAlistair Francis         s->uart_ovrd = value;
432a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
433a7d2d98cSAlistair Francis                       "%s: ovrd is not supported\n", __func__);
434a7d2d98cSAlistair Francis         break;
43559093cc4SAlistair Francis     case R_VAL:
436a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
437a7d2d98cSAlistair Francis                       "%s: val is read only\n", __func__);
438a7d2d98cSAlistair Francis         break;
43959093cc4SAlistair Francis     case R_TIMEOUT_CTRL:
440a7d2d98cSAlistair Francis         s->uart_timeout_ctrl = value;
441a7d2d98cSAlistair Francis         qemu_log_mask(LOG_UNIMP,
442a7d2d98cSAlistair Francis                       "%s: timeout_ctrl is not supported\n", __func__);
443a7d2d98cSAlistair Francis         break;
444a7d2d98cSAlistair Francis     default:
445a7d2d98cSAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
446a7d2d98cSAlistair Francis                       "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
447a7d2d98cSAlistair Francis     }
448a7d2d98cSAlistair Francis }
449a7d2d98cSAlistair Francis 
4505ee0abedSPeter Maydell static void ibex_uart_clk_update(void *opaque, ClockEvent event)
451940aabb9SAlistair Francis {
452940aabb9SAlistair Francis     IbexUartState *s = opaque;
453940aabb9SAlistair Francis 
454940aabb9SAlistair Francis     /* recompute uart's speed on clock change */
455940aabb9SAlistair Francis     uint64_t baud = ibex_uart_get_baud(s);
456940aabb9SAlistair Francis 
457940aabb9SAlistair Francis     s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10;
458940aabb9SAlistair Francis }
459940aabb9SAlistair Francis 
460a7d2d98cSAlistair Francis static void fifo_trigger_update(void *opaque)
461a7d2d98cSAlistair Francis {
462a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
463a7d2d98cSAlistair Francis 
46459093cc4SAlistair Francis     if (s->uart_ctrl & R_CTRL_TX_ENABLE_MASK) {
465a7d2d98cSAlistair Francis         ibex_uart_xmit(NULL, G_IO_OUT, s);
466a7d2d98cSAlistair Francis     }
467a7d2d98cSAlistair Francis }
468a7d2d98cSAlistair Francis 
469a7d2d98cSAlistair Francis static const MemoryRegionOps ibex_uart_ops = {
470a7d2d98cSAlistair Francis     .read = ibex_uart_read,
471a7d2d98cSAlistair Francis     .write = ibex_uart_write,
472a7d2d98cSAlistair Francis     .endianness = DEVICE_NATIVE_ENDIAN,
473a7d2d98cSAlistair Francis     .impl.min_access_size = 4,
474a7d2d98cSAlistair Francis     .impl.max_access_size = 4,
475a7d2d98cSAlistair Francis };
476a7d2d98cSAlistair Francis 
477a7d2d98cSAlistair Francis static int ibex_uart_post_load(void *opaque, int version_id)
478a7d2d98cSAlistair Francis {
479a7d2d98cSAlistair Francis     IbexUartState *s = opaque;
480a7d2d98cSAlistair Francis 
481a7d2d98cSAlistair Francis     ibex_uart_update_irqs(s);
482a7d2d98cSAlistair Francis     return 0;
483a7d2d98cSAlistair Francis }
484a7d2d98cSAlistair Francis 
485a7d2d98cSAlistair Francis static const VMStateDescription vmstate_ibex_uart = {
486a7d2d98cSAlistair Francis     .name = TYPE_IBEX_UART,
487a7d2d98cSAlistair Francis     .version_id = 1,
488a7d2d98cSAlistair Francis     .minimum_version_id = 1,
489a7d2d98cSAlistair Francis     .post_load = ibex_uart_post_load,
490a7d2d98cSAlistair Francis     .fields = (VMStateField[]) {
491a7d2d98cSAlistair Francis         VMSTATE_UINT8_ARRAY(tx_fifo, IbexUartState,
492a7d2d98cSAlistair Francis                             IBEX_UART_TX_FIFO_SIZE),
493a7d2d98cSAlistair Francis         VMSTATE_UINT32(tx_level, IbexUartState),
494a7d2d98cSAlistair Francis         VMSTATE_UINT64(char_tx_time, IbexUartState),
495a7d2d98cSAlistair Francis         VMSTATE_TIMER_PTR(fifo_trigger_handle, IbexUartState),
496a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_intr_state, IbexUartState),
497a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_intr_enable, IbexUartState),
498a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_ctrl, IbexUartState),
499a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_status, IbexUartState),
500a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_rdata, IbexUartState),
501a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_fifo_ctrl, IbexUartState),
502a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_fifo_status, IbexUartState),
503a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_ovrd, IbexUartState),
504a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_val, IbexUartState),
505a7d2d98cSAlistair Francis         VMSTATE_UINT32(uart_timeout_ctrl, IbexUartState),
506a7d2d98cSAlistair Francis         VMSTATE_END_OF_LIST()
507a7d2d98cSAlistair Francis     }
508a7d2d98cSAlistair Francis };
509a7d2d98cSAlistair Francis 
510a7d2d98cSAlistair Francis static Property ibex_uart_properties[] = {
511a7d2d98cSAlistair Francis     DEFINE_PROP_CHR("chardev", IbexUartState, chr),
512a7d2d98cSAlistair Francis     DEFINE_PROP_END_OF_LIST(),
513a7d2d98cSAlistair Francis };
514a7d2d98cSAlistair Francis 
515a7d2d98cSAlistair Francis static void ibex_uart_init(Object *obj)
516a7d2d98cSAlistair Francis {
517a7d2d98cSAlistair Francis     IbexUartState *s = IBEX_UART(obj);
518a7d2d98cSAlistair Francis 
519940aabb9SAlistair Francis     s->f_clk = qdev_init_clock_in(DEVICE(obj), "f_clock",
5205ee0abedSPeter Maydell                                   ibex_uart_clk_update, s, ClockUpdate);
521940aabb9SAlistair Francis     clock_set_hz(s->f_clk, IBEX_UART_CLOCK);
522940aabb9SAlistair Francis 
523a7d2d98cSAlistair Francis     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_watermark);
524a7d2d98cSAlistair Francis     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_watermark);
525a7d2d98cSAlistair Francis     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_empty);
526a7d2d98cSAlistair Francis     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_overflow);
527a7d2d98cSAlistair Francis 
528a7d2d98cSAlistair Francis     memory_region_init_io(&s->mmio, obj, &ibex_uart_ops, s,
529a7d2d98cSAlistair Francis                           TYPE_IBEX_UART, 0x400);
530a7d2d98cSAlistair Francis     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
531a7d2d98cSAlistair Francis }
532a7d2d98cSAlistair Francis 
533a7d2d98cSAlistair Francis static void ibex_uart_realize(DeviceState *dev, Error **errp)
534a7d2d98cSAlistair Francis {
535a7d2d98cSAlistair Francis     IbexUartState *s = IBEX_UART(dev);
536a7d2d98cSAlistair Francis 
537a7d2d98cSAlistair Francis     s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
538a7d2d98cSAlistair Francis                                           fifo_trigger_update, s);
539a7d2d98cSAlistair Francis 
540a7d2d98cSAlistair Francis     qemu_chr_fe_set_handlers(&s->chr, ibex_uart_can_receive,
541a7d2d98cSAlistair Francis                              ibex_uart_receive, NULL, NULL,
542a7d2d98cSAlistair Francis                              s, NULL, true);
543a7d2d98cSAlistair Francis }
544a7d2d98cSAlistair Francis 
545a7d2d98cSAlistair Francis static void ibex_uart_class_init(ObjectClass *klass, void *data)
546a7d2d98cSAlistair Francis {
547a7d2d98cSAlistair Francis     DeviceClass *dc = DEVICE_CLASS(klass);
548a7d2d98cSAlistair Francis 
549a7d2d98cSAlistair Francis     dc->reset = ibex_uart_reset;
550a7d2d98cSAlistair Francis     dc->realize = ibex_uart_realize;
551a7d2d98cSAlistair Francis     dc->vmsd = &vmstate_ibex_uart;
552a7d2d98cSAlistair Francis     device_class_set_props(dc, ibex_uart_properties);
553a7d2d98cSAlistair Francis }
554a7d2d98cSAlistair Francis 
555a7d2d98cSAlistair Francis static const TypeInfo ibex_uart_info = {
556a7d2d98cSAlistair Francis     .name          = TYPE_IBEX_UART,
557a7d2d98cSAlistair Francis     .parent        = TYPE_SYS_BUS_DEVICE,
558a7d2d98cSAlistair Francis     .instance_size = sizeof(IbexUartState),
559a7d2d98cSAlistair Francis     .instance_init = ibex_uart_init,
560a7d2d98cSAlistair Francis     .class_init    = ibex_uart_class_init,
561a7d2d98cSAlistair Francis };
562a7d2d98cSAlistair Francis 
563a7d2d98cSAlistair Francis static void ibex_uart_register_types(void)
564a7d2d98cSAlistair Francis {
565a7d2d98cSAlistair Francis     type_register_static(&ibex_uart_info);
566a7d2d98cSAlistair Francis }
567a7d2d98cSAlistair Francis 
568a7d2d98cSAlistair Francis type_init(ibex_uart_register_types)
569