// Copyright 2024, Linaro Limited // Author(s): Manos Pitsidianakis // 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::()); 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::()); 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::()), 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::()); 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::()); state.as_mut().reset(); } }