Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: adc: stm32-adc: smart calibration support

Add smart calibration support for STM32MP1.
- STM32MP15x: both linear & offset calibration are supported
- STM32MP13x: Only offset calibration is supported

Linear calibration:
Linear calibration is SoC dependent and does not change over time
so it can be done only once.
Linear calibration may have already been done in u-boot.
Skip calibration execution if calibration data are already available.
Save calibration factors in private data and restore them from private
data on next ADC start.

Offset calibration:
This calibration may vary over time, depending on temperature or voltage.
Run offset single-ended and differential calibration on each ADC start,
as it is not time consuming. This calibration do not need to be saved.
So, remove calfact_s and calfact_d value and bitfields that are no
longer used.

Signed-off-by: Olivier Moysan <olivier.moysan@foss.st.com>
Reviewed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Link: https://lore.kernel.org/r/20221115103124.70074-2-olivier.moysan@foss.st.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Olivier Moysan and committed by
Jonathan Cameron
9d901e35 f237cf19

+53 -56
+1
drivers/iio/adc/stm32-adc-core.h
··· 142 142 #define STM32H7_LINCALRDYW3 BIT(24) 143 143 #define STM32H7_LINCALRDYW2 BIT(23) 144 144 #define STM32H7_LINCALRDYW1 BIT(22) 145 + #define STM32H7_LINCALRDYW_MASK GENMASK(27, 22) 145 146 #define STM32H7_ADCALLIN BIT(16) 146 147 #define STM32H7_BOOST BIT(8) 147 148 #define STM32H7_ADSTP BIT(4)
+52 -56
drivers/iio/adc/stm32-adc.c
··· 119 119 120 120 /** 121 121 * struct stm32_adc_calib - optional adc calibration data 122 - * @calfact_s: Calibration offset for single ended channels 123 - * @calfact_d: Calibration offset in differential 124 122 * @lincalfact: Linearity calibration factor 125 - * @calibrated: Indicates calibration status 123 + * @lincal_saved: Indicates that linear calibration factors are saved 126 124 */ 127 125 struct stm32_adc_calib { 128 - u32 calfact_s; 129 - u32 calfact_d; 130 126 u32 lincalfact[STM32H7_LINCALFACT_NUM]; 131 - bool calibrated; 127 + bool lincal_saved; 132 128 }; 133 129 134 130 /** ··· 161 165 * @extsel: trigger selection register & bitfield 162 166 * @res: resolution selection register & bitfield 163 167 * @difsel: differential mode selection register & bitfield 164 - * @calfact_s: single-ended calibration factors register & bitfield 165 - * @calfact_d: differential calibration factors register & bitfield 166 168 * @smpr: smpr1 & smpr2 registers offset array 167 169 * @smp_bits: smpr1 & smpr2 index and bitfields 168 170 * @or_vddcore: option register & vddcore bitfield ··· 180 186 const struct stm32_adc_regs extsel; 181 187 const struct stm32_adc_regs res; 182 188 const struct stm32_adc_regs difsel; 183 - const struct stm32_adc_regs calfact_s; 184 - const struct stm32_adc_regs calfact_d; 185 189 const u32 smpr[2]; 186 190 const struct stm32_adc_regs *smp_bits; 187 191 const struct stm32_adc_regs or_vddcore; ··· 517 525 STM32H7_EXTSEL_SHIFT }, 518 526 .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT }, 519 527 .difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK}, 520 - .calfact_s = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_S_MASK, 521 - STM32H7_CALFACT_S_SHIFT }, 522 - .calfact_d = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_D_MASK, 523 - STM32H7_CALFACT_D_SHIFT }, 524 528 .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 }, 525 529 .smp_bits = stm32h7_smp_bits, 526 530 }; ··· 538 550 STM32H7_EXTSEL_SHIFT }, 539 551 .res = { STM32H7_ADC_CFGR, STM32MP13_RES_MASK, STM32MP13_RES_SHIFT }, 540 552 .difsel = { STM32MP13_ADC_DIFSEL, STM32MP13_DIFSEL_MASK}, 541 - .calfact_s = { STM32MP13_ADC_CALFACT, STM32MP13_CALFACT_S_MASK, 542 - STM32MP13_CALFACT_S_SHIFT }, 543 - .calfact_d = { STM32MP13_ADC_CALFACT, STM32MP13_CALFACT_D_MASK, 544 - STM32MP13_CALFACT_D_SHIFT }, 545 553 .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 }, 546 554 .smp_bits = stm32h7_smp_bits, 547 555 .or_vddcore = { STM32MP13_ADC2_OR, STM32MP13_OP0 }, ··· 559 575 STM32H7_EXTSEL_SHIFT }, 560 576 .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT }, 561 577 .difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK}, 562 - .calfact_s = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_S_MASK, 563 - STM32H7_CALFACT_S_SHIFT }, 564 - .calfact_d = { STM32H7_ADC_CALFACT, STM32H7_CALFACT_D_MASK, 565 - STM32H7_CALFACT_D_SHIFT }, 566 578 .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 }, 567 579 .smp_bits = stm32h7_smp_bits, 568 580 .or_vddcore = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN }, ··· 980 1000 int i, ret; 981 1001 u32 lincalrdyw_mask, val; 982 1002 983 - if (!adc->cfg->has_linearcal) 984 - goto skip_linearcal; 985 - 986 1003 /* Read linearity calibration */ 987 1004 lincalrdyw_mask = STM32H7_LINCALRDYW6; 988 1005 for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) { ··· 1001 1024 1002 1025 lincalrdyw_mask >>= 1; 1003 1026 } 1004 - 1005 - skip_linearcal: 1006 - /* Read offset calibration */ 1007 - val = stm32_adc_readl(adc, adc->cfg->regs->calfact_s.reg); 1008 - adc->cal.calfact_s = (val & adc->cfg->regs->calfact_s.mask); 1009 - adc->cal.calfact_s >>= adc->cfg->regs->calfact_s.shift; 1010 - adc->cal.calfact_d = (val & adc->cfg->regs->calfact_d.mask); 1011 - adc->cal.calfact_d >>= adc->cfg->regs->calfact_d.shift; 1012 - adc->cal.calibrated = true; 1027 + adc->cal.lincal_saved = true; 1013 1028 1014 1029 return 0; 1015 1030 } ··· 1016 1047 struct stm32_adc *adc = iio_priv(indio_dev); 1017 1048 int i, ret; 1018 1049 u32 lincalrdyw_mask, val; 1019 - 1020 - val = (adc->cal.calfact_s << adc->cfg->regs->calfact_s.shift) | 1021 - (adc->cal.calfact_d << adc->cfg->regs->calfact_d.shift); 1022 - stm32_adc_writel(adc, adc->cfg->regs->calfact_s.reg, val); 1023 - 1024 - if (!adc->cfg->has_linearcal) 1025 - return 0; 1026 1050 1027 1051 lincalrdyw_mask = STM32H7_LINCALRDYW6; 1028 1052 for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) { ··· 1078 1116 /** 1079 1117 * stm32h7_adc_selfcalib() - Procedure to calibrate ADC 1080 1118 * @indio_dev: IIO device instance 1119 + * @do_lincal: linear calibration request flag 1081 1120 * Note: Must be called once ADC is out of power down. 1121 + * 1122 + * Run offset calibration unconditionally. 1123 + * Run linear calibration if requested & supported. 1082 1124 */ 1083 - static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev) 1125 + static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev, int do_lincal) 1084 1126 { 1085 1127 struct stm32_adc *adc = iio_priv(indio_dev); 1086 1128 int ret; 1087 1129 u32 msk = STM32H7_ADCALDIF; 1088 1130 u32 val; 1089 1131 1090 - if (adc->cal.calibrated) 1091 - return true; 1092 - 1093 - if (adc->cfg->has_linearcal) 1132 + if (adc->cfg->has_linearcal && do_lincal) 1094 1133 msk |= STM32H7_ADCALLIN; 1095 1134 /* ADC must be disabled for calibration */ 1096 1135 stm32h7_adc_disable(indio_dev); ··· 1136 1173 } 1137 1174 1138 1175 /** 1176 + * stm32h7_adc_check_selfcalib() - Check linear calibration status 1177 + * @indio_dev: IIO device instance 1178 + * 1179 + * Used to check if linear calibration has been done. 1180 + * Return true if linear calibration factors are already saved in private data 1181 + * or if a linear calibration has been done at boot stage. 1182 + */ 1183 + static int stm32h7_adc_check_selfcalib(struct iio_dev *indio_dev) 1184 + { 1185 + struct stm32_adc *adc = iio_priv(indio_dev); 1186 + u32 val; 1187 + 1188 + if (adc->cal.lincal_saved) 1189 + return true; 1190 + 1191 + /* 1192 + * Check if linear calibration factors are available in ADC registers, 1193 + * by checking that all LINCALRDYWx bits are set. 1194 + */ 1195 + val = stm32_adc_readl(adc, STM32H7_ADC_CR) & STM32H7_LINCALRDYW_MASK; 1196 + if (val == STM32H7_LINCALRDYW_MASK) 1197 + return true; 1198 + 1199 + return false; 1200 + } 1201 + 1202 + /** 1139 1203 * stm32h7_adc_prepare() - Leave power down mode to enable ADC. 1140 1204 * @indio_dev: IIO device instance 1141 1205 * Leave power down mode. ··· 1176 1186 static int stm32h7_adc_prepare(struct iio_dev *indio_dev) 1177 1187 { 1178 1188 struct stm32_adc *adc = iio_priv(indio_dev); 1179 - int calib, ret; 1189 + int lincal_done = false; 1190 + int ret; 1180 1191 1181 1192 ret = stm32h7_adc_exit_pwr_down(indio_dev); 1182 1193 if (ret) 1183 1194 return ret; 1184 1195 1185 - ret = stm32h7_adc_selfcalib(indio_dev); 1196 + if (adc->cfg->has_linearcal) 1197 + lincal_done = stm32h7_adc_check_selfcalib(indio_dev); 1198 + 1199 + /* Always run offset calibration. Run linear calibration only once */ 1200 + ret = stm32h7_adc_selfcalib(indio_dev, !lincal_done); 1186 1201 if (ret < 0) 1187 1202 goto pwr_dwn; 1188 - calib = ret; 1189 1203 1190 1204 stm32_adc_int_ch_enable(indio_dev); 1191 1205 ··· 1199 1205 if (ret) 1200 1206 goto ch_disable; 1201 1207 1202 - /* Either restore or read calibration result for future reference */ 1203 - if (calib) 1204 - ret = stm32h7_adc_restore_selfcalib(indio_dev); 1205 - else 1206 - ret = stm32h7_adc_read_selfcalib(indio_dev); 1207 - if (ret) 1208 - goto disable; 1208 + if (adc->cfg->has_linearcal) { 1209 + if (!adc->cal.lincal_saved) 1210 + ret = stm32h7_adc_read_selfcalib(indio_dev); 1211 + else 1212 + ret = stm32h7_adc_restore_selfcalib(indio_dev); 1213 + 1214 + if (ret) 1215 + goto disable; 1216 + } 1209 1217 1210 1218 if (adc->cfg->has_presel) 1211 1219 stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel);