]>
Commit | Line | Data |
---|---|---|
a7d2d98c AF |
1 | /* |
2 | * QEMU lowRISC Ibex UART device | |
3 | * | |
4 | * Copyright (c) 2020 Western Digital | |
5 | * | |
6 | * For details check the documentation here: | |
7 | * https://docs.opentitan.org/hw/ip/uart/doc/ | |
8 | * | |
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
10 | * of this software and associated documentation files (the "Software"), to deal | |
11 | * in the Software without restriction, including without limitation the rights | |
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
13 | * copies of the Software, and to permit persons to whom the Software is | |
14 | * furnished to do so, subject to the following conditions: | |
15 | * | |
16 | * The above copyright notice and this permission notice shall be included in | |
17 | * all copies or substantial portions of the Software. | |
18 | * | |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
22 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
25 | * THE SOFTWARE. | |
26 | */ | |
27 | ||
28 | #include "qemu/osdep.h" | |
29 | #include "hw/char/ibex_uart.h" | |
30 | #include "hw/irq.h" | |
940aabb9 | 31 | #include "hw/qdev-clock.h" |
a7d2d98c | 32 | #include "hw/qdev-properties.h" |
ce35e229 | 33 | #include "hw/qdev-properties-system.h" |
8c6631e6 | 34 | #include "hw/registerfields.h" |
a7d2d98c AF |
35 | #include "migration/vmstate.h" |
36 | #include "qemu/log.h" | |
37 | #include "qemu/module.h" | |
38 | ||
bdc36ce6 AF |
39 | REG32(INTR_STATE, 0x00) |
40 | FIELD(INTR_STATE, TX_WATERMARK, 0, 1) | |
41 | FIELD(INTR_STATE, RX_WATERMARK, 1, 1) | |
42 | FIELD(INTR_STATE, TX_EMPTY, 2, 1) | |
43 | FIELD(INTR_STATE, RX_OVERFLOW, 3, 1) | |
44 | REG32(INTR_ENABLE, 0x04) | |
45 | REG32(INTR_TEST, 0x08) | |
24bfb98d AF |
46 | REG32(ALERT_TEST, 0x0C) |
47 | REG32(CTRL, 0x10) | |
bdc36ce6 AF |
48 | FIELD(CTRL, TX_ENABLE, 0, 1) |
49 | FIELD(CTRL, RX_ENABLE, 1, 1) | |
50 | FIELD(CTRL, NF, 2, 1) | |
51 | FIELD(CTRL, SLPBK, 4, 1) | |
52 | FIELD(CTRL, LLPBK, 5, 1) | |
53 | FIELD(CTRL, PARITY_EN, 6, 1) | |
54 | FIELD(CTRL, PARITY_ODD, 7, 1) | |
55 | FIELD(CTRL, RXBLVL, 8, 2) | |
56 | FIELD(CTRL, NCO, 16, 16) | |
24bfb98d | 57 | REG32(STATUS, 0x14) |
bdc36ce6 AF |
58 | FIELD(STATUS, TXFULL, 0, 1) |
59 | FIELD(STATUS, RXFULL, 1, 1) | |
60 | FIELD(STATUS, TXEMPTY, 2, 1) | |
61 | FIELD(STATUS, RXIDLE, 4, 1) | |
62 | FIELD(STATUS, RXEMPTY, 5, 1) | |
24bfb98d AF |
63 | REG32(RDATA, 0x18) |
64 | REG32(WDATA, 0x1C) | |
65 | REG32(FIFO_CTRL, 0x20) | |
bdc36ce6 AF |
66 | FIELD(FIFO_CTRL, RXRST, 0, 1) |
67 | FIELD(FIFO_CTRL, TXRST, 1, 1) | |
68 | FIELD(FIFO_CTRL, RXILVL, 2, 3) | |
69 | FIELD(FIFO_CTRL, TXILVL, 5, 2) | |
24bfb98d | 70 | REG32(FIFO_STATUS, 0x24) |
bdc36ce6 AF |
71 | FIELD(FIFO_STATUS, TXLVL, 0, 5) |
72 | FIELD(FIFO_STATUS, RXLVL, 16, 5) | |
24bfb98d AF |
73 | REG32(OVRD, 0x28) |
74 | REG32(VAL, 0x2C) | |
75 | REG32(TIMEOUT_CTRL, 0x30) | |
bdc36ce6 | 76 | |
a7d2d98c AF |
77 | static void ibex_uart_update_irqs(IbexUartState *s) |
78 | { | |
59093cc4 | 79 | if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_WATERMARK_MASK) { |
a7d2d98c AF |
80 | qemu_set_irq(s->tx_watermark, 1); |
81 | } else { | |
82 | qemu_set_irq(s->tx_watermark, 0); | |
83 | } | |
84 | ||
59093cc4 | 85 | if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_WATERMARK_MASK) { |
a7d2d98c AF |
86 | qemu_set_irq(s->rx_watermark, 1); |
87 | } else { | |
88 | qemu_set_irq(s->rx_watermark, 0); | |
89 | } | |
90 | ||
59093cc4 | 91 | if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_EMPTY_MASK) { |
a7d2d98c AF |
92 | qemu_set_irq(s->tx_empty, 1); |
93 | } else { | |
94 | qemu_set_irq(s->tx_empty, 0); | |
95 | } | |
96 | ||
59093cc4 | 97 | if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_OVERFLOW_MASK) { |
a7d2d98c AF |
98 | qemu_set_irq(s->rx_overflow, 1); |
99 | } else { | |
100 | qemu_set_irq(s->rx_overflow, 0); | |
101 | } | |
102 | } | |
103 | ||
104 | static int ibex_uart_can_receive(void *opaque) | |
105 | { | |
106 | IbexUartState *s = opaque; | |
107 | ||
82a4ed8e AW |
108 | if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) |
109 | && !(s->uart_status & R_STATUS_RXFULL_MASK)) { | |
a7d2d98c AF |
110 | return 1; |
111 | } | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
116 | static void ibex_uart_receive(void *opaque, const uint8_t *buf, int size) | |
117 | { | |
118 | IbexUartState *s = opaque; | |
59093cc4 AF |
119 | uint8_t rx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_RXILVL_MASK) |
120 | >> R_FIFO_CTRL_RXILVL_SHIFT; | |
a7d2d98c AF |
121 | |
122 | s->uart_rdata = *buf; | |
123 | ||
59093cc4 AF |
124 | s->uart_status &= ~R_STATUS_RXIDLE_MASK; |
125 | s->uart_status &= ~R_STATUS_RXEMPTY_MASK; | |
82a4ed8e AW |
126 | /* The RXFULL is set after receiving a single byte |
127 | * as the FIFO buffers are not yet implemented. | |
128 | */ | |
129 | s->uart_status |= R_STATUS_RXFULL_MASK; | |
130 | s->rx_level += 1; | |
a7d2d98c AF |
131 | |
132 | if (size > rx_fifo_level) { | |
59093cc4 | 133 | s->uart_intr_state |= R_INTR_STATE_RX_WATERMARK_MASK; |
a7d2d98c AF |
134 | } |
135 | ||
136 | ibex_uart_update_irqs(s); | |
137 | } | |
138 | ||
bf7b1eab | 139 | static gboolean ibex_uart_xmit(void *do_not_use, GIOCondition cond, |
a7d2d98c AF |
140 | void *opaque) |
141 | { | |
142 | IbexUartState *s = opaque; | |
59093cc4 AF |
143 | uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK) |
144 | >> R_FIFO_CTRL_TXILVL_SHIFT; | |
a7d2d98c AF |
145 | int ret; |
146 | ||
147 | /* instant drain the fifo when there's no back-end */ | |
148 | if (!qemu_chr_fe_backend_connected(&s->chr)) { | |
149 | s->tx_level = 0; | |
150 | return FALSE; | |
151 | } | |
152 | ||
153 | if (!s->tx_level) { | |
59093cc4 AF |
154 | s->uart_status &= ~R_STATUS_TXFULL_MASK; |
155 | s->uart_status |= R_STATUS_TXEMPTY_MASK; | |
156 | s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK; | |
157 | s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK; | |
a7d2d98c AF |
158 | ibex_uart_update_irqs(s); |
159 | return FALSE; | |
160 | } | |
161 | ||
162 | ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level); | |
163 | ||
164 | if (ret >= 0) { | |
165 | s->tx_level -= ret; | |
166 | memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_level); | |
167 | } | |
168 | ||
169 | if (s->tx_level) { | |
170 | guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, | |
171 | ibex_uart_xmit, s); | |
172 | if (!r) { | |
173 | s->tx_level = 0; | |
174 | return FALSE; | |
175 | } | |
176 | } | |
177 | ||
178 | /* Clear the TX Full bit */ | |
179 | if (s->tx_level != IBEX_UART_TX_FIFO_SIZE) { | |
59093cc4 | 180 | s->uart_status &= ~R_STATUS_TXFULL_MASK; |
a7d2d98c AF |
181 | } |
182 | ||
183 | /* Disable the TX_WATERMARK IRQ */ | |
184 | if (s->tx_level < tx_fifo_level) { | |
59093cc4 | 185 | s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK; |
a7d2d98c AF |
186 | } |
187 | ||
188 | /* Set TX empty */ | |
189 | if (s->tx_level == 0) { | |
59093cc4 AF |
190 | s->uart_status |= R_STATUS_TXEMPTY_MASK; |
191 | s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK; | |
a7d2d98c AF |
192 | } |
193 | ||
194 | ibex_uart_update_irqs(s); | |
195 | return FALSE; | |
196 | } | |
197 | ||
198 | static void uart_write_tx_fifo(IbexUartState *s, const uint8_t *buf, | |
199 | int size) | |
200 | { | |
201 | uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | |
59093cc4 AF |
202 | uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK) |
203 | >> R_FIFO_CTRL_TXILVL_SHIFT; | |
a7d2d98c AF |
204 | |
205 | if (size > IBEX_UART_TX_FIFO_SIZE - s->tx_level) { | |
206 | size = IBEX_UART_TX_FIFO_SIZE - s->tx_level; | |
207 | qemu_log_mask(LOG_GUEST_ERROR, "ibex_uart: TX FIFO overflow"); | |
208 | } | |
209 | ||
210 | memcpy(s->tx_fifo + s->tx_level, buf, size); | |
211 | s->tx_level += size; | |
212 | ||
213 | if (s->tx_level > 0) { | |
59093cc4 | 214 | s->uart_status &= ~R_STATUS_TXEMPTY_MASK; |
a7d2d98c AF |
215 | } |
216 | ||
217 | if (s->tx_level >= tx_fifo_level) { | |
59093cc4 | 218 | s->uart_intr_state |= R_INTR_STATE_TX_WATERMARK_MASK; |
a7d2d98c AF |
219 | ibex_uart_update_irqs(s); |
220 | } | |
221 | ||
222 | if (s->tx_level == IBEX_UART_TX_FIFO_SIZE) { | |
59093cc4 | 223 | s->uart_status |= R_STATUS_TXFULL_MASK; |
a7d2d98c AF |
224 | } |
225 | ||
226 | timer_mod(s->fifo_trigger_handle, current_time + | |
227 | (s->char_tx_time * 4)); | |
228 | } | |
229 | ||
230 | static void ibex_uart_reset(DeviceState *dev) | |
231 | { | |
232 | IbexUartState *s = IBEX_UART(dev); | |
233 | ||
234 | s->uart_intr_state = 0x00000000; | |
235 | s->uart_intr_state = 0x00000000; | |
236 | s->uart_intr_enable = 0x00000000; | |
237 | s->uart_ctrl = 0x00000000; | |
238 | s->uart_status = 0x0000003c; | |
239 | s->uart_rdata = 0x00000000; | |
240 | s->uart_fifo_ctrl = 0x00000000; | |
241 | s->uart_fifo_status = 0x00000000; | |
242 | s->uart_ovrd = 0x00000000; | |
243 | s->uart_val = 0x00000000; | |
244 | s->uart_timeout_ctrl = 0x00000000; | |
245 | ||
246 | s->tx_level = 0; | |
82a4ed8e | 247 | s->rx_level = 0; |
a7d2d98c AF |
248 | |
249 | s->char_tx_time = (NANOSECONDS_PER_SECOND / 230400) * 10; | |
250 | ||
251 | ibex_uart_update_irqs(s); | |
252 | } | |
253 | ||
940aabb9 AF |
254 | static uint64_t ibex_uart_get_baud(IbexUartState *s) |
255 | { | |
256 | uint64_t baud; | |
257 | ||
59093cc4 | 258 | baud = ((s->uart_ctrl & R_CTRL_NCO_MASK) >> 16); |
940aabb9 AF |
259 | baud *= clock_get_hz(s->f_clk); |
260 | baud >>= 20; | |
261 | ||
262 | return baud; | |
263 | } | |
264 | ||
a7d2d98c AF |
265 | static uint64_t ibex_uart_read(void *opaque, hwaddr addr, |
266 | unsigned int size) | |
267 | { | |
268 | IbexUartState *s = opaque; | |
269 | uint64_t retvalue = 0; | |
270 | ||
59093cc4 AF |
271 | switch (addr >> 2) { |
272 | case R_INTR_STATE: | |
a7d2d98c AF |
273 | retvalue = s->uart_intr_state; |
274 | break; | |
59093cc4 | 275 | case R_INTR_ENABLE: |
a7d2d98c AF |
276 | retvalue = s->uart_intr_enable; |
277 | break; | |
59093cc4 | 278 | case R_INTR_TEST: |
a7d2d98c AF |
279 | qemu_log_mask(LOG_GUEST_ERROR, |
280 | "%s: wdata is write only\n", __func__); | |
281 | break; | |
282 | ||
59093cc4 | 283 | case R_CTRL: |
a7d2d98c AF |
284 | retvalue = s->uart_ctrl; |
285 | break; | |
59093cc4 | 286 | case R_STATUS: |
a7d2d98c AF |
287 | retvalue = s->uart_status; |
288 | break; | |
289 | ||
59093cc4 | 290 | case R_RDATA: |
a7d2d98c | 291 | retvalue = s->uart_rdata; |
82a4ed8e | 292 | if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) && (s->rx_level > 0)) { |
a7d2d98c AF |
293 | qemu_chr_fe_accept_input(&s->chr); |
294 | ||
82a4ed8e AW |
295 | s->rx_level -= 1; |
296 | s->uart_status &= ~R_STATUS_RXFULL_MASK; | |
297 | if (s->rx_level == 0) { | |
298 | s->uart_status |= R_STATUS_RXIDLE_MASK; | |
299 | s->uart_status |= R_STATUS_RXEMPTY_MASK; | |
300 | } | |
a7d2d98c AF |
301 | } |
302 | break; | |
59093cc4 | 303 | case R_WDATA: |
a7d2d98c AF |
304 | qemu_log_mask(LOG_GUEST_ERROR, |
305 | "%s: wdata is write only\n", __func__); | |
306 | break; | |
307 | ||
59093cc4 | 308 | case R_FIFO_CTRL: |
a7d2d98c AF |
309 | retvalue = s->uart_fifo_ctrl; |
310 | break; | |
59093cc4 | 311 | case R_FIFO_STATUS: |
a7d2d98c AF |
312 | retvalue = s->uart_fifo_status; |
313 | ||
82a4ed8e AW |
314 | retvalue |= (s->rx_level & 0x1F) << R_FIFO_STATUS_RXLVL_SHIFT; |
315 | retvalue |= (s->tx_level & 0x1F) << R_FIFO_STATUS_TXLVL_SHIFT; | |
a7d2d98c AF |
316 | |
317 | qemu_log_mask(LOG_UNIMP, | |
318 | "%s: RX fifos are not supported\n", __func__); | |
319 | break; | |
320 | ||
59093cc4 | 321 | case R_OVRD: |
a7d2d98c AF |
322 | retvalue = s->uart_ovrd; |
323 | qemu_log_mask(LOG_UNIMP, | |
324 | "%s: ovrd is not supported\n", __func__); | |
325 | break; | |
59093cc4 | 326 | case R_VAL: |
a7d2d98c AF |
327 | retvalue = s->uart_val; |
328 | qemu_log_mask(LOG_UNIMP, | |
329 | "%s: val is not supported\n", __func__); | |
330 | break; | |
59093cc4 | 331 | case R_TIMEOUT_CTRL: |
a7d2d98c AF |
332 | retvalue = s->uart_timeout_ctrl; |
333 | qemu_log_mask(LOG_UNIMP, | |
334 | "%s: timeout_ctrl is not supported\n", __func__); | |
335 | break; | |
336 | default: | |
337 | qemu_log_mask(LOG_GUEST_ERROR, | |
338 | "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); | |
339 | return 0; | |
340 | } | |
341 | ||
342 | return retvalue; | |
343 | } | |
344 | ||
345 | static void ibex_uart_write(void *opaque, hwaddr addr, | |
346 | uint64_t val64, unsigned int size) | |
347 | { | |
348 | IbexUartState *s = opaque; | |
349 | uint32_t value = val64; | |
350 | ||
59093cc4 AF |
351 | switch (addr >> 2) { |
352 | case R_INTR_STATE: | |
a7d2d98c AF |
353 | /* Write 1 clear */ |
354 | s->uart_intr_state &= ~value; | |
355 | ibex_uart_update_irqs(s); | |
356 | break; | |
59093cc4 | 357 | case R_INTR_ENABLE: |
a7d2d98c AF |
358 | s->uart_intr_enable = value; |
359 | ibex_uart_update_irqs(s); | |
360 | break; | |
59093cc4 | 361 | case R_INTR_TEST: |
a7d2d98c AF |
362 | s->uart_intr_state |= value; |
363 | ibex_uart_update_irqs(s); | |
364 | break; | |
365 | ||
59093cc4 | 366 | case R_CTRL: |
a7d2d98c AF |
367 | s->uart_ctrl = value; |
368 | ||
59093cc4 | 369 | if (value & R_CTRL_NF_MASK) { |
a7d2d98c AF |
370 | qemu_log_mask(LOG_UNIMP, |
371 | "%s: UART_CTRL_NF is not supported\n", __func__); | |
372 | } | |
59093cc4 | 373 | if (value & R_CTRL_SLPBK_MASK) { |
a7d2d98c AF |
374 | qemu_log_mask(LOG_UNIMP, |
375 | "%s: UART_CTRL_SLPBK is not supported\n", __func__); | |
376 | } | |
59093cc4 | 377 | if (value & R_CTRL_LLPBK_MASK) { |
a7d2d98c AF |
378 | qemu_log_mask(LOG_UNIMP, |
379 | "%s: UART_CTRL_LLPBK is not supported\n", __func__); | |
380 | } | |
59093cc4 | 381 | if (value & R_CTRL_PARITY_EN_MASK) { |
a7d2d98c AF |
382 | qemu_log_mask(LOG_UNIMP, |
383 | "%s: UART_CTRL_PARITY_EN is not supported\n", | |
384 | __func__); | |
385 | } | |
59093cc4 | 386 | if (value & R_CTRL_PARITY_ODD_MASK) { |
a7d2d98c AF |
387 | qemu_log_mask(LOG_UNIMP, |
388 | "%s: UART_CTRL_PARITY_ODD is not supported\n", | |
389 | __func__); | |
390 | } | |
59093cc4 | 391 | if (value & R_CTRL_RXBLVL_MASK) { |
a7d2d98c AF |
392 | qemu_log_mask(LOG_UNIMP, |
393 | "%s: UART_CTRL_RXBLVL is not supported\n", __func__); | |
394 | } | |
59093cc4 | 395 | if (value & R_CTRL_NCO_MASK) { |
940aabb9 | 396 | uint64_t baud = ibex_uart_get_baud(s); |
a7d2d98c AF |
397 | |
398 | s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10; | |
399 | } | |
400 | break; | |
59093cc4 | 401 | case R_STATUS: |
a7d2d98c AF |
402 | qemu_log_mask(LOG_GUEST_ERROR, |
403 | "%s: status is read only\n", __func__); | |
404 | break; | |
405 | ||
59093cc4 | 406 | case R_RDATA: |
a7d2d98c AF |
407 | qemu_log_mask(LOG_GUEST_ERROR, |
408 | "%s: rdata is read only\n", __func__); | |
409 | break; | |
59093cc4 | 410 | case R_WDATA: |
a7d2d98c AF |
411 | uart_write_tx_fifo(s, (uint8_t *) &value, 1); |
412 | break; | |
413 | ||
59093cc4 | 414 | case R_FIFO_CTRL: |
a7d2d98c AF |
415 | s->uart_fifo_ctrl = value; |
416 | ||
59093cc4 | 417 | if (value & R_FIFO_CTRL_RXRST_MASK) { |
82a4ed8e | 418 | s->rx_level = 0; |
a7d2d98c AF |
419 | qemu_log_mask(LOG_UNIMP, |
420 | "%s: RX fifos are not supported\n", __func__); | |
421 | } | |
59093cc4 | 422 | if (value & R_FIFO_CTRL_TXRST_MASK) { |
a7d2d98c AF |
423 | s->tx_level = 0; |
424 | } | |
425 | break; | |
59093cc4 | 426 | case R_FIFO_STATUS: |
a7d2d98c AF |
427 | qemu_log_mask(LOG_GUEST_ERROR, |
428 | "%s: fifo_status is read only\n", __func__); | |
429 | break; | |
430 | ||
59093cc4 | 431 | case R_OVRD: |
a7d2d98c AF |
432 | s->uart_ovrd = value; |
433 | qemu_log_mask(LOG_UNIMP, | |
434 | "%s: ovrd is not supported\n", __func__); | |
435 | break; | |
59093cc4 | 436 | case R_VAL: |
a7d2d98c AF |
437 | qemu_log_mask(LOG_GUEST_ERROR, |
438 | "%s: val is read only\n", __func__); | |
439 | break; | |
59093cc4 | 440 | case R_TIMEOUT_CTRL: |
a7d2d98c AF |
441 | s->uart_timeout_ctrl = value; |
442 | qemu_log_mask(LOG_UNIMP, | |
443 | "%s: timeout_ctrl is not supported\n", __func__); | |
444 | break; | |
445 | default: | |
446 | qemu_log_mask(LOG_GUEST_ERROR, | |
447 | "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); | |
448 | } | |
449 | } | |
450 | ||
5ee0abed | 451 | static void ibex_uart_clk_update(void *opaque, ClockEvent event) |
940aabb9 AF |
452 | { |
453 | IbexUartState *s = opaque; | |
454 | ||
455 | /* recompute uart's speed on clock change */ | |
456 | uint64_t baud = ibex_uart_get_baud(s); | |
457 | ||
458 | s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10; | |
459 | } | |
460 | ||
a7d2d98c AF |
461 | static void fifo_trigger_update(void *opaque) |
462 | { | |
463 | IbexUartState *s = opaque; | |
464 | ||
59093cc4 | 465 | if (s->uart_ctrl & R_CTRL_TX_ENABLE_MASK) { |
a7d2d98c AF |
466 | ibex_uart_xmit(NULL, G_IO_OUT, s); |
467 | } | |
468 | } | |
469 | ||
470 | static const MemoryRegionOps ibex_uart_ops = { | |
471 | .read = ibex_uart_read, | |
472 | .write = ibex_uart_write, | |
473 | .endianness = DEVICE_NATIVE_ENDIAN, | |
474 | .impl.min_access_size = 4, | |
475 | .impl.max_access_size = 4, | |
476 | }; | |
477 | ||
478 | static int ibex_uart_post_load(void *opaque, int version_id) | |
479 | { | |
480 | IbexUartState *s = opaque; | |
481 | ||
482 | ibex_uart_update_irqs(s); | |
483 | return 0; | |
484 | } | |
485 | ||
486 | static const VMStateDescription vmstate_ibex_uart = { | |
487 | .name = TYPE_IBEX_UART, | |
488 | .version_id = 1, | |
489 | .minimum_version_id = 1, | |
490 | .post_load = ibex_uart_post_load, | |
491 | .fields = (VMStateField[]) { | |
492 | VMSTATE_UINT8_ARRAY(tx_fifo, IbexUartState, | |
493 | IBEX_UART_TX_FIFO_SIZE), | |
494 | VMSTATE_UINT32(tx_level, IbexUartState), | |
495 | VMSTATE_UINT64(char_tx_time, IbexUartState), | |
496 | VMSTATE_TIMER_PTR(fifo_trigger_handle, IbexUartState), | |
497 | VMSTATE_UINT32(uart_intr_state, IbexUartState), | |
498 | VMSTATE_UINT32(uart_intr_enable, IbexUartState), | |
499 | VMSTATE_UINT32(uart_ctrl, IbexUartState), | |
500 | VMSTATE_UINT32(uart_status, IbexUartState), | |
501 | VMSTATE_UINT32(uart_rdata, IbexUartState), | |
502 | VMSTATE_UINT32(uart_fifo_ctrl, IbexUartState), | |
503 | VMSTATE_UINT32(uart_fifo_status, IbexUartState), | |
504 | VMSTATE_UINT32(uart_ovrd, IbexUartState), | |
505 | VMSTATE_UINT32(uart_val, IbexUartState), | |
506 | VMSTATE_UINT32(uart_timeout_ctrl, IbexUartState), | |
507 | VMSTATE_END_OF_LIST() | |
508 | } | |
509 | }; | |
510 | ||
511 | static Property ibex_uart_properties[] = { | |
512 | DEFINE_PROP_CHR("chardev", IbexUartState, chr), | |
513 | DEFINE_PROP_END_OF_LIST(), | |
514 | }; | |
515 | ||
516 | static void ibex_uart_init(Object *obj) | |
517 | { | |
518 | IbexUartState *s = IBEX_UART(obj); | |
519 | ||
940aabb9 | 520 | s->f_clk = qdev_init_clock_in(DEVICE(obj), "f_clock", |
5ee0abed | 521 | ibex_uart_clk_update, s, ClockUpdate); |
940aabb9 AF |
522 | clock_set_hz(s->f_clk, IBEX_UART_CLOCK); |
523 | ||
a7d2d98c AF |
524 | sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_watermark); |
525 | sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_watermark); | |
526 | sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_empty); | |
527 | sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_overflow); | |
528 | ||
529 | memory_region_init_io(&s->mmio, obj, &ibex_uart_ops, s, | |
530 | TYPE_IBEX_UART, 0x400); | |
531 | sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); | |
532 | } | |
533 | ||
534 | static void ibex_uart_realize(DeviceState *dev, Error **errp) | |
535 | { | |
536 | IbexUartState *s = IBEX_UART(dev); | |
537 | ||
538 | s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL, | |
539 | fifo_trigger_update, s); | |
540 | ||
541 | qemu_chr_fe_set_handlers(&s->chr, ibex_uart_can_receive, | |
542 | ibex_uart_receive, NULL, NULL, | |
543 | s, NULL, true); | |
544 | } | |
545 | ||
546 | static void ibex_uart_class_init(ObjectClass *klass, void *data) | |
547 | { | |
548 | DeviceClass *dc = DEVICE_CLASS(klass); | |
549 | ||
550 | dc->reset = ibex_uart_reset; | |
551 | dc->realize = ibex_uart_realize; | |
552 | dc->vmsd = &vmstate_ibex_uart; | |
553 | device_class_set_props(dc, ibex_uart_properties); | |
34229c46 | 554 | set_bit(DEVICE_CATEGORY_INPUT, dc->categories); |
a7d2d98c AF |
555 | } |
556 | ||
557 | static const TypeInfo ibex_uart_info = { | |
558 | .name = TYPE_IBEX_UART, | |
559 | .parent = TYPE_SYS_BUS_DEVICE, | |
560 | .instance_size = sizeof(IbexUartState), | |
561 | .instance_init = ibex_uart_init, | |
562 | .class_init = ibex_uart_class_init, | |
563 | }; | |
564 | ||
565 | static void ibex_uart_register_types(void) | |
566 | { | |
567 | type_register_static(&ibex_uart_info); | |
568 | } | |
569 | ||
570 | type_init(ibex_uart_register_types) |