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