1#![no_std]
2extern crate alloc;
8
9use mbarrier::mb;
10use rdif_base::DriverGeneric;
11
12use crate::{registers::PmuRegs, variants::RockchipPmuInfo};
13use core::ptr::NonNull;
14
15mod registers;
16mod variants;
17
18pub use variants::PD;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub enum RkBoard {
22 Rk3588,
23 Rk3568,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum NpuError {
28 DomainNotFound,
30 Timeout,
32 HardwareError,
34}
35
36pub type NpuResult<T> = Result<T, NpuError>;
37
38pub struct RockchipPM {
39 _board: RkBoard,
40 reg: PmuRegs,
41 info: RockchipPmuInfo,
42}
43
44impl RockchipPM {
45 pub fn new(base: NonNull<u8>, board: RkBoard) -> Self {
46 Self {
47 _board: board,
48 info: RockchipPmuInfo::new(board),
49 reg: PmuRegs::new(base),
50 }
51 }
52
53 pub fn power_domain_on(&mut self, domain: PD) -> NpuResult<()> {
55 self.set_power_domain(domain, true)
56 }
57
58 pub fn power_domain_off(&mut self, domain: PD) -> NpuResult<()> {
60 self.set_power_domain(domain, false)
61 }
62
63 fn set_power_domain(&mut self, domain: PD, power_on: bool) -> NpuResult<()> {
65 let domain_info = self
66 .info
67 .domains
68 .get(&domain)
69 .ok_or(NpuError::DomainNotFound)?;
70
71 if domain_info.pwr_mask == 0 {
72 return Ok(());
73 }
74
75 self.write_power_control(&domain, power_on)?;
77
78 self.wait_power_domain_stable(&domain, power_on)?;
80
81 Ok(())
82 }
83
84 fn write_power_control(&mut self, domain: &PD, power_on: bool) -> NpuResult<()> {
86 let domain_info = self
87 .info
88 .domains
89 .get(domain)
90 .ok_or(NpuError::DomainNotFound)?;
91 let pwr_offset = self.info.pwr_offset + domain_info.pwr_offset;
92
93 if domain_info.pwr_w_mask != 0 {
94 let value = if power_on {
96 domain_info.pwr_w_mask
97 } else {
98 domain_info.pwr_mask | domain_info.pwr_w_mask
99 };
100 self.reg.write_u32(pwr_offset as usize, value as u32);
101 } else {
102 let current = self.reg.read_u32(pwr_offset as usize);
104 let new_value = if power_on {
105 current & !(domain_info.pwr_mask as u32)
106 } else {
107 current | (domain_info.pwr_mask as u32)
108 };
109 self.reg.write_u32(pwr_offset as usize, new_value);
110 }
111
112 mb();
113
114 Ok(())
115 }
116
117 fn wait_power_domain_stable(&self, domain: &PD, expected_on: bool) -> NpuResult<()> {
119 for _ in 0..10000 {
120 if self.is_domain_on(domain)? == expected_on {
121 return Ok(());
122 }
123 }
124 Err(NpuError::Timeout)
125 }
126
127 fn is_domain_on(&self, domain: &PD) -> NpuResult<bool> {
129 let domain_info = self
130 .info
131 .domains
132 .get(domain)
133 .ok_or(NpuError::DomainNotFound)?;
134
135 if domain_info.repair_status_mask != 0 {
136 let val = self.reg.read_u32(self.info.repair_status_offset as usize);
138 return Ok((val & (domain_info.repair_status_mask as u32)) != 0);
140 }
141
142 if domain_info.status_mask == 0 {
143 return Ok(!self.is_domain_idle(domain)?);
145 }
146
147 let val = self.reg.read_u32(self.info.status_offset as usize);
148 Ok((val & (domain_info.status_mask as u32)) == 0)
150 }
151
152 fn is_domain_idle(&self, domain: &PD) -> NpuResult<bool> {
154 let domain_info = self
155 .info
156 .domains
157 .get(domain)
158 .ok_or(NpuError::DomainNotFound)?;
159
160 let val = self.reg.read_u32(self.info.idle_offset as usize);
161 Ok((val & (domain_info.idle_mask as u32)) == (domain_info.idle_mask as u32))
162 }
163}
164
165impl DriverGeneric for RockchipPM {
166 fn open(&mut self) -> Result<(), rdif_base::KError> {
167 Ok(())
168 }
169
170 fn close(&mut self) -> Result<(), rdif_base::KError> {
171 Ok(())
172 }
173}