mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-12-25 04:14:06 +00:00
usb: add xhci registers and mmio space.
Adds xhci register definitions. BUG=chromium:831850 TEST=cargo test Change-Id: I9b5d1a66031d291eb5408f22b88d6e8a1ece6865 Reviewed-on: https://chromium-review.googlesource.com/1142437 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Tested-by: Jingkui Wang <jkwang@google.com> Reviewed-by: Dmitry Torokhov <dtor@chromium.org> Reviewed-by: Zach Reizner <zachr@chromium.org>
This commit is contained in:
parent
212b45f77b
commit
62affa7659
2 changed files with 485 additions and 0 deletions
|
@ -5,3 +5,6 @@
|
|||
#[macro_use]
|
||||
mod mmio_register;
|
||||
mod mmio_space;
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod xhci_regs;
|
||||
|
|
482
devices/src/usb/xhci/xhci_regs.rs
Normal file
482
devices/src/usb/xhci/xhci_regs.rs
Normal file
|
@ -0,0 +1,482 @@
|
|||
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
use super::mmio_register::{BarOffset, Register, RegisterSpec, StaticRegister, StaticRegisterSpec};
|
||||
use super::mmio_space::MMIOSpace;
|
||||
|
||||
/// Max interrupter number.
|
||||
pub const MAX_INTERRUPTER: u8 = 1;
|
||||
/// For port configuration, see register HCSPARAMS1, spcap1.3 and spcap2.3.
|
||||
pub const MAX_SLOTS: u8 = 8;
|
||||
/// Max port number.
|
||||
pub const MAX_PORTS: u8 = 8;
|
||||
|
||||
/// Cap register length.
|
||||
pub const XHCI_CAPLENGTH: u8 = 0x20;
|
||||
/// Offset for doorbell register.
|
||||
pub const XHCI_DBOFF: u32 = 0x00002000;
|
||||
/// Offset for RTs.
|
||||
pub const XHCI_RTSOFF: u32 = 0x00003000;
|
||||
|
||||
/// Bitmask for the usbcmd register, see spec 5.4.1.
|
||||
pub const USB_CMD_RUNSTOP: u32 = 1u32 << 0;
|
||||
/// Bitmask for the usbcmd register, see spec 5.4.1.
|
||||
pub const USB_CMD_RESET: u32 = 1u32 << 1;
|
||||
/// Bitmask for the usbcmd register, see spec 5.4.1.
|
||||
pub const USB_CMD_INTERRUPTER_ENABLE: u32 = 1u32 << 2;
|
||||
|
||||
/// Bitmask for the usbsts register, see spec 5.4.2.
|
||||
pub const USB_STS_HALTED: u32 = 1u32 << 0;
|
||||
/// Bitmask for the usbsts register, see spec 5.4.2.
|
||||
pub const USB_STS_EVENT_INTERRUPT: u32 = 1u32 << 3;
|
||||
/// Bitmask for the usbsts register, see spec 5.4.2.
|
||||
pub const USB_STS_PORT_CHANGE_DETECT: u32 = 1u32 << 4;
|
||||
/// Bitmask for the usbsts register, see spec 5.4.2.
|
||||
pub const USB_STS_CONTROLLER_NOT_READY: u32 = 1u32 << 11;
|
||||
/// Bitmask for the usbsts register, see spec 5.4.2.
|
||||
pub const USB_STS_SET_TO_CLEAR_MASK: u32 = 0x0000041C;
|
||||
|
||||
/// Bitmask for the crcr register, see spec 5.4.5.
|
||||
pub const CRCR_RING_CYCLE_STATE: u64 = 1u64 << 0;
|
||||
/// Bitmask for the crcr register, see spec 5.4.5.
|
||||
pub const CRCR_COMMAND_STOP: u64 = 1u64 << 1;
|
||||
/// Bitmask for the crcr register, see spec 5.4.5.
|
||||
pub const CRCR_COMMAND_ABORT: u64 = 1u64 << 2;
|
||||
/// Bitmask for the crcr register, see spec 5.4.5.
|
||||
pub const CRCR_COMMAND_RING_RUNNING: u64 = 1u64 << 3;
|
||||
/// Bitmask for the crcr register, see spec 5.4.5.
|
||||
pub const CRCR_COMMAND_RING_POINTER: u64 = 0xFFFFFFFFFFFFFFC0;
|
||||
|
||||
/// Bitmask for portsc register, see spec 5.4.8.
|
||||
pub const PORTSC_CURRENT_CONNECT_STATUS: u32 = 1u32 << 0;
|
||||
/// Bitmask for portsc register, see spec 5.4.8.
|
||||
pub const PORTSC_PORT_ENABLED: u32 = 1u32 << 1;
|
||||
/// Bitmask for portsc register, see spec 5.4.8.
|
||||
pub const PORTSC_PORT_RESET: u32 = 1u32 << 4;
|
||||
/// Bitmask for portsc register, see spec 5.4.8.
|
||||
pub const PORTSC_PORT_LINK_STATE_MASK: u32 = 0x000001E0;
|
||||
/// Bitmask for portsc register, see spec 5.4.8.
|
||||
pub const PORTSC_PORT_POWER: u32 = 1u32 << 9;
|
||||
/// Bitmask for portsc register, see spec 5.4.8.
|
||||
pub const PORTSC_CONNECT_STATUS_CHANGE: u32 = 1u32 << 17;
|
||||
/// Bitmask for portsc register, see spec 5.4.8.
|
||||
pub const PORTSC_PORT_ENABLED_DISABLED_CHANGE: u32 = 1u32 << 18;
|
||||
/// Bitmask for portsc register, see spec 5.4.8.
|
||||
pub const PORTSC_PORT_RESET_CHANGE: u32 = 1u32 << 21;
|
||||
/// Bitmask for portsc register, see spec 5.4.8.
|
||||
pub const PORTSC_WARM_PORT_RESET: u32 = 1u32 << 31;
|
||||
/// Bitmask for portsc register, see spec 5.4.8.
|
||||
pub const PORTSC_SET_TO_CLEAR_MASK: u32 = 0x00FE0002;
|
||||
|
||||
/// Bitmask for iman registers, see spec 5.5.2.1.
|
||||
pub const IMAN_INTERRUPT_PENDING: u32 = 1u32 << 0;
|
||||
/// Bitmask for iman registers, see spec 5.5.2.1.
|
||||
pub const IMAN_INTERRUPT_ENABLE: u32 = 1u32 << 1;
|
||||
/// Bitmask for iman registers, see spec 5.5.2.1.
|
||||
pub const IMAN_SET_TO_CLEAR_MASK: u32 = 0x00000001;
|
||||
|
||||
/// Bitmask for imod registers, see spec 5.5.2.2.
|
||||
pub const IMOD_INTERRUPT_MODERATION_INTERVAL: u32 = 0xFFFF;
|
||||
/// Bitmask for imod registers, see spec 5.5.2.2.
|
||||
pub const IMOD_INTERRUPT_MODERATION_COUNTER_OFFSET: u8 = 16;
|
||||
|
||||
/// Bitmask for erstsz registers, see 5.5.2.3.
|
||||
pub const ERSTSZ_SEGMENT_TABLE_SIZE: u32 = 0xFFFF;
|
||||
|
||||
/// Bitmask for erstba registers, see 5.5.2.3.
|
||||
pub const ERSTBA_SEGMENT_TABLE_BASE_ADDRESS: u64 = 0xFFFFFFFFFFFFFFC0;
|
||||
|
||||
/// Bitmask for erdp registers, see 5.5.2.3.
|
||||
pub const ERDP_EVENT_HANDLER_BUSY: u64 = 1u64 << 3;
|
||||
/// Bitmask for erdp registers, see 5.5.2.3.
|
||||
pub const ERDP_EVENT_RING_DEQUEUE_POINTER: u64 = 0xFFFFFFFFFFFFFFF0;
|
||||
/// Bitmask for erdp registers, see 5.5.2.3.
|
||||
pub const ERDP_SET_TO_CLEAR_MASK: u64 = 0x0000000000000008;
|
||||
|
||||
/// Bitmask for doorbell registers.
|
||||
pub const DOORBELL_TARGET: u32 = 0xFF;
|
||||
/// Offset of stream id.
|
||||
pub const DOORBELL_STREAM_ID_OFFSET: u32 = 16;
|
||||
|
||||
/// Bitmask for structural parameter registers.
|
||||
pub const HCSPARAMS1_MAX_INTERRUPTERS_MASK: u32 = 0x7FF00;
|
||||
/// Offset of max interrupters.
|
||||
pub const HCSPARAMS1_MAX_INTERRUPTERS_OFFSET: u32 = 8;
|
||||
/// Mask to get max slots.
|
||||
pub const HCSPARAMS1_MAX_SLOTS_MASK: u32 = 0xFF;
|
||||
|
||||
/// Bitmask for extended capabilities registers.
|
||||
pub const SPCAP_PORT_COUNT_MASK: u32 = 0xFF00;
|
||||
/// Offset of port count.
|
||||
pub const SPCAP_PORT_COUNT_OFFSET: u32 = 8;
|
||||
|
||||
/// Helper function for validating slot_id.
|
||||
pub fn valid_slot_id(slot_id: u8) -> bool {
|
||||
// slot id count from 1.
|
||||
slot_id > 0 && slot_id <= MAX_SLOTS
|
||||
}
|
||||
|
||||
/// XhciRegs hold all xhci registers.
|
||||
pub struct XhciRegs {
|
||||
pub usbcmd: Register<u32>,
|
||||
pub usbsts: Register<u32>,
|
||||
pub dnctrl: Register<u32>,
|
||||
pub crcr: Register<u64>,
|
||||
pub dcbaap: Register<u64>,
|
||||
pub config: Register<u64>,
|
||||
pub portsc: Vec<Register<u32>>,
|
||||
pub doorbells: Vec<Register<u32>>,
|
||||
pub iman: Register<u32>,
|
||||
pub imod: Register<u32>,
|
||||
pub erstsz: Register<u32>,
|
||||
pub erstba: Register<u64>,
|
||||
pub erdp: Register<u64>,
|
||||
}
|
||||
|
||||
/// This function returns mmio space definition for xhci. See Xhci spec chapter 5
|
||||
/// for details.
|
||||
pub fn init_xhci_mmio_space_and_regs() -> (MMIOSpace, XhciRegs) {
|
||||
let mut mmio = MMIOSpace::new();
|
||||
|
||||
/* Host Controller Capability Registers */
|
||||
mmio.add_register(
|
||||
// CAPLENGTH
|
||||
static_register!(
|
||||
ty: u8,
|
||||
offset: 0x00,
|
||||
value: XHCI_CAPLENGTH, // Operation register start at offset 0x20
|
||||
),
|
||||
);
|
||||
mmio.add_register(
|
||||
// HCIVERSION
|
||||
static_register!(
|
||||
ty: u16,
|
||||
offset: 0x02,
|
||||
value: 0x0110,// Revision 1.1
|
||||
),
|
||||
);
|
||||
mmio.add_register(
|
||||
// HCSPARAMS1
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0x04,
|
||||
value: 0x08000108, // max_slots = 8, max_interrupters = 1, max_ports = 8
|
||||
),
|
||||
);
|
||||
|
||||
mmio.add_register(
|
||||
// HCSPARAMS2
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0x08,
|
||||
// Maximum number of event ring segment table entries = 32k
|
||||
// No scratchpad buffers.
|
||||
value: 0xf0,
|
||||
),
|
||||
);
|
||||
|
||||
mmio.add_register(
|
||||
// HCSPARAM3
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0x0c,
|
||||
|
||||
// Exit latencies for U1 (standby with fast exit) and U2 (standby with
|
||||
// slower exit) power states. We use the max values:
|
||||
// - U1 to U0: < 10 us
|
||||
// - U2 to U1: < 2047 us
|
||||
value: 0x07FF000A,
|
||||
),
|
||||
);
|
||||
|
||||
mmio.add_register(
|
||||
// HCCPARAMS1
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0x10,
|
||||
// Supports 64 bit addressing
|
||||
// Max primary stream array size = 0 (streams not supported).
|
||||
// Extended capabilities pointer = 0xC000 offset from base.
|
||||
value: 0x30000501,
|
||||
),
|
||||
);
|
||||
mmio.add_register(
|
||||
// DBOFF
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0x14,
|
||||
value: XHCI_DBOFF, // Doorbell array offset 0x2000 from base.
|
||||
),
|
||||
);
|
||||
|
||||
mmio.add_register(
|
||||
// RTSOFF
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0x18,
|
||||
value: XHCI_RTSOFF, // Runtime registers offset 0x3000 from base.
|
||||
),
|
||||
);
|
||||
|
||||
mmio.add_register(
|
||||
// HCCPARAMS2
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0x1c,
|
||||
value: 0,
|
||||
),
|
||||
);
|
||||
/* End of Host Controller Capability Registers */
|
||||
|
||||
/* Host Controller Operational Registers */
|
||||
let usbcmd = register!(
|
||||
name: "usbcmd",
|
||||
ty: u32,
|
||||
offset: 0x20,
|
||||
reset_value: 0,
|
||||
guest_writeable_mask: 0x00002F0F,
|
||||
guest_write_1_to_clear_mask: 0,
|
||||
);
|
||||
mmio.add_register(usbcmd.clone());
|
||||
|
||||
let usbsts = register!(
|
||||
name: "usbsts",
|
||||
ty: u32,
|
||||
offset: 0x24,
|
||||
reset_value: 0x00000001,
|
||||
guest_writeable_mask: 0x0000041C,
|
||||
guest_write_1_to_clear_mask: 0x0000041C,
|
||||
);
|
||||
mmio.add_register(usbsts.clone());
|
||||
|
||||
mmio.add_register(
|
||||
// Pagesize
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0x28,
|
||||
value: 0x00000001,
|
||||
),
|
||||
);
|
||||
|
||||
let dnctrl = register!(
|
||||
name: "dnctrl",
|
||||
ty: u32,
|
||||
offset: 0x34,
|
||||
reset_value: 0,
|
||||
guest_writeable_mask: 0x0000FFFF,
|
||||
guest_write_1_to_clear_mask: 0,
|
||||
);
|
||||
mmio.add_register(dnctrl.clone());
|
||||
|
||||
let crcr = register!(
|
||||
name: "crcr",
|
||||
ty: u64,
|
||||
offset: 0x38,
|
||||
reset_value: 9,
|
||||
guest_writeable_mask: 0xFFFFFFFFFFFFFFC7,
|
||||
guest_write_1_to_clear_mask: 0,
|
||||
);
|
||||
mmio.add_register(crcr.clone());
|
||||
|
||||
let dcbaap = register!(
|
||||
name: "dcbaap",
|
||||
ty: u64,
|
||||
offset: 0x50,
|
||||
reset_value: 0x0,
|
||||
guest_writeable_mask: 0xFFFFFFFFFFFFFFC0,
|
||||
guest_write_1_to_clear_mask: 0,
|
||||
);
|
||||
mmio.add_register(dcbaap.clone());
|
||||
|
||||
let config = register!(
|
||||
name: "config",
|
||||
ty: u64,
|
||||
offset: 0x58,
|
||||
reset_value: 0,
|
||||
guest_writeable_mask: 0x0000003F,
|
||||
guest_write_1_to_clear_mask: 0,
|
||||
);
|
||||
mmio.add_register(config.clone());
|
||||
|
||||
let portsc = register_array!(
|
||||
name: "portsc",
|
||||
ty: u32,
|
||||
cnt: 8, // Must be equal to max_ports
|
||||
base_offset: 0x420,
|
||||
stride: 16,
|
||||
reset_value: 0x000002A0,
|
||||
guest_writeable_mask: 0x8EFFC3F2,
|
||||
guest_write_1_to_clear_mask: 0x00FE0002,);
|
||||
mmio.add_register_array(&portsc);
|
||||
|
||||
// Portpmsc.
|
||||
mmio.add_register_array(®ister_array!(
|
||||
name: "portpmsc",
|
||||
ty: u32,
|
||||
cnt: 8,
|
||||
base_offset: 0x424,
|
||||
stride: 16,
|
||||
reset_value: 0,
|
||||
guest_writeable_mask: 0x0001FFFF,
|
||||
guest_write_1_to_clear_mask: 0,));
|
||||
|
||||
// Portli
|
||||
mmio.add_register_array(®ister_array!(
|
||||
name: "portli",
|
||||
ty: u32,
|
||||
cnt: 8,
|
||||
base_offset: 0x428,
|
||||
stride: 16,
|
||||
reset_value: 0,
|
||||
guest_writeable_mask: 0,
|
||||
guest_write_1_to_clear_mask: 0,));
|
||||
|
||||
// Porthlpmc
|
||||
mmio.add_register_array(®ister_array!(
|
||||
name: "porthlpmc",
|
||||
ty: u32,
|
||||
cnt: 8,
|
||||
base_offset: 0x42c,
|
||||
stride: 16,
|
||||
reset_value: 0,
|
||||
guest_writeable_mask: 0x00003FFF,
|
||||
guest_write_1_to_clear_mask: 0,));
|
||||
|
||||
let doorbells = register_array!(
|
||||
name: "doorbell",
|
||||
ty: u32,
|
||||
cnt: 9, // Must be equal to max_ports
|
||||
base_offset: 0x2000,
|
||||
stride: 4,
|
||||
reset_value: 0,
|
||||
guest_writeable_mask: 0xFFFF00FF,
|
||||
guest_write_1_to_clear_mask: 0,);
|
||||
mmio.add_register_array(&doorbells);
|
||||
|
||||
/*Runtime Registers */
|
||||
|
||||
mmio.add_register(
|
||||
// mfindex
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0x3000,
|
||||
value: 0, // 4 ports starting at port 5
|
||||
),
|
||||
);
|
||||
|
||||
/* Reg Array for interrupters */
|
||||
// Although the following should be register arrays, we only have one interrupter.
|
||||
let iman = register!(
|
||||
name: "iman",
|
||||
ty: u32,
|
||||
offset: 0x3020,
|
||||
reset_value: 0,
|
||||
guest_writeable_mask: 0x00000003,
|
||||
guest_write_1_to_clear_mask: 0x00000001,);
|
||||
mmio.add_register(iman.clone());
|
||||
|
||||
let imod = register!(
|
||||
name: "imod",
|
||||
ty: u32,
|
||||
offset: 0x3024,
|
||||
reset_value: 0x00000FA0,
|
||||
guest_writeable_mask: 0xFFFFFFFF,
|
||||
guest_write_1_to_clear_mask: 0,);
|
||||
mmio.add_register(imod.clone());
|
||||
|
||||
let erstsz = register!(
|
||||
name: "erstsz",
|
||||
ty: u32,
|
||||
offset: 0x3028,
|
||||
reset_value: 0,
|
||||
guest_writeable_mask: 0x0000FFFF,
|
||||
guest_write_1_to_clear_mask: 0,);
|
||||
mmio.add_register(erstsz.clone());
|
||||
|
||||
let erstba = register!(
|
||||
name: "erstba",
|
||||
ty: u64,
|
||||
offset: 0x3030,
|
||||
reset_value: 0,
|
||||
guest_writeable_mask: 0xFFFFFFFFFFFFFFC0,
|
||||
guest_write_1_to_clear_mask: 0,);
|
||||
mmio.add_register(erstba.clone());
|
||||
|
||||
let erdp = register!(
|
||||
name: "erdp",
|
||||
ty: u64,
|
||||
offset: 0x3038,
|
||||
reset_value: 0,
|
||||
guest_writeable_mask: 0xFFFFFFFFFFFFFFFF,
|
||||
guest_write_1_to_clear_mask: 0x0000000000000008,);
|
||||
mmio.add_register(erdp.clone());
|
||||
|
||||
/* End of Runtime Registers */
|
||||
|
||||
let xhci_regs = XhciRegs {
|
||||
usbcmd,
|
||||
usbsts,
|
||||
dnctrl,
|
||||
crcr,
|
||||
dcbaap,
|
||||
config,
|
||||
portsc,
|
||||
doorbells,
|
||||
iman,
|
||||
imod,
|
||||
erstsz,
|
||||
erstba,
|
||||
erdp,
|
||||
};
|
||||
|
||||
/* End of Host Controller Operational Registers */
|
||||
|
||||
/* Extended Capability Registers */
|
||||
|
||||
// Extended capability registers. Base offset defined by hccparams1.
|
||||
// Each set of 4 registers represents a "Supported Protocol" extended
|
||||
// capability. The first capability indicates that ports 1-8 are USB 2.0. There is no USB 3.0
|
||||
// port for now. See xHCI spec 7.1 & 7.2 for more details.
|
||||
mmio.add_register(
|
||||
// spcap 1.1
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0xc000,
|
||||
// "Supported Protocol" capability.
|
||||
// Not next capability.
|
||||
// USB 2.0. Revision 2.0.
|
||||
value: 0x02000002,
|
||||
),
|
||||
);
|
||||
mmio.add_register(
|
||||
// spcap 1.2
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0xc004,
|
||||
value: 0x20425355, // Name string = "USB "
|
||||
),
|
||||
);
|
||||
mmio.add_register(
|
||||
// spcap 1.3
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0xc008,
|
||||
value: 0x00000801, // 4 ports starting at port 1.
|
||||
),
|
||||
);
|
||||
|
||||
mmio.add_register(
|
||||
// spcap 1.4
|
||||
static_register!(
|
||||
ty: u32,
|
||||
offset: 0xc00c,
|
||||
// The specification says that this shall be set to 0.
|
||||
// Section 7.2.2.1.4.
|
||||
value: 0,
|
||||
),
|
||||
);
|
||||
/* End of Host Controller Operational Registers */
|
||||
|
||||
(mmio, xhci_regs)
|
||||
}
|
Loading…
Reference in a new issue