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, soc::efuse_field::EfuseField};
45
46mod fields;
47
48/// A struct representing the eFuse functionality of the chip.
49pub struct Efuse;
50
51impl Efuse {
52 /// Get status of SPI boot encryption.
53 pub fn flash_encryption() -> bool {
54 (Self::read_field_le::<u8>(SPI_BOOT_CRYPT_CNT).count_ones() % 2) != 0
55 }
56
57 /// Get the multiplier for the timeout value of the RWDT STAGE 0 register.
58 pub fn rwdt_multiplier() -> u8 {
59 Self::read_field_le::<u8>(WDT_DELAY_SEL)
60 }
61
62 /// Get efuse block version
63 ///
64 /// see <https://github.com/espressif/esp-idf/blob/dc016f5987/components/hal/efuse_hal.c#L27-L30>
65 pub fn block_version() -> (u8, u8) {
66 // see <https://github.com/espressif/esp-idf/blob/dc016f5987/components/hal/esp32c2/include/hal/efuse_ll.h#L65-L73>
67 // <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_table.csv#L90-L91>
68 (
69 Self::read_field_le::<u8>(BLK_VERSION_MAJOR),
70 Self::read_field_le::<u8>(BLK_VERSION_MINOR),
71 )
72 }
73
74 /// Get version of RTC calibration block
75 ///
76 /// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_rtc_calib.c#L14>
77 pub fn rtc_calib_version() -> u8 {
78 let (major, _minor) = Self::block_version();
79 if major == 0 { 1 } else { 0 }
80 }
81
82 /// Get ADC initial code for specified attenuation from efuse
83 ///
84 /// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_rtc_calib.c#L27>
85 pub fn rtc_calib_init_code(_unit: u8, atten: Attenuation) -> Option<u16> {
86 let version = Self::rtc_calib_version();
87
88 if version != 1 {
89 return None;
90 }
91
92 // see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_table.csv#L94>
93 let diff_code0: u16 = Self::read_field_le(ADC1_INIT_CODE_ATTEN0);
94 let code0 = if diff_code0 & (1 << 7) != 0 {
95 2160 - (diff_code0 & 0x7f)
96 } else {
97 2160 + diff_code0
98 };
99
100 if matches!(atten, Attenuation::_0dB) {
101 return Some(code0);
102 }
103
104 // see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_table.csv#L95>
105 let diff_code11: u16 = Self::read_field_le(ADC1_INIT_CODE_ATTEN3);
106 let code11 = code0 + diff_code11;
107
108 Some(code11)
109 }
110
111 /// Get ADC reference point voltage for specified attenuation in millivolts
112 ///
113 /// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_rtc_calib.c#L65>
114 pub fn rtc_calib_cal_mv(_unit: u8, atten: Attenuation) -> u16 {
115 match atten {
116 Attenuation::_0dB => 400,
117 Attenuation::_11dB => 1370,
118 }
119 }
120
121 /// Get ADC reference point digital code for specified attenuation
122 ///
123 /// see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_rtc_calib.c#L65>
124 pub fn rtc_calib_cal_code(_unit: u8, atten: Attenuation) -> Option<u16> {
125 let version = Self::rtc_calib_version();
126
127 if version != 1 {
128 return None;
129 }
130
131 // see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_table.csv#L96>
132 let diff_code0: u16 = Self::read_field_le(ADC1_CAL_VOL_ATTEN0);
133 let code0 = if diff_code0 & (1 << 7) != 0 {
134 1540 - (diff_code0 & 0x7f)
135 } else {
136 1540 + diff_code0
137 };
138
139 if matches!(atten, Attenuation::_0dB) {
140 return Some(code0);
141 }
142
143 // see <https://github.com/espressif/esp-idf/blob/903af13e8/components/efuse/esp32c2/esp_efuse_table.csv#L97>
144 let diff_code11: u16 = Self::read_field_le(ADC1_CAL_VOL_ATTEN3);
145 let code11 = if diff_code0 & (1 << 5) != 0 {
146 code0 - (diff_code11 & 0x1f)
147 } else {
148 code0 + diff_code11
149 } - 123;
150
151 Some(code11)
152 }
153}
154
155#[derive(Debug, Clone, Copy, strum::FromRepr)]
156#[repr(u32)]
157pub(crate) enum EfuseBlock {
158 Block0,
159 Block1,
160 Block2,
161 Block3,
162}
163
164impl EfuseBlock {
165 pub(crate) fn address(self) -> *const u32 {
166 let efuse = EFUSE::regs();
167 match self {
168 Self::Block0 => efuse.rd_wr_dis().as_ptr(),
169 Self::Block1 => efuse.rd_blk1_data0().as_ptr(),
170 Self::Block2 => efuse.rd_blk2_data0().as_ptr(),
171 Self::Block3 => efuse.rd_blk3_data0().as_ptr(),
172 }
173 }
174}