Skip to main content

strat9_bus_drivers/
hisi_lpc.rs

1use crate::{BusChild, BusDriver, BusError, PowerState, mmio::MmioRegion};
2use alloc::{string::String, vec::Vec};
3
4const LPC_REG_STARTUP_SIGNAL: usize = 0x00;
5const LPC_REG_OP_STATUS: usize = 0x04;
6const LPC_REG_OP_LEN: usize = 0x10;
7const LPC_REG_CMD: usize = 0x14;
8const LPC_REG_ADDR: usize = 0x20;
9const LPC_REG_WDATA: usize = 0x24;
10const LPC_REG_RDATA: usize = 0x28;
11
12const STARTUP_SIGNAL_START: u32 = 1 << 0;
13const OP_STATUS_IDLE: u32 = 1 << 0;
14const OP_STATUS_FINISHED: u32 = 1 << 1;
15const CMD_OP_READ: u32 = 0;
16const CMD_OP_WRITE: u32 = 1 << 0;
17const CMD_SAMEADDR: u32 = 1 << 3;
18
19const LPC_MAX_DWIDTH: u32 = 4;
20const LPC_PEROP_WAITCNT: u32 = 100;
21const LPC_MAX_WAITCNT: u32 = 1300;
22
23const COMPATIBLE: &[&str] = &["hisilicon,hip06-lpc", "hisilicon,hip07-lpc"];
24
25pub struct HisiLpc {
26    regs: MmioRegion,
27    power_state: PowerState,
28    children: Vec<BusChild>,
29}
30
31impl HisiLpc {
32    /// Creates a new instance.
33    pub fn new() -> Self {
34        Self {
35            regs: MmioRegion::new(),
36            power_state: PowerState::Off,
37            children: Vec::new(),
38        }
39    }
40
41    /// Performs the wait idle operation.
42    fn wait_idle(&self) -> Result<(), BusError> {
43        for _ in 0..LPC_MAX_WAITCNT {
44            let status = self.regs.read32(LPC_REG_OP_STATUS);
45            if status & OP_STATUS_IDLE != 0 {
46                return Ok(());
47            }
48        }
49        Err(BusError::Timeout)
50    }
51
52    /// Performs the wait finish operation.
53    fn wait_finish(&self) -> Result<(), BusError> {
54        for _ in 0..LPC_MAX_WAITCNT {
55            let status = self.regs.read32(LPC_REG_OP_STATUS);
56            if status & OP_STATUS_FINISHED != 0 {
57                return Ok(());
58            }
59        }
60        Err(BusError::Timeout)
61    }
62
63    /// Performs the lpc read operation.
64    pub fn lpc_read(&self, addr: u32, width: u32) -> Result<u32, BusError> {
65        if width == 0 || width > LPC_MAX_DWIDTH {
66            return Err(BusError::InvalidArgument);
67        }
68
69        self.wait_idle()?;
70
71        self.regs.write32(LPC_REG_ADDR, addr);
72        self.regs.write32(LPC_REG_CMD, CMD_OP_READ);
73        self.regs.write32(LPC_REG_OP_LEN, width);
74        self.regs
75            .write32(LPC_REG_STARTUP_SIGNAL, STARTUP_SIGNAL_START);
76
77        self.wait_finish()?;
78
79        let mut result = 0u32;
80        for i in 0..width {
81            let byte = self.regs.read32(LPC_REG_RDATA) & 0xFF;
82            result |= byte << (i * 8);
83        }
84
85        Ok(result)
86    }
87
88    /// Performs the lpc write operation.
89    pub fn lpc_write(&self, addr: u32, width: u32, data: u32) -> Result<(), BusError> {
90        if width == 0 || width > LPC_MAX_DWIDTH {
91            return Err(BusError::InvalidArgument);
92        }
93
94        self.wait_idle()?;
95
96        self.regs.write32(LPC_REG_ADDR, addr);
97        self.regs.write32(LPC_REG_CMD, CMD_OP_WRITE);
98        self.regs.write32(LPC_REG_OP_LEN, width);
99
100        for i in 0..width {
101            self.regs.write32(LPC_REG_WDATA, (data >> (i * 8)) & 0xFF);
102        }
103
104        self.regs
105            .write32(LPC_REG_STARTUP_SIGNAL, STARTUP_SIGNAL_START);
106
107        self.wait_finish()?;
108
109        Ok(())
110    }
111
112    /// Performs the io read8 operation.
113    pub fn io_read8(&self, port: u16) -> Result<u8, BusError> {
114        self.lpc_read(port as u32, 1).map(|v| v as u8)
115    }
116
117    /// Performs the io write8 operation.
118    pub fn io_write8(&self, port: u16, val: u8) -> Result<(), BusError> {
119        self.lpc_write(port as u32, 1, val as u32)
120    }
121
122    /// Performs the io read16 operation.
123    pub fn io_read16(&self, port: u16) -> Result<u16, BusError> {
124        self.lpc_read(port as u32, 2).map(|v| v as u16)
125    }
126
127    /// Performs the io write16 operation.
128    pub fn io_write16(&self, port: u16, val: u16) -> Result<(), BusError> {
129        self.lpc_write(port as u32, 2, val as u32)
130    }
131
132    /// Performs the io read32 operation.
133    pub fn io_read32(&self, port: u16) -> Result<u32, BusError> {
134        self.lpc_read(port as u32, 4)
135    }
136
137    /// Performs the io write32 operation.
138    pub fn io_write32(&self, port: u16, val: u32) -> Result<(), BusError> {
139        self.lpc_write(port as u32, 4, val)
140    }
141
142    /// Performs the add child operation.
143    pub fn add_child(&mut self, child: BusChild) {
144        self.children.push(child);
145    }
146}
147
148impl BusDriver for HisiLpc {
149    /// Performs the name operation.
150    fn name(&self) -> &str {
151        "hisi-lpc"
152    }
153
154    /// Performs the compatible operation.
155    fn compatible(&self) -> &[&str] {
156        COMPATIBLE
157    }
158
159    /// Performs the init operation.
160    fn init(&mut self, base: usize) -> Result<(), BusError> {
161        self.regs.init(base, 0x30);
162        self.wait_idle()?;
163        self.power_state = PowerState::On;
164        Ok(())
165    }
166
167    /// Performs the shutdown operation.
168    fn shutdown(&mut self) -> Result<(), BusError> {
169        self.power_state = PowerState::Off;
170        Ok(())
171    }
172
173    /// Reads reg.
174    fn read_reg(&self, offset: usize) -> Result<u32, BusError> {
175        if !self.regs.is_valid() {
176            return Err(BusError::InitFailed);
177        }
178        Ok(self.regs.read32(offset))
179    }
180
181    /// Writes reg.
182    fn write_reg(&mut self, offset: usize, value: u32) -> Result<(), BusError> {
183        if !self.regs.is_valid() {
184            return Err(BusError::InitFailed);
185        }
186        self.regs.write32(offset, value);
187        Ok(())
188    }
189
190    /// Performs the children operation.
191    fn children(&self) -> Vec<BusChild> {
192        self.children.clone()
193    }
194}