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::PowerDomain;
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 get_power_dowain_by_name(&self, name: &str) -> Option<PowerDomain> {
54 for (domain, info) in &self.info.domains {
55 if info.name == name {
56 return Some(*domain);
57 }
58 }
59 None
60 }
61
62 pub fn power_domain_on(&mut self, domain: PowerDomain) -> NpuResult<()> {
64 self.set_power_domain(domain, true)
65 }
66
67 pub fn power_domain_off(&mut self, domain: PowerDomain) -> NpuResult<()> {
69 self.set_power_domain(domain, false)
70 }
71
72 fn set_power_domain(&mut self, domain: PowerDomain, power_on: bool) -> NpuResult<()> {
74 let domain_info = self
75 .info
76 .domains
77 .get(&domain)
78 .ok_or(NpuError::DomainNotFound)?;
79
80 if domain_info.pwr_mask == 0 {
81 return Ok(());
82 }
83
84 self.write_power_control(&domain, power_on)?;
86
87 self.wait_power_domain_stable(&domain, power_on)?;
89
90 Ok(())
91 }
92
93 fn write_power_control(&mut self, domain: &PowerDomain, power_on: bool) -> NpuResult<()> {
95 let domain_info = self
96 .info
97 .domains
98 .get(domain)
99 .ok_or(NpuError::DomainNotFound)?;
100 let pwr_offset = self.info.pwr_offset + domain_info.pwr_offset;
101
102 if domain_info.pwr_w_mask != 0 {
103 let value = if power_on {
105 domain_info.pwr_w_mask
106 } else {
107 domain_info.pwr_mask | domain_info.pwr_w_mask
108 };
109 self.reg.write_u32(pwr_offset as usize, value as u32);
110 } else {
111 let current = self.reg.read_u32(pwr_offset as usize);
113 let new_value = if power_on {
114 current & !(domain_info.pwr_mask as u32)
115 } else {
116 current | (domain_info.pwr_mask as u32)
117 };
118 self.reg.write_u32(pwr_offset as usize, new_value);
119 }
120
121 mb();
122
123 Ok(())
124 }
125
126 fn wait_power_domain_stable(&self, domain: &PowerDomain, expected_on: bool) -> NpuResult<()> {
128 for _ in 0..10000 {
129 if self.is_domain_on(domain)? == expected_on {
130 return Ok(());
131 }
132 }
133 Err(NpuError::Timeout)
134 }
135
136 fn is_domain_on(&self, domain: &PowerDomain) -> NpuResult<bool> {
138 let domain_info = self
139 .info
140 .domains
141 .get(domain)
142 .ok_or(NpuError::DomainNotFound)?;
143
144 if domain_info.repair_status_mask != 0 {
145 let val = self.reg.read_u32(self.info.repair_status_offset as usize);
147 return Ok((val & (domain_info.repair_status_mask as u32)) != 0);
149 }
150
151 if domain_info.status_mask == 0 {
152 return Ok(!self.is_domain_idle(domain)?);
154 }
155
156 let val = self.reg.read_u32(self.info.status_offset as usize);
157 Ok((val & (domain_info.status_mask as u32)) == 0)
159 }
160
161 fn is_domain_idle(&self, domain: &PowerDomain) -> NpuResult<bool> {
163 let domain_info = self
164 .info
165 .domains
166 .get(domain)
167 .ok_or(NpuError::DomainNotFound)?;
168
169 let val = self.reg.read_u32(self.info.idle_offset as usize);
170 Ok((val & (domain_info.idle_mask as u32)) == (domain_info.idle_mask as u32))
171 }
172}
173
174impl DriverGeneric for RockchipPM {
175 fn open(&mut self) -> Result<(), rdif_base::KError> {
176 Ok(())
177 }
178
179 fn close(&mut self) -> Result<(), rdif_base::KError> {
180 Ok(())
181 }
182}