esp_hal/soc/esp32c2/efuse/
mod.rs

1//! # Reading of eFuses (ESP32-C2)
2//!
3//! ## Overview
4//! The `efuse` module provides functionality for reading eFuse data
5//! from the `ESP32-C2` chip, allowing access to various chip-specific
6//! information such as:
7//!
8//!   * MAC address
9//!   * ADC calibration information
10//!
11//! and more. It is useful for retrieving chip-specific configuration and
12//! identification data during runtime.
13//!
14//! The `Efuse` struct represents the eFuse peripheral and is responsible for
15//! reading various eFuse fields and values.
16//!
17//! ## Examples
18//!
19//! ### Read data from the eFuse storage.
20//!
21//! ```rust, no_run
22#![doc = crate::before_snippet!()]
23//! # use esp_hal::efuse::Efuse;
24//!
25//! let mac_address = Efuse::read_base_mac_address();
26//!
27//! println!(
28//!     "MAC: {:#X}:{:#X}:{:#X}:{:#X}:{:#X}:{:#X}",
29//!     mac_address[0],
30//!     mac_address[1],
31//!     mac_address[2],
32//!     mac_address[3],
33//!     mac_address[4],
34//!     mac_address[5]
35//! );
36//!
37//! println!("MAC address {:02x?}", Efuse::mac_address());
38//! println!("Flash Encryption {:?}", Efuse::flash_encryption());
39//! # Ok(())
40//! # }
41//! ```
42
43pub use self::fields::*;
44use crate::{analog::adc::Attenuation, peripherals::EFUSE};
45
46mod fields;
47
48/// A struct representing the eFuse functionality of the chip.
49pub struct Efuse;
50
51impl Efuse {
52    /// Reads chip's MAC address from the eFuse storage.
53    pub fn read_base_mac_address() -> [u8; 6] {
54        Self::read_field_be(MAC)
55    }
56
57    /// Get status of SPI boot encryption.
58    pub fn flash_encryption() -> bool {
59        (Self::read_field_le::<u8>(SPI_BOOT_CRYPT_CNT).count_ones() % 2) != 0
60    }
61
62    /// Get the multiplier for the timeout value of the RWDT STAGE 0 register.
63    pub fn rwdt_multiplier() -> u8 {
64        Self::read_field_le::<u8>(WDT_DELAY_SEL)
65    }
66
67    /// Get efuse block version
68    ///
69    /// see <https://github.com/espressif/esp-idf/blob/dc016f5987/components/hal/efuse_hal.c#L27-L30>
70    pub fn block_version() -> (u8, u8) {
71        // see <https://github.com/espressif/esp-idf/blob/dc016f5987/components/hal/esp32c2/include/hal/efuse_ll.h#L65-L73>
72        // <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_table.csv#L90-L91>
73        (
74            Self::read_field_le::<u8>(BLK_VERSION_MAJOR),
75            Self::read_field_le::<u8>(BLK_VERSION_MINOR),
76        )
77    }
78
79    /// Get version of RTC calibration block
80    ///
81    /// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_rtc_calib.c#L14>
82    pub fn rtc_calib_version() -> u8 {
83        let (major, _minor) = Self::block_version();
84        if major == 0 {
85            1
86        } else {
87            0
88        }
89    }
90
91    /// Get ADC initial code for specified attenuation from efuse
92    ///
93    /// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_rtc_calib.c#L27>
94    pub fn rtc_calib_init_code(_unit: u8, atten: Attenuation) -> Option<u16> {
95        let version = Self::rtc_calib_version();
96
97        if version != 1 {
98            return None;
99        }
100
101        // see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_table.csv#L94>
102        let diff_code0: u16 = Self::read_field_le(ADC1_INIT_CODE_ATTEN0);
103        let code0 = if diff_code0 & (1 << 7) != 0 {
104            2160 - (diff_code0 & 0x7f)
105        } else {
106            2160 + diff_code0
107        };
108
109        if matches!(atten, Attenuation::_0dB) {
110            return Some(code0);
111        }
112
113        // see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_table.csv#L95>
114        let diff_code11: u16 = Self::read_field_le(ADC1_INIT_CODE_ATTEN3);
115        let code11 = code0 + diff_code11;
116
117        Some(code11)
118    }
119
120    /// Get ADC reference point voltage for specified attenuation in millivolts
121    ///
122    /// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_rtc_calib.c#L65>
123    pub fn rtc_calib_cal_mv(_unit: u8, atten: Attenuation) -> u16 {
124        match atten {
125            Attenuation::_0dB => 400,
126            Attenuation::_11dB => 1370,
127        }
128    }
129
130    /// Get ADC reference point digital code for specified attenuation
131    ///
132    /// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_rtc_calib.c#L65>
133    pub fn rtc_calib_cal_code(_unit: u8, atten: Attenuation) -> Option<u16> {
134        let version = Self::rtc_calib_version();
135
136        if version != 1 {
137            return None;
138        }
139
140        // see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_table.csv#L96>
141        let diff_code0: u16 = Self::read_field_le(ADC1_CAL_VOL_ATTEN0);
142        let code0 = if diff_code0 & (1 << 7) != 0 {
143            1540 - (diff_code0 & 0x7f)
144        } else {
145            1540 + diff_code0
146        };
147
148        if matches!(atten, Attenuation::_0dB) {
149            return Some(code0);
150        }
151
152        // see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_table.csv#L97>
153        let diff_code11: u16 = Self::read_field_le(ADC1_CAL_VOL_ATTEN3);
154        let code11 = if diff_code0 & (1 << 5) != 0 {
155            code0 - (diff_code11 & 0x1f)
156        } else {
157            code0 + diff_code11
158        } - 123;
159
160        Some(code11)
161    }
162}
163
164#[derive(Copy, Clone)]
165pub(crate) enum EfuseBlock {
166    Block0,
167    Block1,
168    Block2,
169    Block3,
170}
171
172impl EfuseBlock {
173    pub(crate) fn address(self) -> *const u32 {
174        let efuse = EFUSE::regs();
175        match self {
176            Self::Block0 => efuse.rd_wr_dis().as_ptr(),
177            Self::Block1 => efuse.rd_blk1_data0().as_ptr(),
178            Self::Block2 => efuse.rd_blk2_data0().as_ptr(),
179            Self::Block3 => efuse.rd_blk3_data0().as_ptr(),
180        }
181    }
182}