1use core::marker::PhantomData;
2
3use super::AdcHasLineCal;
4use crate::{
5 analog::adc::{AdcCalScheme, Attenuation},
6 efuse,
7};
8
9const COEFF_A_SCALE: u32 = 65536;
10const COEFF_A_ROUND: u32 = COEFF_A_SCALE / 2;
11
12const VREF_MASK: u32 = 0x1F;
13const VREF_STEP_SIZE: i32 = 7;
14const VREF_OFFSET: i32 = 1100;
15
16const TP_LOW1_OFFSET: i32 = 278;
17const TP_LOW2_OFFSET: i32 = 421;
18const TP_LOW_MASK: u32 = 0x7F;
19const TP_LOW_VOLTAGE: u32 = 150;
20const TP_HIGH1_OFFSET: i32 = 3265;
21const TP_HIGH2_OFFSET: i32 = 3406;
22const TP_HIGH_MASK: u32 = 0x1FF;
23const TP_HIGH_VOLTAGE: u32 = 850;
24const TP_STEP_SIZE: i32 = 4;
25
26const LUT_VREF_LOW: i32 = 1000;
27const LUT_VREF_HIGH: i32 = 1200;
28const LUT_ADC_STEP_SIZE: u32 = 64;
29const LUT_POINTS: usize = 20;
30const LUT_LOW_THRESH: u32 = 2880;
31const LUT_HIGH_THRESH: u32 = LUT_LOW_THRESH + LUT_ADC_STEP_SIZE;
32const ADC_12_BIT_RES: u32 = 4096;
33
34const ADC1_TP_ATTEN_SCALE: [u32; 4] = [65504, 86975, 120389, 224310];
35const ADC2_TP_ATTEN_SCALE: [u32; 4] = [65467, 86861, 120416, 224708];
36const ADC1_TP_ATTEN_OFFSET: [u32; 4] = [0, 1, 27, 54];
37const ADC2_TP_ATTEN_OFFSET: [u32; 4] = [0, 9, 26, 66];
38
39const ADC1_VREF_ATTEN_SCALE: [u32; 4] = [57431, 76236, 105481, 196602];
40const ADC2_VREF_ATTEN_SCALE: [u32; 4] = [57236, 76175, 105678, 197170];
41const ADC1_VREF_ATTEN_OFFSET: [u32; 4] = [75, 78, 107, 142];
42const ADC2_VREF_ATTEN_OFFSET: [u32; 4] = [63, 66, 89, 128];
43
44const LUT_ADC1_LOW: [u32; LUT_POINTS] = [
45 2240, 2297, 2352, 2405, 2457, 2512, 2564, 2616, 2664, 2709, 2754, 2795, 2832, 2868, 2903, 2937,
46 2969, 3000, 3030, 3060,
47];
48const LUT_ADC1_HIGH: [u32; LUT_POINTS] = [
49 2667, 2706, 2745, 2780, 2813, 2844, 2873, 2901, 2928, 2956, 2982, 3006, 3032, 3059, 3084, 3110,
50 3135, 3160, 3184, 3209,
51];
52const LUT_ADC2_LOW: [u32; LUT_POINTS] = [
53 2238, 2293, 2347, 2399, 2451, 2507, 2561, 2613, 2662, 2710, 2754, 2792, 2831, 2869, 2904, 2937,
54 2968, 2999, 3029, 3059,
55];
56const LUT_ADC2_HIGH: [u32; LUT_POINTS] = [
57 2657, 2698, 2738, 2774, 2807, 2838, 2867, 2894, 2921, 2946, 2971, 2996, 3020, 3043, 3067, 3092,
58 3116, 3139, 3162, 3185,
59];
60
61#[derive(Clone, Copy)]
68pub struct AdcCalLine<ADCX> {
69 coeff_a: u32,
70 coeff_b: u32,
71 vref: u32,
72 use_lut: bool,
73 unit: u8,
74 _phantom: PhantomData<ADCX>,
75}
76
77fn decode_bits(bits: u32, mask: u32, twos_compl: bool) -> i32 {
78 let sign_bit = !(mask >> 1) & mask;
79 if bits & sign_bit != 0 {
80 if twos_compl {
81 -(((!bits).wrapping_add(1) & (mask >> 1)) as i32)
82 } else {
83 -((bits & (mask >> 1)) as i32)
84 }
85 } else {
86 (bits & (mask >> 1)) as i32
87 }
88}
89
90fn read_efuse_vref() -> u32 {
91 let bits = efuse::read_field_le::<u32>(efuse::ADC_VREF);
92 (VREF_OFFSET + decode_bits(bits, VREF_MASK, false) * VREF_STEP_SIZE) as u32
93}
94
95fn check_efuse_vref() -> bool {
96 efuse::read_field_le::<u32>(efuse::ADC_VREF) != 0
97}
98
99fn check_efuse_tp() -> bool {
100 if !efuse::read_bit(efuse::BLK3_PART_RESERVE) {
101 return false;
102 }
103 efuse::read_field_le::<u32>(efuse::ADC1_TP_LOW) != 0
104 && efuse::read_field_le::<u32>(efuse::ADC2_TP_LOW) != 0
105 && efuse::read_field_le::<u32>(efuse::ADC1_TP_HIGH) != 0
106 && efuse::read_field_le::<u32>(efuse::ADC2_TP_HIGH) != 0
107}
108
109fn read_efuse_tp_low(unit: u8) -> u32 {
110 let (high_offset, efuse) = if unit == 0 {
111 (TP_LOW1_OFFSET, efuse::ADC1_TP_LOW)
112 } else {
113 (TP_LOW2_OFFSET, efuse::ADC2_TP_LOW)
114 };
115
116 let efuse_value = decode_bits(efuse::read_field_le::<u32>(efuse), TP_LOW_MASK, true);
117 (high_offset + efuse_value * TP_STEP_SIZE) as u32
118}
119
120fn read_efuse_tp_high(unit: u8) -> u32 {
121 let (high_offset, efuse) = if unit == 0 {
122 (TP_HIGH1_OFFSET, efuse::ADC1_TP_HIGH)
123 } else {
124 (TP_HIGH2_OFFSET, efuse::ADC2_TP_HIGH)
125 };
126
127 let efuse_value = decode_bits(efuse::read_field_le::<u32>(efuse), TP_HIGH_MASK, true);
128 (high_offset + efuse_value * TP_STEP_SIZE) as u32
129}
130
131fn characterize_using_two_point(unit: u8, atten: usize, high: u32, low: u32) -> (u32, u32) {
132 let (scales, offsets) = if unit == 0 {
133 (&ADC1_TP_ATTEN_SCALE, &ADC1_TP_ATTEN_OFFSET)
134 } else {
135 (&ADC2_TP_ATTEN_SCALE, &ADC2_TP_ATTEN_OFFSET)
136 };
137 let delta_x = high - low;
138 let delta_v = TP_HIGH_VOLTAGE - TP_LOW_VOLTAGE;
139 let coeff_a = (delta_v * scales[atten] + (delta_x / 2)) / delta_x;
140 let coeff_b = TP_HIGH_VOLTAGE - ((delta_v * high + (delta_x / 2)) / delta_x) + offsets[atten];
141 (coeff_a, coeff_b)
142}
143
144fn characterize_using_vref(unit: u8, atten: usize, vref: u32) -> (u32, u32) {
145 let (scales, offsets) = if unit == 0 {
146 (&ADC1_VREF_ATTEN_SCALE, &ADC1_VREF_ATTEN_OFFSET)
147 } else {
148 (&ADC2_VREF_ATTEN_SCALE, &ADC2_VREF_ATTEN_OFFSET)
149 };
150 let coeff_a = (vref * scales[atten]) / ADC_12_BIT_RES;
151 (coeff_a, offsets[atten])
152}
153
154fn calculate_voltage_linear(adc: u32, coeff_a: u32, coeff_b: u32) -> u32 {
155 ((coeff_a * adc + COEFF_A_ROUND) / COEFF_A_SCALE) + coeff_b
156}
157
158fn interpolate_two_points(y1: u32, y2: u32, x_step: u32, x: u32) -> u32 {
159 ((y1 * x_step) + (y2 * x) - (y1 * x) + (x_step / 2)) / x_step
160}
161
162fn calculate_voltage_lut(
163 adc: u32,
164 vref: u32,
165 low: &[u32; LUT_POINTS],
166 high: &[u32; LUT_POINTS],
167) -> u32 {
168 let i = ((adc - LUT_LOW_THRESH) / LUT_ADC_STEP_SIZE) as usize;
169 let i = i.min(LUT_POINTS - 2);
170
171 let x2dist = LUT_VREF_HIGH - vref as i32;
172 let x1dist = vref as i32 - LUT_VREF_LOW;
173 let y2dist = ((i as u32 + 1) * LUT_ADC_STEP_SIZE + LUT_LOW_THRESH) as i32 - adc as i32;
174 let y1dist = adc as i32 - (i as u32 * LUT_ADC_STEP_SIZE + LUT_LOW_THRESH) as i32;
175
176 let q11 = low[i] as i32;
177 let q12 = low[i + 1] as i32;
178 let q21 = high[i] as i32;
179 let q22 = high[i + 1] as i32;
180
181 let mut voltage = (q11 * x2dist * y2dist)
182 + (q21 * x1dist * y2dist)
183 + (q12 * x2dist * y1dist)
184 + (q22 * x1dist * y1dist);
185 voltage += ((LUT_VREF_HIGH - LUT_VREF_LOW) * LUT_ADC_STEP_SIZE as i32) / 2;
186 voltage /= (LUT_VREF_HIGH - LUT_VREF_LOW) * LUT_ADC_STEP_SIZE as i32;
187 voltage as u32
188}
189
190impl<ADCX> crate::private::Sealed for AdcCalLine<ADCX> {}
191
192impl<ADCX> AdcCalScheme<ADCX> for AdcCalLine<ADCX>
193where
194 ADCX: AdcHasLineCal,
195{
196 fn new_cal(atten: Attenuation) -> Self {
197 let unit = ADCX::UNIT;
198 let atten_idx = atten as usize;
199
200 let vref = if check_efuse_vref() {
201 read_efuse_vref()
202 } else {
203 1100
204 };
205
206 let (coeff_a, coeff_b) = if check_efuse_tp() {
207 let high = read_efuse_tp_high(unit);
208 let low = read_efuse_tp_low(unit);
209 characterize_using_two_point(unit, atten_idx, high, low)
210 } else {
211 characterize_using_vref(unit, atten_idx, vref)
212 };
213
214 Self {
215 coeff_a,
216 coeff_b,
217 vref,
218 use_lut: atten == Attenuation::_11dB,
219 unit,
220 _phantom: PhantomData,
221 }
222 }
223
224 fn adc_val(&self, val: u16) -> u16 {
225 let raw = val as u32;
226
227 if self.use_lut && raw >= LUT_LOW_THRESH {
228 let (low, high) = if self.unit == 0 {
229 (&LUT_ADC1_LOW, &LUT_ADC1_HIGH)
230 } else {
231 (&LUT_ADC2_LOW, &LUT_ADC2_HIGH)
232 };
233 let lut_voltage = calculate_voltage_lut(raw, self.vref, low, high);
234 if raw <= LUT_HIGH_THRESH {
235 let linear = calculate_voltage_linear(raw, self.coeff_a, self.coeff_b);
236 interpolate_two_points(linear, lut_voltage, LUT_ADC_STEP_SIZE, raw - LUT_LOW_THRESH)
237 as u16
238 } else {
239 lut_voltage as u16
240 }
241 } else {
242 calculate_voltage_linear(raw, self.coeff_a, self.coeff_b) as u16
243 }
244 }
245}