zynq7000_hal/
lib.rs

1//! # HAL for the AMD Zynq 7000 SoC family
2//!
3//! This repository contains the **H**ardware **A**bstraction **L**ayer (HAL), which is an additional
4//! hardware abstraction on top of the [peripheral access API](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000).
5//!
6//! It is the result of reading the datasheet for the device and encoding a type-safe layer over the
7//! raw PAC. This crate also implements traits specified by the
8//! [embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
9//! various drivers in the embedded rust ecosystem.
10//!
11//! ## Examples
12//!
13//! All exaples can be found inside the [examples folder](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples)
14//! of the project
15#![no_std]
16#![cfg_attr(docsrs, feature(doc_cfg))]
17
18#[cfg(feature = "alloc")]
19extern crate alloc;
20
21use slcr::Slcr;
22use zynq7000::{
23    SpiClockPhase, SpiClockPolarity,
24    slcr::{BootModeRegister, BootPllConfig, LevelShifterRegister},
25};
26
27pub mod cache;
28pub mod clocks;
29pub mod ddr;
30pub mod devcfg;
31pub mod eth;
32pub mod gic;
33pub mod gpio;
34pub mod gtc;
35pub mod i2c;
36pub mod l2_cache;
37pub mod log;
38pub mod prelude;
39pub mod priv_tim;
40pub mod qspi;
41pub mod slcr;
42pub mod spi;
43pub mod time;
44pub mod ttc;
45pub mod uart;
46
47pub use zynq7000 as pac;
48pub use zynq7000::slcr::LevelShifterConfig;
49
50#[derive(Debug, thiserror::Error)]
51pub enum InitError {
52    #[error("peripheral singleton was already taken")]
53    PeripheralsAlreadyTaken,
54}
55
56#[derive(Debug)]
57pub enum InteruptConfig {
58    /// GIC is configured to route all interrupts to CPU0. Suitable if the software handles all
59    /// the interrupts and only runs on CPU0.
60    AllInterruptsToCpu0,
61}
62
63#[derive(Debug)]
64pub struct Config {
65    pub init_l2_cache: bool,
66    /// If this has some value, it will configure the level shifter between PS and PL.
67    pub level_shifter_config: Option<LevelShifterConfig>,
68    /// If this has some value, it configures the GIC to pre-defined settings.
69    pub interrupt_config: Option<InteruptConfig>,
70}
71
72/// Utility function to perform common initialization steps.
73pub fn init(config: Config) -> Result<zynq7000::Peripherals, InitError> {
74    let mut periphs = zynq7000::Peripherals::take().ok_or(InitError::PeripheralsAlreadyTaken)?;
75    if config.init_l2_cache {
76        l2_cache::init_with_defaults(&mut periphs.l2c);
77    }
78    if let Some(config) = config.level_shifter_config {
79        configure_level_shifter(config);
80    }
81    if let Some(interrupt_config) = config.interrupt_config {
82        let mut gic = gic::GicConfigurator::new_with_init(periphs.gicc, periphs.gicd);
83        match interrupt_config {
84            InteruptConfig::AllInterruptsToCpu0 => {
85                gic.enable_all_interrupts();
86                gic.set_all_spi_interrupt_targets_cpu0();
87            }
88        }
89        gic.enable();
90        unsafe {
91            gic.enable_interrupts();
92        }
93    }
94
95    Ok(unsafe { zynq7000::Peripherals::steal() })
96}
97
98/// This enumeration encodes the various boot sources.
99#[derive(Debug, Copy, Clone)]
100pub enum BootDevice {
101    JtagCascaded,
102    JtagIndependent,
103    Nor,
104    Nand,
105    Qspi,
106    SdCard,
107}
108
109#[derive(Debug, Copy, Clone)]
110pub struct BootMode {
111    boot_mode: Option<BootDevice>,
112    pll_config: BootPllConfig,
113}
114
115impl BootMode {
116    /// Create a new boot mode information structure by reading the boot mode register from the
117    /// fixed SLCR block.
118    pub fn new_from_regs() -> Self {
119        // Safety: Only read a read-only register here.
120        Self::new_with_reg(unsafe { zynq7000::slcr::Slcr::new_mmio_fixed() }.read_boot_mode())
121    }
122
123    fn new_with_reg(boot_mode_reg: BootModeRegister) -> Self {
124        let boot_dev = boot_mode_reg.boot_mode();
125        let msb_three_bits = (boot_dev.value() >> 1) & 0b111;
126
127        let boot_mode = match msb_three_bits {
128            0b000 => {
129                if boot_dev.value() & 0b1 == 0 {
130                    Some(BootDevice::JtagCascaded)
131                } else {
132                    Some(BootDevice::JtagIndependent)
133                }
134            }
135            0b001 => Some(BootDevice::Nor),
136            0b010 => Some(BootDevice::Nand),
137            0b100 => Some(BootDevice::Qspi),
138            0b110 => Some(BootDevice::SdCard),
139            _ => None,
140        };
141        Self {
142            boot_mode,
143            pll_config: boot_mode_reg.pll_config(),
144        }
145    }
146
147    pub const fn boot_device(&self) -> Option<BootDevice> {
148        self.boot_mode
149    }
150
151    pub const fn pll_config(&self) -> BootPllConfig {
152        self.pll_config
153    }
154}
155
156/// This configures the level shifters between the programmable logic (PL) and the processing
157/// system (PS).
158///
159/// The Zynq-7000 TRM p.32 specifies more information about this register and how to use it.
160pub fn configure_level_shifter(config: LevelShifterConfig) {
161    // Safety: We only manipulate the level shift registers.
162    unsafe {
163        Slcr::with(|slcr_unlocked| {
164            slcr_unlocked
165                .write_lvl_shftr_en(LevelShifterRegister::new_with_raw_value(config as u32));
166        });
167    }
168}
169
170#[derive(Debug, PartialEq, Eq, Clone, Copy)]
171pub enum PeriphSelect {
172    Smc = 24,
173    Lqspi = 23,
174    Gpio = 22,
175    Uart1 = 21,
176    Uart0 = 20,
177    I2c1 = 19,
178    I2c0 = 18,
179    Can1 = 17,
180    Can0 = 16,
181    Spi1 = 15,
182    Spi0 = 14,
183    Sdio1 = 11,
184    Sdio0 = 10,
185    Gem1 = 7,
186    Gem0 = 6,
187    Usb1 = 3,
188    Usb0 = 2,
189    Dma = 0,
190}
191
192/// Enable the AMBA peripheral clock, which is required to read the registers of a peripheral
193/// block.
194#[inline]
195pub fn enable_amba_peripheral_clock(select: PeriphSelect) {
196    unsafe {
197        Slcr::with(|regs| {
198            regs.clk_ctrl().modify_aper_clk_ctrl(|mut val| {
199                match select {
200                    PeriphSelect::Smc => val.set_smc_1x_clk_act(true),
201                    PeriphSelect::Lqspi => val.set_lqspi_1x_clk_act(true),
202                    PeriphSelect::Gpio => val.set_gpio_1x_clk_act(true),
203                    PeriphSelect::Uart1 => val.set_uart_1_1x_clk_act(true),
204                    PeriphSelect::Uart0 => val.set_uart_0_1x_clk_act(true),
205                    PeriphSelect::I2c1 => val.set_i2c_1_1x_clk_act(true),
206                    PeriphSelect::I2c0 => val.set_i2c_0_1x_clk_act(true),
207                    PeriphSelect::Can1 => val.set_can_1_1x_clk_act(true),
208                    PeriphSelect::Can0 => val.set_can_0_1x_clk_act(true),
209                    PeriphSelect::Spi1 => val.set_spi_1_1x_clk_act(true),
210                    PeriphSelect::Spi0 => val.set_spi_1_1x_clk_act(true),
211                    PeriphSelect::Sdio1 => val.set_sdio_1_1x_clk_act(true),
212                    PeriphSelect::Sdio0 => val.set_sdio_0_1x_clk_act(true),
213                    PeriphSelect::Gem1 => val.set_gem_1_1x_clk_act(true),
214                    PeriphSelect::Gem0 => val.set_gem_0_1x_clk_act(true),
215                    PeriphSelect::Usb1 => val.set_usb_1_cpu_1x_clk_act(true),
216                    PeriphSelect::Usb0 => val.set_usb_0_cpu_1x_clk_act(true),
217                    PeriphSelect::Dma => val.set_dma_cpu_2x_clk_act(true),
218                }
219                val
220            })
221        });
222    }
223}
224
225/// Disable the AMBA peripheral clock, which is required to read the registers of a peripheral
226/// block.
227#[inline]
228pub fn disable_amba_periph_clk(select: PeriphSelect) {
229    unsafe {
230        Slcr::with(|regs| {
231            regs.clk_ctrl().modify_aper_clk_ctrl(|mut val| {
232                match select {
233                    PeriphSelect::Smc => val.set_smc_1x_clk_act(false),
234                    PeriphSelect::Lqspi => val.set_lqspi_1x_clk_act(false),
235                    PeriphSelect::Gpio => val.set_gpio_1x_clk_act(false),
236                    PeriphSelect::Uart1 => val.set_uart_1_1x_clk_act(false),
237                    PeriphSelect::Uart0 => val.set_uart_0_1x_clk_act(false),
238                    PeriphSelect::I2c1 => val.set_i2c_1_1x_clk_act(false),
239                    PeriphSelect::I2c0 => val.set_i2c_0_1x_clk_act(false),
240                    PeriphSelect::Can1 => val.set_can_1_1x_clk_act(false),
241                    PeriphSelect::Can0 => val.set_can_0_1x_clk_act(false),
242                    PeriphSelect::Spi1 => val.set_spi_1_1x_clk_act(false),
243                    PeriphSelect::Spi0 => val.set_spi_1_1x_clk_act(false),
244                    PeriphSelect::Sdio1 => val.set_sdio_1_1x_clk_act(false),
245                    PeriphSelect::Sdio0 => val.set_sdio_0_1x_clk_act(false),
246                    PeriphSelect::Gem1 => val.set_gem_1_1x_clk_act(false),
247                    PeriphSelect::Gem0 => val.set_gem_0_1x_clk_act(false),
248                    PeriphSelect::Usb1 => val.set_usb_1_cpu_1x_clk_act(false),
249                    PeriphSelect::Usb0 => val.set_usb_0_cpu_1x_clk_act(false),
250                    PeriphSelect::Dma => val.set_dma_cpu_2x_clk_act(false),
251                }
252                val
253            })
254        });
255    }
256}
257
258#[inline]
259const fn spi_mode_const_to_cpol_cpha(
260    mode: embedded_hal::spi::Mode,
261) -> (SpiClockPolarity, SpiClockPhase) {
262    match mode {
263        embedded_hal::spi::MODE_0 => (
264            SpiClockPolarity::QuiescentLow,
265            SpiClockPhase::ActiveOutsideOfWord,
266        ),
267        embedded_hal::spi::MODE_1 => (
268            SpiClockPolarity::QuiescentLow,
269            SpiClockPhase::InactiveOutsideOfWord,
270        ),
271        embedded_hal::spi::MODE_2 => (
272            SpiClockPolarity::QuiescentHigh,
273            SpiClockPhase::ActiveOutsideOfWord,
274        ),
275        embedded_hal::spi::MODE_3 => (
276            SpiClockPolarity::QuiescentHigh,
277            SpiClockPhase::InactiveOutsideOfWord,
278        ),
279    }
280}
281
282pub(crate) mod sealed {
283    pub trait Sealed {}
284}