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

iio: adc: mcp3911: add support to set PGA

Add support for setting the Programmable Gain Amplifiers by adjust the
scale value.

Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20220922194639.1118971-1-marcus.folkesson@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Marcus Folkesson and committed by
Jonathan Cameron
8cf5f032 4e615140

+80 -24
+80 -24
drivers/iio/adc/mcp3911.c
··· 29 29 #define MCP3911_REG_MOD 0x06 30 30 #define MCP3911_REG_PHASE 0x07 31 31 #define MCP3911_REG_GAIN 0x09 32 + #define MCP3911_GAIN_MASK(ch) (GENMASK(2, 0) << 3 * ch) 33 + #define MCP3911_GAIN_VAL(ch, val) ((val << 3 * ch) & MCP3911_GAIN_MASK(ch)) 32 34 33 35 #define MCP3911_REG_STATUSCOM 0x0a 34 36 #define MCP3911_STATUSCOM_DRHIZ BIT(12) ··· 62 60 #define MCP3911_REG_MASK GENMASK(4, 1) 63 61 64 62 #define MCP3911_NUM_CHANNELS 2 63 + #define MCP3911_NUM_SCALES 6 65 64 66 65 static const int mcp3911_osr_table[] = { 32, 64, 128, 256, 512, 1024, 2048, 4096 }; 66 + static u32 mcp3911_scale_table[MCP3911_NUM_SCALES][2]; 67 67 68 68 struct mcp3911 { 69 69 struct spi_device *spi; ··· 74 70 struct clk *clki; 75 71 u32 dev_addr; 76 72 struct iio_trigger *trig; 73 + u32 gain[MCP3911_NUM_CHANNELS]; 77 74 struct { 78 75 u32 channels[MCP3911_NUM_CHANNELS]; 79 76 s64 ts __aligned(8); ··· 151 146 *vals = mcp3911_osr_table; 152 147 *length = ARRAY_SIZE(mcp3911_osr_table); 153 148 return IIO_AVAIL_LIST; 149 + case IIO_CHAN_INFO_SCALE: 150 + *type = IIO_VAL_INT_PLUS_NANO; 151 + *vals = (int *)mcp3911_scale_table; 152 + *length = ARRAY_SIZE(mcp3911_scale_table) * 2; 153 + return IIO_AVAIL_LIST; 154 154 default: 155 155 return -EINVAL; 156 156 } ··· 200 190 break; 201 191 202 192 case IIO_CHAN_INFO_SCALE: 203 - if (adc->vref) { 204 - ret = regulator_get_voltage(adc->vref); 205 - if (ret < 0) { 206 - dev_err(indio_dev->dev.parent, 207 - "failed to get vref voltage: %d\n", 208 - ret); 209 - goto out; 210 - } 211 - 212 - *val = ret / 1000; 213 - } else { 214 - *val = MCP3911_INT_VREF_MV; 215 - } 216 - 217 - /* 218 - * For 24bit Conversion 219 - * Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5 220 - * Voltage = Raw * (Vref)/(2^23 * Gain * 1.5) 221 - */ 222 - 223 - /* val2 = (2^23 * 1.5) */ 224 - *val2 = 12582912; 225 - ret = IIO_VAL_FRACTIONAL; 193 + *val = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][0]; 194 + *val2 = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][1]; 195 + ret = IIO_VAL_INT_PLUS_NANO; 226 196 break; 227 197 } 228 198 ··· 220 230 221 231 mutex_lock(&adc->lock); 222 232 switch (mask) { 233 + case IIO_CHAN_INFO_SCALE: 234 + for (int i = 0; i < MCP3911_NUM_SCALES; i++) { 235 + if (val == mcp3911_scale_table[i][0] && 236 + val2 == mcp3911_scale_table[i][1]) { 237 + 238 + adc->gain[channel->channel] = BIT(i); 239 + ret = mcp3911_update(adc, MCP3911_REG_GAIN, 240 + MCP3911_GAIN_MASK(channel->channel), 241 + MCP3911_GAIN_VAL(channel->channel, i), 1); 242 + } 243 + } 244 + break; 223 245 case IIO_CHAN_INFO_OFFSET: 224 246 if (val2 != 0) { 225 247 ret = -EINVAL; ··· 267 265 return ret; 268 266 } 269 267 268 + static int mcp3911_calc_scale_table(struct mcp3911 *adc) 269 + { 270 + u32 ref = MCP3911_INT_VREF_MV; 271 + u32 div; 272 + int ret; 273 + u64 tmp; 274 + 275 + if (adc->vref) { 276 + ret = regulator_get_voltage(adc->vref); 277 + if (ret < 0) { 278 + dev_err(&adc->spi->dev, 279 + "failed to get vref voltage: %d\n", 280 + ret); 281 + return ret; 282 + } 283 + 284 + ref = ret / 1000; 285 + } 286 + 287 + /* 288 + * For 24-bit Conversion 289 + * Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5 290 + * Voltage = Raw * (Vref)/(2^23 * Gain * 1.5) 291 + * 292 + * ref = Reference voltage 293 + * div = (2^23 * 1.5 * gain) = 12582912 * gain 294 + */ 295 + for (int i = 0; i < MCP3911_NUM_SCALES; i++) { 296 + div = 12582912 * BIT(i); 297 + tmp = div_s64((s64)ref * 1000000000LL, div); 298 + 299 + mcp3911_scale_table[i][0] = 0; 300 + mcp3911_scale_table[i][1] = tmp; 301 + } 302 + 303 + return 0; 304 + } 305 + 270 306 #define MCP3911_CHAN(idx) { \ 271 307 .type = IIO_VOLTAGE, \ 272 308 .indexed = 1, \ ··· 314 274 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 315 275 BIT(IIO_CHAN_INFO_OFFSET) | \ 316 276 BIT(IIO_CHAN_INFO_SCALE), \ 317 - .info_mask_shared_by_type_available = \ 277 + .info_mask_shared_by_type_available = \ 318 278 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ 279 + .info_mask_separate_available = \ 280 + BIT(IIO_CHAN_INFO_SCALE), \ 319 281 .scan_type = { \ 320 282 .sign = 's', \ 321 283 .realbits = 24, \ ··· 523 481 MCP3911_STATUSCOM_DRHIZ, 2); 524 482 if (ret) 525 483 return ret; 484 + 485 + ret = mcp3911_calc_scale_table(adc); 486 + if (ret) 487 + return ret; 488 + 489 + /* Set gain to 1 for all channels */ 490 + for (int i = 0; i < MCP3911_NUM_CHANNELS; i++) { 491 + adc->gain[i] = 1; 492 + ret = mcp3911_update(adc, MCP3911_REG_GAIN, 493 + MCP3911_GAIN_MASK(i), 494 + MCP3911_GAIN_VAL(i, 0), 1); 495 + if (ret) 496 + return ret; 497 + } 526 498 527 499 indio_dev->name = spi_get_device_id(spi)->name; 528 500 indio_dev->modes = INDIO_DIRECT_MODE;