]>
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" |
a7d2d98c AF |
34 | #include "migration/vmstate.h" |
35 | #include "qemu/log.h" | |
36 | #include "qemu/module.h" | |
37 | ||
38 | static void ibex_uart_update_irqs(IbexUartState *s) | |
39 | { | |
59093cc4 | 40 | if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_WATERMARK_MASK) { |
a7d2d98c AF |
41 | qemu_set_irq(s->tx_watermark, 1); |
42 | } else { | |
43 | qemu_set_irq(s->tx_watermark, 0); | |
44 | } | |
45 | ||
59093cc4 | 46 | if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_WATERMARK_MASK) { |
a7d2d98c AF |
47 | qemu_set_irq(s->rx_watermark, 1); |
48 | } else { | |
49 | qemu_set_irq(s->rx_watermark, 0); | |
50 | } | |
51 | ||
59093cc4 | 52 | if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_EMPTY_MASK) { |
a7d2d98c AF |
53 | qemu_set_irq(s->tx_empty, 1); |
54 | } else { | |
55 | qemu_set_irq(s->tx_empty, 0); | |
56 | } | |
57 | ||
59093cc4 | 58 | if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_OVERFLOW_MASK) { |
a7d2d98c AF |
59 | qemu_set_irq(s->rx_overflow, 1); |
60 | } else { | |
61 | qemu_set_irq(s->rx_overflow, 0); | |
62 | } | |
63 | } | |
64 | ||
65 | static int ibex_uart_can_receive(void *opaque) | |
66 | { | |
67 | IbexUartState *s = opaque; | |
68 | ||
82a4ed8e AW |
69 | if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) |
70 | && !(s->uart_status & R_STATUS_RXFULL_MASK)) { | |
a7d2d98c AF |
71 | return 1; |
72 | } | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
77 | static void ibex_uart_receive(void *opaque, const uint8_t *buf, int size) | |
78 | { | |
79 | IbexUartState *s = opaque; | |
59093cc4 AF |
80 | uint8_t rx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_RXILVL_MASK) |
81 | >> R_FIFO_CTRL_RXILVL_SHIFT; | |
a7d2d98c AF |
82 | |
83 | s->uart_rdata = *buf; | |
84 | ||
59093cc4 AF |
85 | s->uart_status &= ~R_STATUS_RXIDLE_MASK; |
86 | s->uart_status &= ~R_STATUS_RXEMPTY_MASK; | |
82a4ed8e AW |
87 | /* The RXFULL is set after receiving a single byte |
88 | * as the FIFO buffers are not yet implemented. | |
89 | */ | |
90 | s->uart_status |= R_STATUS_RXFULL_MASK; | |
91 | s->rx_level += 1; | |
a7d2d98c AF |
92 | |
93 | if (size > rx_fifo_level) { | |
59093cc4 | 94 | s->uart_intr_state |= R_INTR_STATE_RX_WATERMARK_MASK; |
a7d2d98c AF |
95 | } |
96 | ||
97 | ibex_uart_update_irqs(s); | |
98 | } | |
99 | ||
100 | static gboolean ibex_uart_xmit(GIOChannel *chan, GIOCondition cond, | |
101 | void *opaque) | |
102 | { | |
103 | IbexUartState *s = opaque; | |
59093cc4 AF |
104 | uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK) |
105 | >> R_FIFO_CTRL_TXILVL_SHIFT; | |
a7d2d98c AF |
106 | int ret; |
107 | ||
108 | /* instant drain the fifo when there's no back-end */ | |
109 | if (!qemu_chr_fe_backend_connected(&s->chr)) { | |
110 | s->tx_level = 0; | |
111 | return FALSE; | |
112 | } | |
113 | ||
114 | if (!s->tx_level) { | |
59093cc4 AF |
115 | s->uart_status &= ~R_STATUS_TXFULL_MASK; |
116 | s->uart_status |= R_STATUS_TXEMPTY_MASK; | |
117 | s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK; | |
118 | s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK; | |
a7d2d98c AF |
119 | ibex_uart_update_irqs(s); |
120 | return FALSE; | |
121 | } | |
122 | ||
123 | ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level); | |
124 | ||
125 | if (ret >= 0) { | |
126 | s->tx_level -= ret; | |
127 | memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_level); | |
128 | } | |
129 | ||
130 | if (s->tx_level) { | |
131 | guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, | |
132 | ibex_uart_xmit, s); | |
133 | if (!r) { | |
134 | s->tx_level = 0; | |
135 | return FALSE; | |
136 | } | |
137 | } | |
138 | ||
139 | /* Clear the TX Full bit */ | |
140 | if (s->tx_level != IBEX_UART_TX_FIFO_SIZE) { | |
59093cc4 | 141 | s->uart_status &= ~R_STATUS_TXFULL_MASK; |
a7d2d98c AF |
142 | } |
143 | ||
144 | /* Disable the TX_WATERMARK IRQ */ | |
145 | if (s->tx_level < tx_fifo_level) { | |
59093cc4 | 146 | s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK; |
a7d2d98c AF |
147 | } |
148 | ||
149 | /* Set TX empty */ | |
150 | if (s->tx_level == 0) { | |
59093cc4 AF |
151 | s->uart_status |= R_STATUS_TXEMPTY_MASK; |
152 | s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK; | |
a7d2d98c AF |
153 | } |
154 | ||
155 | ibex_uart_update_irqs(s); | |
156 | return FALSE; | |
157 | } | |
158 | ||
159 | static void uart_write_tx_fifo(IbexUartState *s, const uint8_t *buf, | |
160 | int size) | |
161 | { | |
162 | uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | |
59093cc4 AF |
163 | uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK) |
164 | >> R_FIFO_CTRL_TXILVL_SHIFT; | |
a7d2d98c AF |
165 | |
166 | if (size > IBEX_UART_TX_FIFO_SIZE - s->tx_level) { | |
167 | size = IBEX_UART_TX_FIFO_SIZE - s->tx_level; | |
168 | qemu_log_mask(LOG_GUEST_ERROR, "ibex_uart: TX FIFO overflow"); | |
169 | } | |
170 | ||
171 | memcpy(s->tx_fifo + s->tx_level, buf, size); | |
172 | s->tx_level += size; | |
173 | ||
174 | if (s->tx_level > 0) { | |
59093cc4 | 175 | s->uart_status &= ~R_STATUS_TXEMPTY_MASK; |
a7d2d98c AF |
176 | } |
177 | ||
178 | if (s->tx_level >= tx_fifo_level) { | |
59093cc4 | 179 | s->uart_intr_state |= R_INTR_STATE_TX_WATERMARK_MASK; |
a7d2d98c AF |
180 | ibex_uart_update_irqs(s); |
181 | } | |
182 | ||
183 | if (s->tx_level == IBEX_UART_TX_FIFO_SIZE) { | |
59093cc4 | 184 | s->uart_status |= R_STATUS_TXFULL_MASK; |
a7d2d98c AF |
185 | } |
186 | ||
187 | timer_mod(s->fifo_trigger_handle, current_time + | |
188 | (s->char_tx_time * 4)); | |
189 | } | |
190 | ||
191 | static void ibex_uart_reset(DeviceState *dev) | |
192 | { | |
193 | IbexUartState *s = IBEX_UART(dev); | |
194 | ||
195 | s->uart_intr_state = 0x00000000; | |
196 | s->uart_intr_state = 0x00000000; | |
197 | s->uart_intr_enable = 0x00000000; | |
198 | s->uart_ctrl = 0x00000000; | |
199 | s->uart_status = 0x0000003c; | |
200 | s->uart_rdata = 0x00000000; | |
201 | s->uart_fifo_ctrl = 0x00000000; | |
202 | s->uart_fifo_status = 0x00000000; | |
203 | s->uart_ovrd = 0x00000000; | |
204 | s->uart_val = 0x00000000; | |
205 | s->uart_timeout_ctrl = 0x00000000; | |
206 | ||
207 | s->tx_level = 0; | |
82a4ed8e | 208 | s->rx_level = 0; |
a7d2d98c AF |
209 | |
210 | s->char_tx_time = (NANOSECONDS_PER_SECOND / 230400) * 10; | |
211 | ||
212 | ibex_uart_update_irqs(s); | |
213 | } | |
214 | ||
940aabb9 AF |
215 | static uint64_t ibex_uart_get_baud(IbexUartState *s) |
216 | { | |
217 | uint64_t baud; | |
218 | ||
59093cc4 | 219 | baud = ((s->uart_ctrl & R_CTRL_NCO_MASK) >> 16); |
940aabb9 AF |
220 | baud *= clock_get_hz(s->f_clk); |
221 | baud >>= 20; | |
222 | ||
223 | return baud; | |
224 | } | |
225 | ||
a7d2d98c AF |
226 | static uint64_t ibex_uart_read(void *opaque, hwaddr addr, |
227 | unsigned int size) | |
228 | { | |
229 | IbexUartState *s = opaque; | |
230 | uint64_t retvalue = 0; | |
231 | ||
59093cc4 AF |
232 | switch (addr >> 2) { |
233 | case R_INTR_STATE: | |
a7d2d98c AF |
234 | retvalue = s->uart_intr_state; |
235 | break; | |
59093cc4 | 236 | case R_INTR_ENABLE: |
a7d2d98c AF |
237 | retvalue = s->uart_intr_enable; |
238 | break; | |
59093cc4 | 239 | case R_INTR_TEST: |
a7d2d98c AF |
240 | qemu_log_mask(LOG_GUEST_ERROR, |
241 | "%s: wdata is write only\n", __func__); | |
242 | break; | |
243 | ||
59093cc4 | 244 | case R_CTRL: |
a7d2d98c AF |
245 | retvalue = s->uart_ctrl; |
246 | break; | |
59093cc4 | 247 | case R_STATUS: |
a7d2d98c AF |
248 | retvalue = s->uart_status; |
249 | break; | |
250 | ||
59093cc4 | 251 | case R_RDATA: |
a7d2d98c | 252 | retvalue = s->uart_rdata; |
82a4ed8e | 253 | if ((s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) && (s->rx_level > 0)) { |
a7d2d98c AF |
254 | qemu_chr_fe_accept_input(&s->chr); |
255 | ||
82a4ed8e AW |
256 | s->rx_level -= 1; |
257 | s->uart_status &= ~R_STATUS_RXFULL_MASK; | |
258 | if (s->rx_level == 0) { | |
259 | s->uart_status |= R_STATUS_RXIDLE_MASK; | |
260 | s->uart_status |= R_STATUS_RXEMPTY_MASK; | |
261 | } | |
a7d2d98c AF |
262 | } |
263 | break; | |
59093cc4 | 264 | case R_WDATA: |
a7d2d98c AF |
265 | qemu_log_mask(LOG_GUEST_ERROR, |
266 | "%s: wdata is write only\n", __func__); | |
267 | break; | |
268 | ||
59093cc4 | 269 | case R_FIFO_CTRL: |
a7d2d98c AF |
270 | retvalue = s->uart_fifo_ctrl; |
271 | break; | |
59093cc4 | 272 | case R_FIFO_STATUS: |
a7d2d98c AF |
273 | retvalue = s->uart_fifo_status; |
274 | ||
82a4ed8e AW |
275 | retvalue |= (s->rx_level & 0x1F) << R_FIFO_STATUS_RXLVL_SHIFT; |
276 | retvalue |= (s->tx_level & 0x1F) << R_FIFO_STATUS_TXLVL_SHIFT; | |
a7d2d98c AF |
277 | |
278 | qemu_log_mask(LOG_UNIMP, | |
279 | "%s: RX fifos are not supported\n", __func__); | |
280 | break; | |
281 | ||
59093cc4 | 282 | case R_OVRD: |
a7d2d98c AF |
283 | retvalue = s->uart_ovrd; |
284 | qemu_log_mask(LOG_UNIMP, | |
285 | "%s: ovrd is not supported\n", __func__); | |
286 | break; | |
59093cc4 | 287 | case R_VAL: |
a7d2d98c AF |
288 | retvalue = s->uart_val; |
289 | qemu_log_mask(LOG_UNIMP, | |
290 | "%s: val is not supported\n", __func__); | |
291 | break; | |
59093cc4 | 292 | case R_TIMEOUT_CTRL: |
a7d2d98c AF |
293 | retvalue = s->uart_timeout_ctrl; |
294 | qemu_log_mask(LOG_UNIMP, | |
295 | "%s: timeout_ctrl is not supported\n", __func__); | |
296 | break; | |
297 | default: | |
298 | qemu_log_mask(LOG_GUEST_ERROR, | |
299 | "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); | |
300 | return 0; | |
301 | } | |
302 | ||
303 | return retvalue; | |
304 | } | |
305 | ||
306 | static void ibex_uart_write(void *opaque, hwaddr addr, | |
307 | uint64_t val64, unsigned int size) | |
308 | { | |
309 | IbexUartState *s = opaque; | |
310 | uint32_t value = val64; | |
311 | ||
59093cc4 AF |
312 | switch (addr >> 2) { |
313 | case R_INTR_STATE: | |
a7d2d98c AF |
314 | /* Write 1 clear */ |
315 | s->uart_intr_state &= ~value; | |
316 | ibex_uart_update_irqs(s); | |
317 | break; | |
59093cc4 | 318 | case R_INTR_ENABLE: |
a7d2d98c AF |
319 | s->uart_intr_enable = value; |
320 | ibex_uart_update_irqs(s); | |
321 | break; | |
59093cc4 | 322 | case R_INTR_TEST: |
a7d2d98c AF |
323 | s->uart_intr_state |= value; |
324 | ibex_uart_update_irqs(s); | |
325 | break; | |
326 | ||
59093cc4 | 327 | case R_CTRL: |
a7d2d98c AF |
328 | s->uart_ctrl = value; |
329 | ||
59093cc4 | 330 | if (value & R_CTRL_NF_MASK) { |
a7d2d98c AF |
331 | qemu_log_mask(LOG_UNIMP, |
332 | "%s: UART_CTRL_NF is not supported\n", __func__); | |
333 | } | |
59093cc4 | 334 | if (value & R_CTRL_SLPBK_MASK) { |
a7d2d98c AF |
335 | qemu_log_mask(LOG_UNIMP, |
336 | "%s: UART_CTRL_SLPBK is not supported\n", __func__); | |
337 | } | |
59093cc4 | 338 | if (value & R_CTRL_LLPBK_MASK) { |
a7d2d98c AF |
339 | qemu_log_mask(LOG_UNIMP, |
340 | "%s: UART_CTRL_LLPBK is not supported\n", __func__); | |
341 | } | |
59093cc4 | 342 | if (value & R_CTRL_PARITY_EN_MASK) { |
a7d2d98c AF |
343 | qemu_log_mask(LOG_UNIMP, |
344 | "%s: UART_CTRL_PARITY_EN is not supported\n", | |
345 | __func__); | |
346 | } | |
59093cc4 | 347 | if (value & R_CTRL_PARITY_ODD_MASK) { |
a7d2d98c AF |
348 | qemu_log_mask(LOG_UNIMP, |
349 | "%s: UART_CTRL_PARITY_ODD is not supported\n", | |
350 | __func__); | |
351 | } | |
59093cc4 | 352 | if (value & R_CTRL_RXBLVL_MASK) { |
a7d2d98c AF |
353 | qemu_log_mask(LOG_UNIMP, |
354 | "%s: UART_CTRL_RXBLVL is not supported\n", __func__); | |
355 | } | |
59093cc4 | 356 | if (value & R_CTRL_NCO_MASK) { |
940aabb9 | 357 | uint64_t baud = ibex_uart_get_baud(s); |
a7d2d98c AF |
358 | |
359 | s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10; | |
360 | } | |
361 | break; | |
59093cc4 | 362 | case R_STATUS: |
a7d2d98c AF |
363 | qemu_log_mask(LOG_GUEST_ERROR, |
364 | "%s: status is read only\n", __func__); | |
365 | break; | |
366 | ||
59093cc4 | 367 | case R_RDATA: |
a7d2d98c AF |
368 | qemu_log_mask(LOG_GUEST_ERROR, |
369 | "%s: rdata is read only\n", __func__); | |
370 | break; | |
59093cc4 | 371 | case R_WDATA: |
a7d2d98c AF |
372 | uart_write_tx_fifo(s, (uint8_t *) &value, 1); |
373 | break; | |
374 | ||
59093cc4 | 375 | case R_FIFO_CTRL: |
a7d2d98c AF |
376 | s->uart_fifo_ctrl = value; |
377 | ||
59093cc4 | 378 | if (value & R_FIFO_CTRL_RXRST_MASK) { |
82a4ed8e | 379 | s->rx_level = 0; |
a7d2d98c AF |
380 | qemu_log_mask(LOG_UNIMP, |
381 | "%s: RX fifos are not supported\n", __func__); | |
382 | } | |
59093cc4 | 383 | if (value & R_FIFO_CTRL_TXRST_MASK) { |
a7d2d98c AF |
384 | s->tx_level = 0; |
385 | } | |
386 | break; | |
59093cc4 | 387 | case R_FIFO_STATUS: |
a7d2d98c AF |
388 | qemu_log_mask(LOG_GUEST_ERROR, |
389 | "%s: fifo_status is read only\n", __func__); | |
390 | break; | |
391 | ||
59093cc4 | 392 | case R_OVRD: |
a7d2d98c AF |
393 | s->uart_ovrd = value; |
394 | qemu_log_mask(LOG_UNIMP, | |
395 | "%s: ovrd is not supported\n", __func__); | |
396 | break; | |
59093cc4 | 397 | case R_VAL: |
a7d2d98c AF |
398 | qemu_log_mask(LOG_GUEST_ERROR, |
399 | "%s: val is read only\n", __func__); | |
400 | break; | |
59093cc4 | 401 | case R_TIMEOUT_CTRL: |
a7d2d98c AF |
402 | s->uart_timeout_ctrl = value; |
403 | qemu_log_mask(LOG_UNIMP, | |
404 | "%s: timeout_ctrl is not supported\n", __func__); | |
405 | break; | |
406 | default: | |
407 | qemu_log_mask(LOG_GUEST_ERROR, | |
408 | "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); | |
409 | } | |
410 | } | |
411 | ||
5ee0abed | 412 | static void ibex_uart_clk_update(void *opaque, ClockEvent event) |
940aabb9 AF |
413 | { |
414 | IbexUartState *s = opaque; | |
415 | ||
416 | /* recompute uart's speed on clock change */ | |
417 | uint64_t baud = ibex_uart_get_baud(s); | |
418 | ||
419 | s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10; | |
420 | } | |
421 | ||
a7d2d98c AF |
422 | static void fifo_trigger_update(void *opaque) |
423 | { | |
424 | IbexUartState *s = opaque; | |
425 | ||
59093cc4 | 426 | if (s->uart_ctrl & R_CTRL_TX_ENABLE_MASK) { |
a7d2d98c AF |
427 | ibex_uart_xmit(NULL, G_IO_OUT, s); |
428 | } | |
429 | } | |
430 | ||
431 | static const MemoryRegionOps ibex_uart_ops = { | |
432 | .read = ibex_uart_read, | |
433 | .write = ibex_uart_write, | |
434 | .endianness = DEVICE_NATIVE_ENDIAN, | |
435 | .impl.min_access_size = 4, | |
436 | .impl.max_access_size = 4, | |
437 | }; | |
438 | ||
439 | static int ibex_uart_post_load(void *opaque, int version_id) | |
440 | { | |
441 | IbexUartState *s = opaque; | |
442 | ||
443 | ibex_uart_update_irqs(s); | |
444 | return 0; | |
445 | } | |
446 | ||
447 | static const VMStateDescription vmstate_ibex_uart = { | |
448 | .name = TYPE_IBEX_UART, | |
449 | .version_id = 1, | |
450 | .minimum_version_id = 1, | |
451 | .post_load = ibex_uart_post_load, | |
452 | .fields = (VMStateField[]) { | |
453 | VMSTATE_UINT8_ARRAY(tx_fifo, IbexUartState, | |
454 | IBEX_UART_TX_FIFO_SIZE), | |
455 | VMSTATE_UINT32(tx_level, IbexUartState), | |
456 | VMSTATE_UINT64(char_tx_time, IbexUartState), | |
457 | VMSTATE_TIMER_PTR(fifo_trigger_handle, IbexUartState), | |
458 | VMSTATE_UINT32(uart_intr_state, IbexUartState), | |
459 | VMSTATE_UINT32(uart_intr_enable, IbexUartState), | |
460 | VMSTATE_UINT32(uart_ctrl, IbexUartState), | |
461 | VMSTATE_UINT32(uart_status, IbexUartState), | |
462 | VMSTATE_UINT32(uart_rdata, IbexUartState), | |
463 | VMSTATE_UINT32(uart_fifo_ctrl, IbexUartState), | |
464 | VMSTATE_UINT32(uart_fifo_status, IbexUartState), | |
465 | VMSTATE_UINT32(uart_ovrd, IbexUartState), | |
466 | VMSTATE_UINT32(uart_val, IbexUartState), | |
467 | VMSTATE_UINT32(uart_timeout_ctrl, IbexUartState), | |
468 | VMSTATE_END_OF_LIST() | |
469 | } | |
470 | }; | |
471 | ||
472 | static Property ibex_uart_properties[] = { | |
473 | DEFINE_PROP_CHR("chardev", IbexUartState, chr), | |
474 | DEFINE_PROP_END_OF_LIST(), | |
475 | }; | |
476 | ||
477 | static void ibex_uart_init(Object *obj) | |
478 | { | |
479 | IbexUartState *s = IBEX_UART(obj); | |
480 | ||
940aabb9 | 481 | s->f_clk = qdev_init_clock_in(DEVICE(obj), "f_clock", |
5ee0abed | 482 | ibex_uart_clk_update, s, ClockUpdate); |
940aabb9 AF |
483 | clock_set_hz(s->f_clk, IBEX_UART_CLOCK); |
484 | ||
a7d2d98c AF |
485 | sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_watermark); |
486 | sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_watermark); | |
487 | sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_empty); | |
488 | sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_overflow); | |
489 | ||
490 | memory_region_init_io(&s->mmio, obj, &ibex_uart_ops, s, | |
491 | TYPE_IBEX_UART, 0x400); | |
492 | sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); | |
493 | } | |
494 | ||
495 | static void ibex_uart_realize(DeviceState *dev, Error **errp) | |
496 | { | |
497 | IbexUartState *s = IBEX_UART(dev); | |
498 | ||
499 | s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL, | |
500 | fifo_trigger_update, s); | |
501 | ||
502 | qemu_chr_fe_set_handlers(&s->chr, ibex_uart_can_receive, | |
503 | ibex_uart_receive, NULL, NULL, | |
504 | s, NULL, true); | |
505 | } | |
506 | ||
507 | static void ibex_uart_class_init(ObjectClass *klass, void *data) | |
508 | { | |
509 | DeviceClass *dc = DEVICE_CLASS(klass); | |
510 | ||
511 | dc->reset = ibex_uart_reset; | |
512 | dc->realize = ibex_uart_realize; | |
513 | dc->vmsd = &vmstate_ibex_uart; | |
514 | device_class_set_props(dc, ibex_uart_properties); | |
515 | } | |
516 | ||
517 | static const TypeInfo ibex_uart_info = { | |
518 | .name = TYPE_IBEX_UART, | |
519 | .parent = TYPE_SYS_BUS_DEVICE, | |
520 | .instance_size = sizeof(IbexUartState), | |
521 | .instance_init = ibex_uart_init, | |
522 | .class_init = ibex_uart_class_init, | |
523 | }; | |
524 | ||
525 | static void ibex_uart_register_types(void) | |
526 | { | |
527 | type_register_static(&ibex_uart_info); | |
528 | } | |
529 | ||
530 | type_init(ibex_uart_register_types) |