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

iio: adc: ad7124: add external clock support

Add support for an external clock source to the AD7124 ADC driver.

Previously, the driver only supported using the internal clock and had
bad devicetree bindings that used a fake clock to essentially select
the power mode. This is preserved for backwards compatibility.

If the clock is not named "mclk", then we know that the devicetree is
using the correct bindings and we can configure the chip to use an
external clock source rather than internal.

Also drop a redundant comment when configuring the register fields
instead of adding more.

Signed-off-by: David Lechner <dlechner@baylibre.com>
Link: https://patch.msgid.link/20250828-iio-adc-ad7124-proper-clock-support-v3-3-0b317b4605e5@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

David Lechner and committed by
Jonathan Cameron
ed231e25 aead8e4c

+76 -7
+76 -7
drivers/iio/adc/ad7124.c
··· 18 18 #include <linux/property.h> 19 19 #include <linux/regulator/consumer.h> 20 20 #include <linux/spi/spi.h> 21 + #include <linux/units.h> 21 22 22 23 #include <linux/iio/iio.h> 23 24 #include <linux/iio/adc/ad_sigma_delta.h> ··· 45 44 #define AD7124_STATUS_POR_FLAG BIT(4) 46 45 47 46 /* AD7124_ADC_CONTROL */ 47 + #define AD7124_ADC_CONTROL_CLK_SEL GENMASK(1, 0) 48 + #define AD7124_ADC_CONTROL_CLK_SEL_INT 0 49 + #define AD7124_ADC_CONTROL_CLK_SEL_INT_OUT 1 50 + #define AD7124_ADC_CONTROL_CLK_SEL_EXT 2 51 + #define AD7124_ADC_CONTROL_CLK_SEL_EXT_DIV4 3 48 52 #define AD7124_ADC_CONTROL_MODE GENMASK(5, 2) 49 53 #define AD7124_ADC_CONTROL_MODE_CONTINUOUS 0 50 54 #define AD7124_ADC_CONTROL_MODE_SINGLE 1 ··· 98 92 #define AD7124_MAX_CONFIGS 8 99 93 #define AD7124_MAX_CHANNELS 16 100 94 95 + #define AD7124_INT_CLK_HZ 614400 96 + 101 97 /* AD7124 input sources */ 102 98 103 99 enum ad7124_ref_sel { ··· 128 120 }; 129 121 130 122 static const int ad7124_master_clk_freq_hz[3] = { 131 - [AD7124_LOW_POWER] = 76800, 132 - [AD7124_MID_POWER] = 153600, 133 - [AD7124_FULL_POWER] = 614400, 123 + [AD7124_LOW_POWER] = AD7124_INT_CLK_HZ / 8, 124 + [AD7124_MID_POWER] = AD7124_INT_CLK_HZ / 4, 125 + [AD7124_FULL_POWER] = AD7124_INT_CLK_HZ, 134 126 }; 135 127 136 128 static const char * const ad7124_ref_names[] = { ··· 182 174 struct ad_sigma_delta sd; 183 175 struct ad7124_channel *channels; 184 176 struct regulator *vref[4]; 177 + u32 clk_hz; 185 178 unsigned int adc_control; 186 179 unsigned int num_channels; 187 180 struct mutex cfgs_lock; /* lock for configs access */ ··· 258 249 return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); 259 250 } 260 251 252 + static u32 ad7124_get_fclk_hz(struct ad7124_state *st) 253 + { 254 + enum ad7124_power_mode power_mode; 255 + u32 fclk_hz; 256 + 257 + power_mode = FIELD_GET(AD7124_ADC_CONTROL_POWER_MODE, st->adc_control); 258 + fclk_hz = st->clk_hz; 259 + 260 + switch (power_mode) { 261 + case AD7124_LOW_POWER: 262 + fclk_hz /= 8; 263 + break; 264 + case AD7124_MID_POWER: 265 + fclk_hz /= 4; 266 + break; 267 + default: 268 + break; 269 + } 270 + 271 + return fclk_hz; 272 + } 273 + 261 274 static void ad7124_set_channel_odr(struct ad7124_state *st, unsigned int channel, unsigned int odr) 262 275 { 263 276 unsigned int fclk, odr_sel_bits; 264 277 265 - fclk = ad7124_master_clk_freq_hz[FIELD_GET(AD7124_ADC_CONTROL_POWER_MODE, 266 - st->adc_control)]; 278 + fclk = ad7124_get_fclk_hz(st); 267 279 268 280 /* 269 281 * FS[10:0] = fCLK / (fADC x 32) where: ··· 1142 1112 static int ad7124_setup(struct ad7124_state *st) 1143 1113 { 1144 1114 struct device *dev = &st->sd.spi->dev; 1145 - unsigned int power_mode; 1115 + unsigned int power_mode, clk_sel; 1146 1116 struct clk *mclk; 1147 1117 int i, ret; 1148 1118 ··· 1186 1156 return dev_err_probe(dev, ret, 1187 1157 "Failed to set mclk rate\n"); 1188 1158 } 1159 + 1160 + clk_sel = AD7124_ADC_CONTROL_CLK_SEL_INT; 1161 + st->clk_hz = AD7124_INT_CLK_HZ; 1162 + } else { 1163 + struct clk *clk; 1164 + 1165 + clk = devm_clk_get_optional_enabled(dev, NULL); 1166 + if (IS_ERR(clk)) 1167 + return dev_err_probe(dev, PTR_ERR(clk), 1168 + "Failed to get external clock\n"); 1169 + 1170 + if (clk) { 1171 + unsigned long clk_hz; 1172 + 1173 + clk_hz = clk_get_rate(clk); 1174 + if (!clk_hz) 1175 + return dev_err_probe(dev, -EINVAL, 1176 + "Failed to get external clock rate\n"); 1177 + 1178 + /* 1179 + * The external clock may be 4x the nominal clock rate, 1180 + * in which case the ADC needs to be configured to 1181 + * divide it by 4. Using MEGA is a bit arbitrary, but 1182 + * the expected clock rates are either 614.4 kHz or 1183 + * 2.4576 MHz, so this should work. 1184 + */ 1185 + if (clk_hz > (1 * HZ_PER_MHZ)) { 1186 + clk_sel = AD7124_ADC_CONTROL_CLK_SEL_EXT_DIV4; 1187 + st->clk_hz = clk_hz / 4; 1188 + } else { 1189 + clk_sel = AD7124_ADC_CONTROL_CLK_SEL_EXT; 1190 + st->clk_hz = clk_hz; 1191 + } 1192 + } else { 1193 + clk_sel = AD7124_ADC_CONTROL_CLK_SEL_INT; 1194 + st->clk_hz = AD7124_INT_CLK_HZ; 1195 + } 1189 1196 } 1190 1197 1191 - /* Set the power mode */ 1198 + st->adc_control &= ~AD7124_ADC_CONTROL_CLK_SEL; 1199 + st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_CLK_SEL, clk_sel); 1200 + 1192 1201 st->adc_control &= ~AD7124_ADC_CONTROL_POWER_MODE; 1193 1202 st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_POWER_MODE, power_mode); 1194 1203