1 // Copyright 2024, Linaro Limited
2 // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
3 // SPDX-License-Identifier: GPL-2.0-or-later
4
5 use std::{ffi::CStr, mem::size_of};
6
7 use qemu_api::{
8 chardev::{CharBackend, Chardev, Event},
9 impl_vmstate_forward,
10 irq::{IRQState, InterruptSource},
11 log::Log,
12 log_mask_ln,
13 memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
14 prelude::*,
15 qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
16 qom::{ObjectImpl, Owned, ParentField, ParentInit},
17 static_assert,
18 sysbus::{SysBusDevice, SysBusDeviceImpl},
19 uninit_field_mut,
20 vmstate::VMStateDescription,
21 };
22
23 use crate::{
24 device_class,
25 registers::{self, Interrupt, RegisterOffset},
26 };
27
28 // TODO: You must disable the UART before any of the control registers are
29 // reprogrammed. When the UART is disabled in the middle of transmission or
30 // reception, it completes the current character before stopping
31
32 /// Integer Baud Rate Divider, `UARTIBRD`
33 const IBRD_MASK: u32 = 0xffff;
34
35 /// Fractional Baud Rate Divider, `UARTFBRD`
36 const FBRD_MASK: u32 = 0x3f;
37
38 /// QEMU sourced constant.
39 pub const PL011_FIFO_DEPTH: u32 = 16;
40
41 #[derive(Clone, Copy)]
42 struct DeviceId(&'static [u8; 8]);
43
44 impl std::ops::Index<hwaddr> for DeviceId {
45 type Output = u8;
46
index(&self, idx: hwaddr) -> &Self::Output47 fn index(&self, idx: hwaddr) -> &Self::Output {
48 &self.0[idx as usize]
49 }
50 }
51
52 // FIFOs use 32-bit indices instead of usize, for compatibility with
53 // the migration stream produced by the C version of this device.
54 #[repr(transparent)]
55 #[derive(Debug, Default)]
56 pub struct Fifo([registers::Data; PL011_FIFO_DEPTH as usize]);
57 impl_vmstate_forward!(Fifo);
58
59 impl Fifo {
len(&self) -> u3260 const fn len(&self) -> u32 {
61 self.0.len() as u32
62 }
63 }
64
65 impl std::ops::IndexMut<u32> for Fifo {
index_mut(&mut self, idx: u32) -> &mut Self::Output66 fn index_mut(&mut self, idx: u32) -> &mut Self::Output {
67 &mut self.0[idx as usize]
68 }
69 }
70
71 impl std::ops::Index<u32> for Fifo {
72 type Output = registers::Data;
73
index(&self, idx: u32) -> &Self::Output74 fn index(&self, idx: u32) -> &Self::Output {
75 &self.0[idx as usize]
76 }
77 }
78
79 #[repr(C)]
80 #[derive(Debug, Default)]
81 pub struct PL011Registers {
82 #[doc(alias = "fr")]
83 pub flags: registers::Flags,
84 #[doc(alias = "lcr")]
85 pub line_control: registers::LineControl,
86 #[doc(alias = "rsr")]
87 pub receive_status_error_clear: registers::ReceiveStatusErrorClear,
88 #[doc(alias = "cr")]
89 pub control: registers::Control,
90 pub dmacr: u32,
91 pub int_enabled: Interrupt,
92 pub int_level: Interrupt,
93 pub read_fifo: Fifo,
94 pub ilpr: u32,
95 pub ibrd: u32,
96 pub fbrd: u32,
97 pub ifl: u32,
98 pub read_pos: u32,
99 pub read_count: u32,
100 pub read_trigger: u32,
101 }
102
103 #[repr(C)]
104 #[derive(qemu_api_macros::Object)]
105 /// PL011 Device Model in QEMU
106 pub struct PL011State {
107 pub parent_obj: ParentField<SysBusDevice>,
108 pub iomem: MemoryRegion,
109 #[doc(alias = "chr")]
110 pub char_backend: CharBackend,
111 pub regs: BqlRefCell<PL011Registers>,
112 /// QEMU interrupts
113 ///
114 /// ```text
115 /// * sysbus MMIO region 0: device registers
116 /// * sysbus IRQ 0: `UARTINTR` (combined interrupt line)
117 /// * sysbus IRQ 1: `UARTRXINTR` (receive FIFO interrupt line)
118 /// * sysbus IRQ 2: `UARTTXINTR` (transmit FIFO interrupt line)
119 /// * sysbus IRQ 3: `UARTRTINTR` (receive timeout interrupt line)
120 /// * sysbus IRQ 4: `UARTMSINTR` (momem status interrupt line)
121 /// * sysbus IRQ 5: `UARTEINTR` (error interrupt line)
122 /// ```
123 #[doc(alias = "irq")]
124 pub interrupts: [InterruptSource; IRQMASK.len()],
125 #[doc(alias = "clk")]
126 pub clock: Owned<Clock>,
127 #[doc(alias = "migrate_clk")]
128 pub migrate_clock: bool,
129 }
130
131 // Some C users of this device embed its state struct into their own
132 // structs, so the size of the Rust version must not be any larger
133 // than the size of the C one. If this assert triggers you need to
134 // expand the padding_for_rust[] array in the C PL011State struct.
135 static_assert!(size_of::<PL011State>() <= size_of::<qemu_api::bindings::PL011State>());
136
137 qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
138
139 #[repr(C)]
140 pub struct PL011Class {
141 parent_class: <SysBusDevice as ObjectType>::Class,
142 /// The byte string that identifies the device.
143 device_id: DeviceId,
144 }
145
146 trait PL011Impl: SysBusDeviceImpl + IsA<PL011State> {
147 const DEVICE_ID: DeviceId;
148 }
149
150 impl PL011Class {
class_init<T: PL011Impl>(&mut self)151 fn class_init<T: PL011Impl>(&mut self) {
152 self.device_id = T::DEVICE_ID;
153 self.parent_class.class_init::<T>();
154 }
155 }
156
157 unsafe impl ObjectType for PL011State {
158 type Class = PL011Class;
159 const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
160 }
161
162 impl PL011Impl for PL011State {
163 const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]);
164 }
165
166 impl ObjectImpl for PL011State {
167 type ParentType = SysBusDevice;
168
169 const INSTANCE_INIT: Option<unsafe fn(ParentInit<Self>)> = Some(Self::init);
170 const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init);
171 const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
172 }
173
174 impl DeviceImpl for PL011State {
properties() -> &'static [Property]175 fn properties() -> &'static [Property] {
176 &device_class::PL011_PROPERTIES
177 }
vmsd() -> Option<&'static VMStateDescription>178 fn vmsd() -> Option<&'static VMStateDescription> {
179 Some(&device_class::VMSTATE_PL011)
180 }
181 const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize);
182 }
183
184 impl ResettablePhasesImpl for PL011State {
185 const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold);
186 }
187
188 impl SysBusDeviceImpl for PL011State {}
189
190 impl PL011Registers {
read(&mut self, offset: RegisterOffset) -> (bool, u32)191 pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) {
192 use RegisterOffset::*;
193
194 let mut update = false;
195 let result = match offset {
196 DR => self.read_data_register(&mut update),
197 RSR => u32::from(self.receive_status_error_clear),
198 FR => u32::from(self.flags),
199 FBRD => self.fbrd,
200 ILPR => self.ilpr,
201 IBRD => self.ibrd,
202 LCR_H => u32::from(self.line_control),
203 CR => u32::from(self.control),
204 FLS => self.ifl,
205 IMSC => u32::from(self.int_enabled),
206 RIS => u32::from(self.int_level),
207 MIS => u32::from(self.int_level & self.int_enabled),
208 ICR => {
209 // "The UARTICR Register is the interrupt clear register and is write-only"
210 // Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR
211 0
212 }
213 DMACR => self.dmacr,
214 };
215 (update, result)
216 }
217
write( &mut self, offset: RegisterOffset, value: u32, char_backend: &CharBackend, ) -> bool218 pub(self) fn write(
219 &mut self,
220 offset: RegisterOffset,
221 value: u32,
222 char_backend: &CharBackend,
223 ) -> bool {
224 // eprintln!("write offset {offset} value {value}");
225 use RegisterOffset::*;
226 match offset {
227 DR => return self.write_data_register(value),
228 RSR => {
229 self.receive_status_error_clear = 0.into();
230 }
231 FR => {
232 // flag writes are ignored
233 }
234 ILPR => {
235 self.ilpr = value;
236 }
237 IBRD => {
238 self.ibrd = value;
239 }
240 FBRD => {
241 self.fbrd = value;
242 }
243 LCR_H => {
244 let new_val: registers::LineControl = value.into();
245 // Reset the FIFO state on FIFO enable or disable
246 if self.line_control.fifos_enabled() != new_val.fifos_enabled() {
247 self.reset_rx_fifo();
248 self.reset_tx_fifo();
249 }
250 let update = (self.line_control.send_break() != new_val.send_break()) && {
251 let break_enable = new_val.send_break();
252 let _ = char_backend.send_break(break_enable);
253 self.loopback_break(break_enable)
254 };
255 self.line_control = new_val;
256 self.set_read_trigger();
257 return update;
258 }
259 CR => {
260 // ??? Need to implement the enable bit.
261 self.control = value.into();
262 return self.loopback_mdmctrl();
263 }
264 FLS => {
265 self.ifl = value;
266 self.set_read_trigger();
267 }
268 IMSC => {
269 self.int_enabled = Interrupt::from(value);
270 return true;
271 }
272 RIS => {}
273 MIS => {}
274 ICR => {
275 self.int_level &= !Interrupt::from(value);
276 return true;
277 }
278 DMACR => {
279 self.dmacr = value;
280 if value & 3 > 0 {
281 log_mask_ln!(Log::Unimp, "pl011: DMA not implemented");
282 }
283 }
284 }
285 false
286 }
287
read_data_register(&mut self, update: &mut bool) -> u32288 fn read_data_register(&mut self, update: &mut bool) -> u32 {
289 self.flags.set_receive_fifo_full(false);
290 let c = self.read_fifo[self.read_pos];
291
292 if self.read_count > 0 {
293 self.read_count -= 1;
294 self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1);
295 }
296 if self.read_count == 0 {
297 self.flags.set_receive_fifo_empty(true);
298 }
299 if self.read_count + 1 == self.read_trigger {
300 self.int_level &= !Interrupt::RX;
301 }
302 self.receive_status_error_clear.set_from_data(c);
303 *update = true;
304 u32::from(c)
305 }
306
write_data_register(&mut self, value: u32) -> bool307 fn write_data_register(&mut self, value: u32) -> bool {
308 if !self.control.enable_uart() {
309 log_mask_ln!(Log::GuestError, "PL011 data written to disabled UART");
310 }
311 if !self.control.enable_transmit() {
312 log_mask_ln!(Log::GuestError, "PL011 data written to disabled TX UART");
313 }
314 // interrupts always checked
315 let _ = self.loopback_tx(value.into());
316 self.int_level |= Interrupt::TX;
317 true
318 }
319
320 #[inline]
321 #[must_use]
loopback_tx(&mut self, value: registers::Data) -> bool322 fn loopback_tx(&mut self, value: registers::Data) -> bool {
323 // Caveat:
324 //
325 // In real hardware, TX loopback happens at the serial-bit level
326 // and then reassembled by the RX logics back into bytes and placed
327 // into the RX fifo. That is, loopback happens after TX fifo.
328 //
329 // Because the real hardware TX fifo is time-drained at the frame
330 // rate governed by the configured serial format, some loopback
331 // bytes in TX fifo may still be able to get into the RX fifo
332 // that could be full at times while being drained at software
333 // pace.
334 //
335 // In such scenario, the RX draining pace is the major factor
336 // deciding which loopback bytes get into the RX fifo, unless
337 // hardware flow-control is enabled.
338 //
339 // For simplicity, the above described is not emulated.
340 self.loopback_enabled() && self.fifo_rx_put(value)
341 }
342
343 #[must_use]
loopback_mdmctrl(&mut self) -> bool344 fn loopback_mdmctrl(&mut self) -> bool {
345 if !self.loopback_enabled() {
346 return false;
347 }
348
349 /*
350 * Loopback software-driven modem control outputs to modem status inputs:
351 * FR.RI <= CR.Out2
352 * FR.DCD <= CR.Out1
353 * FR.CTS <= CR.RTS
354 * FR.DSR <= CR.DTR
355 *
356 * The loopback happens immediately even if this call is triggered
357 * by setting only CR.LBE.
358 *
359 * CTS/RTS updates due to enabled hardware flow controls are not
360 * dealt with here.
361 */
362
363 self.flags.set_ring_indicator(self.control.out_2());
364 self.flags.set_data_carrier_detect(self.control.out_1());
365 self.flags.set_clear_to_send(self.control.request_to_send());
366 self.flags
367 .set_data_set_ready(self.control.data_transmit_ready());
368
369 // Change interrupts based on updated FR
370 let mut il = self.int_level;
371
372 il &= !Interrupt::MS;
373
374 if self.flags.data_set_ready() {
375 il |= Interrupt::DSR;
376 }
377 if self.flags.data_carrier_detect() {
378 il |= Interrupt::DCD;
379 }
380 if self.flags.clear_to_send() {
381 il |= Interrupt::CTS;
382 }
383 if self.flags.ring_indicator() {
384 il |= Interrupt::RI;
385 }
386 self.int_level = il;
387 true
388 }
389
loopback_break(&mut self, enable: bool) -> bool390 fn loopback_break(&mut self, enable: bool) -> bool {
391 enable && self.loopback_tx(registers::Data::BREAK)
392 }
393
set_read_trigger(&mut self)394 fn set_read_trigger(&mut self) {
395 self.read_trigger = 1;
396 }
397
reset(&mut self)398 pub fn reset(&mut self) {
399 self.line_control.reset();
400 self.receive_status_error_clear.reset();
401 self.dmacr = 0;
402 self.int_enabled = 0.into();
403 self.int_level = 0.into();
404 self.ilpr = 0;
405 self.ibrd = 0;
406 self.fbrd = 0;
407 self.read_trigger = 1;
408 self.ifl = 0x12;
409 self.control.reset();
410 self.flags.reset();
411 self.reset_rx_fifo();
412 self.reset_tx_fifo();
413 }
414
reset_rx_fifo(&mut self)415 pub fn reset_rx_fifo(&mut self) {
416 self.read_count = 0;
417 self.read_pos = 0;
418
419 // Reset FIFO flags
420 self.flags.set_receive_fifo_full(false);
421 self.flags.set_receive_fifo_empty(true);
422 }
423
reset_tx_fifo(&mut self)424 pub fn reset_tx_fifo(&mut self) {
425 // Reset FIFO flags
426 self.flags.set_transmit_fifo_full(false);
427 self.flags.set_transmit_fifo_empty(true);
428 }
429
430 #[inline]
fifo_enabled(&self) -> bool431 pub fn fifo_enabled(&self) -> bool {
432 self.line_control.fifos_enabled() == registers::Mode::FIFO
433 }
434
435 #[inline]
loopback_enabled(&self) -> bool436 pub fn loopback_enabled(&self) -> bool {
437 self.control.enable_loopback()
438 }
439
440 #[inline]
fifo_depth(&self) -> u32441 pub fn fifo_depth(&self) -> u32 {
442 // Note: FIFO depth is expected to be power-of-2
443 if self.fifo_enabled() {
444 return PL011_FIFO_DEPTH;
445 }
446 1
447 }
448
449 #[must_use]
fifo_rx_put(&mut self, value: registers::Data) -> bool450 pub fn fifo_rx_put(&mut self, value: registers::Data) -> bool {
451 let depth = self.fifo_depth();
452 assert!(depth > 0);
453 let slot = (self.read_pos + self.read_count) & (depth - 1);
454 self.read_fifo[slot] = value;
455 self.read_count += 1;
456 self.flags.set_receive_fifo_empty(false);
457 if self.read_count == depth {
458 self.flags.set_receive_fifo_full(true);
459 }
460
461 if self.read_count == self.read_trigger {
462 self.int_level |= Interrupt::RX;
463 return true;
464 }
465 false
466 }
467
post_load(&mut self) -> Result<(), ()>468 pub fn post_load(&mut self) -> Result<(), ()> {
469 /* Sanity-check input state */
470 if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() {
471 return Err(());
472 }
473
474 if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 {
475 // Older versions of PL011 didn't ensure that the single
476 // character in the FIFO in FIFO-disabled mode is in
477 // element 0 of the array; convert to follow the current
478 // code's assumptions.
479 self.read_fifo[0] = self.read_fifo[self.read_pos];
480 self.read_pos = 0;
481 }
482
483 self.ibrd &= IBRD_MASK;
484 self.fbrd &= FBRD_MASK;
485
486 Ok(())
487 }
488 }
489
490 impl PL011State {
491 /// Initializes a pre-allocated, uninitialized instance of `PL011State`.
492 ///
493 /// # Safety
494 ///
495 /// `self` must point to a correctly sized and aligned location for the
496 /// `PL011State` type. It must not be called more than once on the same
497 /// location/instance. All its fields are expected to hold uninitialized
498 /// values with the sole exception of `parent_obj`.
init(mut this: ParentInit<Self>)499 unsafe fn init(mut this: ParentInit<Self>) {
500 static PL011_OPS: MemoryRegionOps<PL011State> = MemoryRegionOpsBuilder::<PL011State>::new()
501 .read(&PL011State::read)
502 .write(&PL011State::write)
503 .native_endian()
504 .impl_sizes(4, 4)
505 .build();
506
507 // SAFETY: this and this.iomem are guaranteed to be valid at this point
508 MemoryRegion::init_io(
509 &mut uninit_field_mut!(*this, iomem),
510 &PL011_OPS,
511 "pl011",
512 0x1000,
513 );
514
515 uninit_field_mut!(*this, regs).write(Default::default());
516
517 let clock = DeviceState::init_clock_in(
518 &mut this,
519 "clk",
520 &Self::clock_update,
521 ClockEvent::ClockUpdate,
522 );
523 uninit_field_mut!(*this, clock).write(clock);
524 }
525
clock_update(&self, _event: ClockEvent)526 const fn clock_update(&self, _event: ClockEvent) {
527 /* pl011_trace_baudrate_change(s); */
528 }
529
post_init(&self)530 fn post_init(&self) {
531 self.init_mmio(&self.iomem);
532 for irq in self.interrupts.iter() {
533 self.init_irq(irq);
534 }
535 }
536
read(&self, offset: hwaddr, _size: u32) -> u64537 fn read(&self, offset: hwaddr, _size: u32) -> u64 {
538 match RegisterOffset::try_from(offset) {
539 Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => {
540 let device_id = self.get_class().device_id;
541 u64::from(device_id[(offset - 0xfe0) >> 2])
542 }
543 Err(_) => {
544 log_mask_ln!(Log::GuestError, "PL011State::read: Bad offset {offset}");
545 0
546 }
547 Ok(field) => {
548 let (update_irq, result) = self.regs.borrow_mut().read(field);
549 if update_irq {
550 self.update();
551 self.char_backend.accept_input();
552 }
553 result.into()
554 }
555 }
556 }
557
write(&self, offset: hwaddr, value: u64, _size: u32)558 fn write(&self, offset: hwaddr, value: u64, _size: u32) {
559 let mut update_irq = false;
560 if let Ok(field) = RegisterOffset::try_from(offset) {
561 // qemu_chr_fe_write_all() calls into the can_receive
562 // callback, so handle writes before entering PL011Registers.
563 if field == RegisterOffset::DR {
564 // ??? Check if transmitter is enabled.
565 let ch: [u8; 1] = [value as u8];
566 // XXX this blocks entire thread. Rewrite to use
567 // qemu_chr_fe_write and background I/O callbacks
568 let _ = self.char_backend.write_all(&ch);
569 }
570
571 update_irq = self
572 .regs
573 .borrow_mut()
574 .write(field, value as u32, &self.char_backend);
575 } else {
576 log_mask_ln!(
577 Log::GuestError,
578 "PL011State::write: Bad offset {offset} value {value}"
579 );
580 }
581 if update_irq {
582 self.update();
583 }
584 }
585
can_receive(&self) -> u32586 fn can_receive(&self) -> u32 {
587 let regs = self.regs.borrow();
588 // trace_pl011_can_receive(s->lcr, s->read_count, r);
589 regs.fifo_depth() - regs.read_count
590 }
591
receive(&self, buf: &[u8])592 fn receive(&self, buf: &[u8]) {
593 let mut regs = self.regs.borrow_mut();
594 if regs.loopback_enabled() {
595 // In loopback mode, the RX input signal is internally disconnected
596 // from the entire receiving logics; thus, all inputs are ignored,
597 // and BREAK detection on RX input signal is also not performed.
598 return;
599 }
600
601 let mut update_irq = false;
602 for &c in buf {
603 let c: u32 = c.into();
604 update_irq |= regs.fifo_rx_put(c.into());
605 }
606
607 // Release the BqlRefCell before calling self.update()
608 drop(regs);
609 if update_irq {
610 self.update();
611 }
612 }
613
event(&self, event: Event)614 fn event(&self, event: Event) {
615 let mut update_irq = false;
616 let mut regs = self.regs.borrow_mut();
617 if event == Event::CHR_EVENT_BREAK && !regs.loopback_enabled() {
618 update_irq = regs.fifo_rx_put(registers::Data::BREAK);
619 }
620 // Release the BqlRefCell before calling self.update()
621 drop(regs);
622
623 if update_irq {
624 self.update()
625 }
626 }
627
realize(&self) -> qemu_api::Result<()>628 fn realize(&self) -> qemu_api::Result<()> {
629 self.char_backend
630 .enable_handlers(self, Self::can_receive, Self::receive, Self::event);
631 Ok(())
632 }
633
reset_hold(&self, _type: ResetType)634 fn reset_hold(&self, _type: ResetType) {
635 self.regs.borrow_mut().reset();
636 }
637
update(&self)638 fn update(&self) {
639 let regs = self.regs.borrow();
640 let flags = regs.int_level & regs.int_enabled;
641 for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
642 irq.set(flags.any_set(i));
643 }
644 }
645
post_load(&self, _version_id: u32) -> Result<(), ()>646 pub fn post_load(&self, _version_id: u32) -> Result<(), ()> {
647 self.regs.borrow_mut().post_load()
648 }
649 }
650
651 /// Which bits in the interrupt status matter for each outbound IRQ line ?
652 const IRQMASK: [Interrupt; 6] = [
653 Interrupt::all(),
654 Interrupt::RX,
655 Interrupt::TX,
656 Interrupt::RT,
657 Interrupt::MS,
658 Interrupt::E,
659 ];
660
661 /// # Safety
662 ///
663 /// We expect the FFI user of this function to pass a valid pointer for `chr`
664 /// and `irq`.
665 #[no_mangle]
pl011_create( addr: u64, irq: *mut IRQState, chr: *mut Chardev, ) -> *mut DeviceState666 pub unsafe extern "C" fn pl011_create(
667 addr: u64,
668 irq: *mut IRQState,
669 chr: *mut Chardev,
670 ) -> *mut DeviceState {
671 // SAFETY: The callers promise that they have owned references.
672 // They do not gift them to pl011_create, so use `Owned::from`.
673 let irq = unsafe { Owned::<IRQState>::from(&*irq) };
674
675 let dev = PL011State::new();
676 if !chr.is_null() {
677 let chr = unsafe { Owned::<Chardev>::from(&*chr) };
678 dev.prop_set_chr("chardev", &chr);
679 }
680 dev.sysbus_realize();
681 dev.mmio_map(0, addr);
682 dev.connect_irq(0, &irq);
683
684 // The pointer is kept alive by the QOM tree; drop the owned ref
685 dev.as_mut_ptr()
686 }
687
688 #[repr(C)]
689 #[derive(qemu_api_macros::Object)]
690 /// PL011 Luminary device model.
691 pub struct PL011Luminary {
692 parent_obj: ParentField<PL011State>,
693 }
694
695 qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object);
696
697 unsafe impl ObjectType for PL011Luminary {
698 type Class = <PL011State as ObjectType>::Class;
699 const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
700 }
701
702 impl ObjectImpl for PL011Luminary {
703 type ParentType = PL011State;
704
705 const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
706 }
707
708 impl PL011Impl for PL011Luminary {
709 const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]);
710 }
711
712 impl DeviceImpl for PL011Luminary {}
713 impl ResettablePhasesImpl for PL011Luminary {}
714 impl SysBusDeviceImpl for PL011Luminary {}
715