xref: /DragonOS/kernel/src/driver/clocksource/acpi_pm.rs (revision af097f9f4b317337fe74aaa5070c34a14b8635fd)
1dd8e74efSMingtao Huang use crate::{
2dd8e74efSMingtao Huang     alloc::string::ToString,
3dd8e74efSMingtao Huang     arch::{io::PortIOArch, CurrentPortIOArch},
4dd8e74efSMingtao Huang     driver::acpi::{
5dd8e74efSMingtao Huang         acpi_manager,
6dd8e74efSMingtao Huang         pmtmr::{ACPI_PM_MASK, PMTMR_TICKS_PER_SEC},
7dd8e74efSMingtao Huang     },
8dd8e74efSMingtao Huang     libs::spinlock::SpinLock,
9dd8e74efSMingtao Huang     time::{
10dd8e74efSMingtao Huang         clocksource::{Clocksource, ClocksourceData, ClocksourceFlags, ClocksourceMask, CycleNum},
11dd8e74efSMingtao Huang         PIT_TICK_RATE,
12dd8e74efSMingtao Huang     },
13dd8e74efSMingtao Huang };
14dd8e74efSMingtao Huang use acpi::fadt::Fadt;
15dd8e74efSMingtao Huang use alloc::sync::{Arc, Weak};
16dd8e74efSMingtao Huang use core::intrinsics::unlikely;
17dd8e74efSMingtao Huang use core::sync::atomic::{AtomicU32, Ordering};
182eab6dd7S曾俊 use log::info;
19dd8e74efSMingtao Huang use system_error::SystemError;
20dd8e74efSMingtao Huang 
21dd8e74efSMingtao Huang // 参考:https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/clocksource/acpi_pm.c
22dd8e74efSMingtao Huang 
23dd8e74efSMingtao Huang /// acpi_pmtmr所在的I/O端口
2492deae63SLoGin pub static PMTMR_IO_PORT: AtomicU32 = AtomicU32::new(0);
25dd8e74efSMingtao Huang 
26dd8e74efSMingtao Huang /// # 读取acpi_pmtmr当前值,并对齐进行掩码操作
27dd8e74efSMingtao Huang #[inline(always)]
read_pmtmr() -> u3228dd8e74efSMingtao Huang fn read_pmtmr() -> u32 {
29dd8e74efSMingtao Huang     return unsafe { CurrentPortIOArch::in32(PMTMR_IO_PORT.load(Ordering::SeqCst) as u16) }
30dd8e74efSMingtao Huang         & ACPI_PM_MASK as u32;
31dd8e74efSMingtao Huang }
32dd8e74efSMingtao Huang 
33dd8e74efSMingtao Huang //参考: https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/clocksource/acpi_pm.c#41
34dd8e74efSMingtao Huang /// # 读取acpi_pmtmr的值,并进行多次读取以保证获取正确的值
35dd8e74efSMingtao Huang ///
36dd8e74efSMingtao Huang /// ## 返回值
37dd8e74efSMingtao Huang /// - u32: 读取到的acpi_pmtmr值
38942cf26bSLoGin #[allow(dead_code)]
acpi_pm_read_verified() -> u3239dd8e74efSMingtao Huang pub fn acpi_pm_read_verified() -> u32 {
40dd8e74efSMingtao Huang     let mut v2: u32;
41dd8e74efSMingtao Huang 
42dd8e74efSMingtao Huang     // 因为某些损坏芯片组(如ICH4、PIIX4和PIIX4E)可能导致APCI PM时钟源未锁存
43dd8e74efSMingtao Huang     // 因此需要多次读取以保证获取正确的值
44dd8e74efSMingtao Huang     loop {
45dd8e74efSMingtao Huang         let v1 = read_pmtmr();
46dd8e74efSMingtao Huang         v2 = read_pmtmr();
47dd8e74efSMingtao Huang         let v3 = read_pmtmr();
48dd8e74efSMingtao Huang 
49dd8e74efSMingtao Huang         if !(unlikely((v2 > v3 || v1 < v3) && v1 > v2 || v1 < v3 && v2 > v3)) {
50dd8e74efSMingtao Huang             break;
51dd8e74efSMingtao Huang         }
52dd8e74efSMingtao Huang     }
53dd8e74efSMingtao Huang 
54dd8e74efSMingtao Huang     return v2;
55dd8e74efSMingtao Huang }
56dd8e74efSMingtao Huang 
57dd8e74efSMingtao Huang /// # 作为时钟源的读取函数
58dd8e74efSMingtao Huang ///
59dd8e74efSMingtao Huang /// ## 返回值
60dd8e74efSMingtao Huang /// - u64: acpi_pmtmr的当前值
acpi_pm_read() -> u6461dd8e74efSMingtao Huang fn acpi_pm_read() -> u64 {
62dd8e74efSMingtao Huang     return read_pmtmr() as u64;
63dd8e74efSMingtao Huang }
64dd8e74efSMingtao Huang 
65dd8e74efSMingtao Huang pub static mut CLOCKSOURCE_ACPI_PM: Option<Arc<Acpipm>> = None;
66dd8e74efSMingtao Huang 
clocksource_acpi_pm() -> Arc<Acpipm>67dd8e74efSMingtao Huang pub fn clocksource_acpi_pm() -> Arc<Acpipm> {
68dd8e74efSMingtao Huang     return unsafe { CLOCKSOURCE_ACPI_PM.as_ref().unwrap().clone() };
69dd8e74efSMingtao Huang }
70dd8e74efSMingtao Huang 
71dd8e74efSMingtao Huang #[derive(Debug)]
72dd8e74efSMingtao Huang pub struct Acpipm(SpinLock<InnerAcpipm>);
73dd8e74efSMingtao Huang 
74dd8e74efSMingtao Huang #[derive(Debug)]
75dd8e74efSMingtao Huang struct InnerAcpipm {
76dd8e74efSMingtao Huang     data: ClocksourceData,
77dd8e74efSMingtao Huang     self_reaf: Weak<Acpipm>,
78dd8e74efSMingtao Huang }
79dd8e74efSMingtao Huang 
80dd8e74efSMingtao Huang impl Acpipm {
new() -> Arc<Self>81dd8e74efSMingtao Huang     pub fn new() -> Arc<Self> {
82dd8e74efSMingtao Huang         let data = ClocksourceData {
83dd8e74efSMingtao Huang             name: "acpi_pm".to_string(),
84dd8e74efSMingtao Huang             rating: 200,
85dd8e74efSMingtao Huang             mask: ClocksourceMask::new(ACPI_PM_MASK),
86dd8e74efSMingtao Huang             mult: 0,
87dd8e74efSMingtao Huang             shift: 0,
88dd8e74efSMingtao Huang             max_idle_ns: Default::default(),
89dd8e74efSMingtao Huang             flags: ClocksourceFlags::CLOCK_SOURCE_IS_CONTINUOUS,
90dd8e74efSMingtao Huang             watchdog_last: CycleNum::new(0),
91*af097f9fS黄铭涛             cs_last: CycleNum::new(0),
92dd8e74efSMingtao Huang             uncertainty_margin: 0,
93dd8e74efSMingtao Huang             maxadj: 0,
94*af097f9fS黄铭涛             cycle_last: CycleNum::new(0),
95dd8e74efSMingtao Huang         };
96dd8e74efSMingtao Huang         let acpi_pm = Arc::new(Acpipm(SpinLock::new(InnerAcpipm {
97dd8e74efSMingtao Huang             data,
98dd8e74efSMingtao Huang             self_reaf: Default::default(),
99dd8e74efSMingtao Huang         })));
100dd8e74efSMingtao Huang         acpi_pm.0.lock().self_reaf = Arc::downgrade(&acpi_pm);
101dd8e74efSMingtao Huang 
102dd8e74efSMingtao Huang         return acpi_pm;
103dd8e74efSMingtao Huang     }
104dd8e74efSMingtao Huang }
105dd8e74efSMingtao Huang 
106dd8e74efSMingtao Huang impl Clocksource for Acpipm {
read(&self) -> CycleNum107dd8e74efSMingtao Huang     fn read(&self) -> CycleNum {
108dd8e74efSMingtao Huang         return CycleNum::new(acpi_pm_read());
109dd8e74efSMingtao Huang     }
110dd8e74efSMingtao Huang 
clocksource_data(&self) -> ClocksourceData111dd8e74efSMingtao Huang     fn clocksource_data(&self) -> ClocksourceData {
112dd8e74efSMingtao Huang         let inner = self.0.lock_irqsave();
113dd8e74efSMingtao Huang         return inner.data.clone();
114dd8e74efSMingtao Huang     }
115dd8e74efSMingtao Huang 
clocksource(&self) -> Arc<dyn Clocksource>116dd8e74efSMingtao Huang     fn clocksource(&self) -> Arc<dyn Clocksource> {
117dd8e74efSMingtao Huang         return self.0.lock_irqsave().self_reaf.upgrade().unwrap();
118dd8e74efSMingtao Huang     }
119dd8e74efSMingtao Huang 
update_clocksource_data(&self, data: ClocksourceData) -> Result<(), SystemError>120dd8e74efSMingtao Huang     fn update_clocksource_data(&self, data: ClocksourceData) -> Result<(), SystemError> {
121dd8e74efSMingtao Huang         let d = &mut self.0.lock_irqsave().data;
122dd8e74efSMingtao Huang         d.set_name(data.name);
123dd8e74efSMingtao Huang         d.set_rating(data.rating);
124*af097f9fS黄铭涛         d.set_mask(data.mask);
125*af097f9fS黄铭涛         d.set_mult(data.mult);
126dd8e74efSMingtao Huang         d.set_shift(data.shift);
127*af097f9fS黄铭涛         d.set_max_idle_ns(data.max_idle_ns);
128*af097f9fS黄铭涛         d.set_flags(data.flags);
129dd8e74efSMingtao Huang         d.watchdog_last = data.watchdog_last;
130*af097f9fS黄铭涛         d.cs_last = data.cs_last;
131*af097f9fS黄铭涛         d.set_uncertainty_margin(data.uncertainty_margin);
132*af097f9fS黄铭涛         d.set_maxadj(data.maxadj);
133*af097f9fS黄铭涛         d.cycle_last = data.cycle_last;
134dd8e74efSMingtao Huang         return Ok(());
135dd8e74efSMingtao Huang     }
136dd8e74efSMingtao Huang }
137dd8e74efSMingtao Huang 
138dd8e74efSMingtao Huang // 参考:https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/include/asm/mach_timer.h?fi=mach_prepare_counter
139dd8e74efSMingtao Huang #[allow(dead_code)]
140dd8e74efSMingtao Huang pub const CALIBRATE_TIME_MSEC: u64 = 30;
141dd8e74efSMingtao Huang pub const CALIBRATE_LATCH: u64 = (PIT_TICK_RATE * CALIBRATE_TIME_MSEC + 1000 / 2) / 1000;
142dd8e74efSMingtao Huang 
143dd8e74efSMingtao Huang #[inline(always)]
144dd8e74efSMingtao Huang #[allow(dead_code)]
mach_prepare_counter()145dd8e74efSMingtao Huang pub fn mach_prepare_counter() {
146dd8e74efSMingtao Huang     unsafe {
147dd8e74efSMingtao Huang         // 将Gate位设置为高电平,从而禁用扬声器
148dd8e74efSMingtao Huang         CurrentPortIOArch::out8(0x61, (CurrentPortIOArch::in8(0x61) & !0x02) | 0x01);
149dd8e74efSMingtao Huang 
150dd8e74efSMingtao Huang         // 针对计数器/定时器控制器的通道2进行配置,设置为模式0,二进制计数
151dd8e74efSMingtao Huang         CurrentPortIOArch::out8(0x43, 0xb0);
152dd8e74efSMingtao Huang         CurrentPortIOArch::out8(0x42, (CALIBRATE_LATCH & 0xff) as u8);
153dd8e74efSMingtao Huang         CurrentPortIOArch::out8(0x42, (CALIBRATE_LATCH >> 8) as u8);
154dd8e74efSMingtao Huang     }
155dd8e74efSMingtao Huang }
156dd8e74efSMingtao Huang 
157dd8e74efSMingtao Huang #[allow(dead_code)]
mach_countup(count: &mut u32)158dd8e74efSMingtao Huang pub fn mach_countup(count: &mut u32) {
159dd8e74efSMingtao Huang     let mut tmp: u32 = 0;
160dd8e74efSMingtao Huang     loop {
161dd8e74efSMingtao Huang         tmp += 1;
162dd8e74efSMingtao Huang         if (unsafe { CurrentPortIOArch::in8(0x61) } & 0x20) != 0 {
163dd8e74efSMingtao Huang             break;
164dd8e74efSMingtao Huang         }
165dd8e74efSMingtao Huang     }
166dd8e74efSMingtao Huang     *count = tmp;
167dd8e74efSMingtao Huang }
168dd8e74efSMingtao Huang 
169dd8e74efSMingtao Huang #[allow(dead_code)]
170dd8e74efSMingtao Huang const PMTMR_EXPECTED_RATE: u64 =
171dd8e74efSMingtao Huang     (CALIBRATE_LATCH * (PMTMR_TICKS_PER_SEC >> 10)) / (PIT_TICK_RATE >> 10);
172dd8e74efSMingtao Huang 
173dd8e74efSMingtao Huang /// # 验证ACPI PM Timer的运行速率是否在预期范围内(在x86_64架构以外的情况下验证)
174dd8e74efSMingtao Huang ///
175dd8e74efSMingtao Huang /// ## 返回值
176dd8e74efSMingtao Huang /// - i32:如果为0则表示在预期范围内,否则不在
177dd8e74efSMingtao Huang #[cfg(not(target_arch = "x86_64"))]
178942cf26bSLoGin #[allow(dead_code)]
verify_pmtmr_rate() -> bool179dd8e74efSMingtao Huang fn verify_pmtmr_rate() -> bool {
1802eab6dd7S曾俊     use log::info;
1812eab6dd7S曾俊 
182dd8e74efSMingtao Huang     let mut count: u32 = 0;
183dd8e74efSMingtao Huang 
184dd8e74efSMingtao Huang     mach_prepare_counter();
185dd8e74efSMingtao Huang     let value1 = clocksource_acpi_pm().read().data();
186dd8e74efSMingtao Huang     mach_countup(&mut count);
187dd8e74efSMingtao Huang     let value2 = clocksource_acpi_pm().read().data();
188dd8e74efSMingtao Huang     let delta = (value2 - value1) & ACPI_PM_MASK;
189dd8e74efSMingtao Huang 
190dd8e74efSMingtao Huang     if (delta < (PMTMR_EXPECTED_RATE * 19) / 20) || (delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
1912eab6dd7S曾俊         info!(
192dd8e74efSMingtao Huang             "PM Timer running at invalid rate: {}",
193dd8e74efSMingtao Huang             100 * delta / PMTMR_EXPECTED_RATE
194dd8e74efSMingtao Huang         );
195dd8e74efSMingtao Huang         return false;
196dd8e74efSMingtao Huang     }
197dd8e74efSMingtao Huang 
198dd8e74efSMingtao Huang     return true;
199dd8e74efSMingtao Huang }
200dd8e74efSMingtao Huang #[cfg(target_arch = "x86_64")]
verify_pmtmr_rate() -> bool201dd8e74efSMingtao Huang fn verify_pmtmr_rate() -> bool {
202dd8e74efSMingtao Huang     return true;
203dd8e74efSMingtao Huang }
204dd8e74efSMingtao Huang 
205dd8e74efSMingtao Huang const ACPI_PM_MONOTONIC_CHECKS: u32 = 10;
206dd8e74efSMingtao Huang const ACPI_PM_READ_CHECKS: u32 = 10000;
207dd8e74efSMingtao Huang 
208dd8e74efSMingtao Huang /// # 解析fadt
find_acpi_pm_clock() -> Result<(), SystemError>209dd8e74efSMingtao Huang fn find_acpi_pm_clock() -> Result<(), SystemError> {
210dd8e74efSMingtao Huang     let fadt = acpi_manager()
211dd8e74efSMingtao Huang         .tables()
212dd8e74efSMingtao Huang         .unwrap()
213dd8e74efSMingtao Huang         .find_table::<Fadt>()
214dd8e74efSMingtao Huang         .expect("failed to find FADT table");
215dd8e74efSMingtao Huang     let pm_timer_block = fadt.pm_timer_block().map_err(|_| SystemError::ENODEV)?;
216dd8e74efSMingtao Huang     let pm_timer_block = pm_timer_block.ok_or(SystemError::ENODEV)?;
217dd8e74efSMingtao Huang     let pmtmr_addr = pm_timer_block.address;
21892deae63SLoGin 
219dd8e74efSMingtao Huang     PMTMR_IO_PORT.store(pmtmr_addr as u32, Ordering::SeqCst);
22092deae63SLoGin 
2212eab6dd7S曾俊     info!(
22292deae63SLoGin         "apic_pmtmr I/O port: {}",
223dd8e74efSMingtao Huang         PMTMR_IO_PORT.load(Ordering::SeqCst)
22492deae63SLoGin     );
225dd8e74efSMingtao Huang 
226dd8e74efSMingtao Huang     return Ok(());
227dd8e74efSMingtao Huang }
228dd8e74efSMingtao Huang 
229dd8e74efSMingtao Huang /// # 初始化ACPI PM Timer作为系统时钟源
230dd8e74efSMingtao Huang // #[unified_init(INITCALL_FS)]
231942cf26bSLoGin #[inline(never)]
232942cf26bSLoGin #[allow(dead_code)]
init_acpi_pm_clocksource() -> Result<(), SystemError>233dd8e74efSMingtao Huang pub fn init_acpi_pm_clocksource() -> Result<(), SystemError> {
234dd8e74efSMingtao Huang     let acpi_pm = Acpipm::new();
235dd8e74efSMingtao Huang 
236dd8e74efSMingtao Huang     // 解析fadt
237dd8e74efSMingtao Huang     find_acpi_pm_clock()?;
238dd8e74efSMingtao Huang 
239dd8e74efSMingtao Huang     // 检查pmtmr_io_port是否被设置
24092deae63SLoGin     if PMTMR_IO_PORT.load(Ordering::SeqCst) == 0 {
241dd8e74efSMingtao Huang         return Err(SystemError::ENODEV);
242dd8e74efSMingtao Huang     }
243dd8e74efSMingtao Huang 
24492deae63SLoGin     unsafe {
24592deae63SLoGin         CLOCKSOURCE_ACPI_PM = Some(acpi_pm);
24692deae63SLoGin     }
24792deae63SLoGin 
248dd8e74efSMingtao Huang     // 验证ACPI PM Timer作为时钟源的稳定性和一致性
249dd8e74efSMingtao Huang     for j in 0..ACPI_PM_MONOTONIC_CHECKS {
250dd8e74efSMingtao Huang         let mut cnt = 100 * j;
251dd8e74efSMingtao Huang         while cnt > 0 {
252dd8e74efSMingtao Huang             cnt -= 1;
253dd8e74efSMingtao Huang         }
254dd8e74efSMingtao Huang 
255dd8e74efSMingtao Huang         let value1 = clocksource_acpi_pm().read().data();
256dd8e74efSMingtao Huang         let mut i = 0;
257dd8e74efSMingtao Huang         for _ in 0..ACPI_PM_READ_CHECKS {
258dd8e74efSMingtao Huang             let value2 = clocksource_acpi_pm().read().data();
259dd8e74efSMingtao Huang             if value2 == value1 {
260dd8e74efSMingtao Huang                 i += 1;
261dd8e74efSMingtao Huang                 continue;
262dd8e74efSMingtao Huang             }
263dd8e74efSMingtao Huang             if value2 > value1 {
264dd8e74efSMingtao Huang                 break;
265dd8e74efSMingtao Huang             }
266dd8e74efSMingtao Huang             if (value2 < value1) && (value2 < 0xfff) {
267dd8e74efSMingtao Huang                 break;
268dd8e74efSMingtao Huang             }
2692eab6dd7S曾俊             info!("PM Timer had inconsistens results: {} {}", value1, value2);
27092deae63SLoGin 
271dd8e74efSMingtao Huang             PMTMR_IO_PORT.store(0, Ordering::SeqCst);
27292deae63SLoGin 
273dd8e74efSMingtao Huang             return Err(SystemError::EINVAL);
274dd8e74efSMingtao Huang         }
275dd8e74efSMingtao Huang         if i == ACPI_PM_READ_CHECKS {
2762eab6dd7S曾俊             info!("PM Timer failed consistency check: {}", value1);
27792deae63SLoGin 
278dd8e74efSMingtao Huang             PMTMR_IO_PORT.store(0, Ordering::SeqCst);
27992deae63SLoGin 
280dd8e74efSMingtao Huang             return Err(SystemError::EINVAL);
281dd8e74efSMingtao Huang         }
282dd8e74efSMingtao Huang     }
283dd8e74efSMingtao Huang 
284dd8e74efSMingtao Huang     // 检查ACPI PM Timer的频率是否正确
285dd8e74efSMingtao Huang     if !verify_pmtmr_rate() {
286dd8e74efSMingtao Huang         PMTMR_IO_PORT.store(0, Ordering::SeqCst);
287dd8e74efSMingtao Huang     }
288dd8e74efSMingtao Huang 
289dd8e74efSMingtao Huang     // 检查TSC时钟源的监视器是否被禁用,如果被禁用则将时钟源的标志设置为CLOCK_SOURCE_MUST_VERIFY
290*af097f9fS黄铭涛     // 是因为jiffies精度小于acpi pm,所以不需要被jiffies监视
291dd8e74efSMingtao Huang     let tsc_clocksource_watchdog_disabled = false;
292dd8e74efSMingtao Huang     if tsc_clocksource_watchdog_disabled {
293dd8e74efSMingtao Huang         clocksource_acpi_pm().0.lock_irqsave().data.flags |=
294dd8e74efSMingtao Huang             ClocksourceFlags::CLOCK_SOURCE_MUST_VERIFY;
295dd8e74efSMingtao Huang     }
296dd8e74efSMingtao Huang 
297dd8e74efSMingtao Huang     // 注册ACPI PM Timer
298dd8e74efSMingtao Huang     let acpi_pmtmr = clocksource_acpi_pm() as Arc<dyn Clocksource>;
299*af097f9fS黄铭涛     match acpi_pmtmr.register(1, PMTMR_TICKS_PER_SEC as u32) {
300dd8e74efSMingtao Huang         Ok(_) => {
3012eab6dd7S曾俊             info!("ACPI PM Timer registered as clocksource sccessfully");
302dd8e74efSMingtao Huang             return Ok(());
303dd8e74efSMingtao Huang         }
304dd8e74efSMingtao Huang         Err(_) => {
3052eab6dd7S曾俊             info!("ACPI PM Timer init registered failed");
306dd8e74efSMingtao Huang             return Err(SystemError::ENOSYS);
307dd8e74efSMingtao Huang         }
308dd8e74efSMingtao Huang     };
309dd8e74efSMingtao Huang }
310