blob: 6a554ad79262bcff9d0950ccfcbe150d34cb9059 [file] [log] [blame]
// Copyright 2024, Linaro Limited
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
use core::{
ffi::{c_int, c_void},
ptr::NonNull,
};
use qemu_api::{
bindings::*, vmstate_clock, vmstate_fields, vmstate_int32, vmstate_subsections, vmstate_uint32,
vmstate_uint32_array, vmstate_unused, zeroable::Zeroable,
};
use crate::device::{PL011State, PL011_FIFO_DEPTH};
extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool {
unsafe {
debug_assert!(!opaque.is_null());
let state = NonNull::new_unchecked(opaque.cast::<PL011State>());
state.as_ref().migrate_clock
}
}
/// Migration subsection for [`PL011State`] clock.
pub static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription {
name: c"pl011/clock".as_ptr(),
version_id: 1,
minimum_version_id: 1,
needed: Some(pl011_clock_needed),
fields: vmstate_fields! {
vmstate_clock!(clock, PL011State),
},
..Zeroable::ZERO
};
extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
unsafe {
debug_assert!(!opaque.is_null());
let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
let result = state.as_mut().post_load(version_id as u32);
if result.is_err() {
-1
} else {
0
}
}
}
pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
name: c"pl011".as_ptr(),
version_id: 2,
minimum_version_id: 2,
post_load: Some(pl011_post_load),
fields: vmstate_fields! {
vmstate_unused!(core::mem::size_of::<u32>()),
vmstate_uint32!(flags, PL011State),
vmstate_uint32!(line_control, PL011State),
vmstate_uint32!(receive_status_error_clear, PL011State),
vmstate_uint32!(control, PL011State),
vmstate_uint32!(dmacr, PL011State),
vmstate_uint32!(int_enabled, PL011State),
vmstate_uint32!(int_level, PL011State),
vmstate_uint32_array!(read_fifo, PL011State, PL011_FIFO_DEPTH),
vmstate_uint32!(ilpr, PL011State),
vmstate_uint32!(ibrd, PL011State),
vmstate_uint32!(fbrd, PL011State),
vmstate_uint32!(ifl, PL011State),
vmstate_int32!(read_pos, PL011State),
vmstate_int32!(read_count, PL011State),
vmstate_int32!(read_trigger, PL011State),
},
subsections: vmstate_subsections! {
VMSTATE_PL011_CLOCK
},
..Zeroable::ZERO
};
qemu_api::declare_properties! {
PL011_PROPERTIES,
qemu_api::define_property!(
c"chardev",
PL011State,
char_backend,
unsafe { &qdev_prop_chr },
CharBackend
),
qemu_api::define_property!(
c"migrate-clk",
PL011State,
migrate_clock,
unsafe { &qdev_prop_bool },
bool,
default = true
),
}
qemu_api::device_class_init! {
pl011_class_init,
props => PL011_PROPERTIES,
realize_fn => Some(pl011_realize),
legacy_reset_fn => Some(pl011_reset),
vmsd => VMSTATE_PL011,
}
/// # Safety
///
/// We expect the FFI user of this function to pass a valid pointer, that has
/// the same size as [`PL011State`]. We also expect the device is
/// readable/writeable from one thread at any time.
pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) {
unsafe {
assert!(!dev.is_null());
let mut state = NonNull::new_unchecked(dev.cast::<PL011State>());
state.as_mut().realize();
}
}
/// # Safety
///
/// We expect the FFI user of this function to pass a valid pointer, that has
/// the same size as [`PL011State`]. We also expect the device is
/// readable/writeable from one thread at any time.
pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) {
unsafe {
assert!(!dev.is_null());
let mut state = NonNull::new_unchecked(dev.cast::<PL011State>());
state.as_mut().reset();
}
}