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

Configure Feed

Select the types of activity you want to include in your feed.

iio: adc: ad9467: add digital interface test to debugfs

One useful thing to do (in case of problems) in this high speed devices
with digital interfaces is to try different test patterns to see if the
interface is working properly (and properly calibrated). Hence add this
to debugfs.

On top of this, for some test patterns, the backend may have a matching
validator block which can be helpful in identifying possible issues. For
the other patterns some test equipment must be used so one can look into
the signal and see how it looks like.

Hence, we also add the backend debugfs interface with
iio_backend_debugfs_add().

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
Link: https://patch.msgid.link/20240802-dev-iio-backend-add-debugfs-v2-8-4cb62852f0d0@analog.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Nuno Sa and committed by
Jonathan Cameron
d6b133a6 5b30937b

+188
+188
drivers/iio/adc/ad9467.c
··· 15 15 #include <linux/kernel.h> 16 16 #include <linux/slab.h> 17 17 #include <linux/spi/spi.h> 18 + #include <linux/seq_file.h> 18 19 #include <linux/err.h> 19 20 #include <linux/delay.h> 20 21 #include <linux/gpio/consumer.h> ··· 137 136 unsigned int num_channels; 138 137 const unsigned int (*scale_table)[2]; 139 138 int num_scales; 139 + unsigned long test_mask; 140 + unsigned int test_mask_len; 140 141 unsigned long max_rate; 141 142 unsigned int default_output_mode; 142 143 unsigned int vref_mask; ··· 150 147 bool has_dco_invert; 151 148 }; 152 149 150 + struct ad9467_chan_test_mode { 151 + struct ad9467_state *st; 152 + unsigned int idx; 153 + u8 mode; 154 + }; 155 + 153 156 struct ad9467_state { 154 157 const struct ad9467_chip_info *info; 155 158 struct iio_backend *back; 156 159 struct spi_device *spi; 157 160 struct clk *clk; 161 + /* used for debugfs */ 162 + struct ad9467_chan_test_mode *chan_test; 158 163 unsigned int output_mode; 159 164 unsigned int (*scales)[2]; 160 165 /* ··· 319 308 AD9467_CHAN(1, BIT(IIO_CHAN_INFO_SCALE), 1, 16, 's'), 320 309 }; 321 310 311 + static const char * const ad9467_test_modes[] = { 312 + [AN877_ADC_TESTMODE_OFF] = "off", 313 + [AN877_ADC_TESTMODE_MIDSCALE_SHORT] = "midscale_short", 314 + [AN877_ADC_TESTMODE_POS_FULLSCALE] = "pos_fullscale", 315 + [AN877_ADC_TESTMODE_NEG_FULLSCALE] = "neg_fullscale", 316 + [AN877_ADC_TESTMODE_ALT_CHECKERBOARD] = "checkerboard", 317 + [AN877_ADC_TESTMODE_PN23_SEQ] = "prbs23", 318 + [AN877_ADC_TESTMODE_PN9_SEQ] = "prbs9", 319 + [AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE] = "one_zero_toggle", 320 + [AN877_ADC_TESTMODE_USER] = "user", 321 + [AN877_ADC_TESTMODE_BIT_TOGGLE] = "bit_toggle", 322 + [AN877_ADC_TESTMODE_SYNC] = "sync", 323 + [AN877_ADC_TESTMODE_ONE_BIT_HIGH] = "one_bit_high", 324 + [AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY] = "mixed_bit_frequency", 325 + [AN877_ADC_TESTMODE_RAMP] = "ramp", 326 + }; 327 + 322 328 static const struct ad9467_chip_info ad9467_chip_tbl = { 323 329 .name = "ad9467", 324 330 .id = CHIPID_AD9467, ··· 345 317 .channels = ad9467_channels, 346 318 .num_channels = ARRAY_SIZE(ad9467_channels), 347 319 .test_points = AD9647_MAX_TEST_POINTS, 320 + .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE, 321 + AN877_ADC_TESTMODE_OFF), 322 + .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1, 348 323 .default_output_mode = AD9467_DEF_OUTPUT_MODE, 349 324 .vref_mask = AD9467_REG_VREF_MASK, 350 325 .num_lanes = 8, ··· 362 331 .channels = ad9434_channels, 363 332 .num_channels = ARRAY_SIZE(ad9434_channels), 364 333 .test_points = AD9647_MAX_TEST_POINTS, 334 + .test_mask = GENMASK(AN877_ADC_TESTMODE_USER, AN877_ADC_TESTMODE_OFF), 335 + .test_mask_len = AN877_ADC_TESTMODE_USER + 1, 365 336 .default_output_mode = AD9434_DEF_OUTPUT_MODE, 366 337 .vref_mask = AD9434_REG_VREF_MASK, 367 338 .num_lanes = 6, ··· 378 345 .channels = ad9467_channels, 379 346 .num_channels = ARRAY_SIZE(ad9467_channels), 380 347 .test_points = AD9647_MAX_TEST_POINTS, 348 + .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE, 349 + AN877_ADC_TESTMODE_OFF), 350 + .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1, 381 351 .default_output_mode = AD9265_DEF_OUTPUT_MODE, 382 352 .vref_mask = AD9265_REG_VREF_MASK, 383 353 .has_dco = true, ··· 396 360 .channels = ad9643_channels, 397 361 .num_channels = ARRAY_SIZE(ad9643_channels), 398 362 .test_points = AD9647_MAX_TEST_POINTS, 363 + .test_mask = BIT(AN877_ADC_TESTMODE_RAMP) | 364 + GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY, AN877_ADC_TESTMODE_OFF), 365 + .test_mask_len = AN877_ADC_TESTMODE_RAMP + 1, 399 366 .vref_mask = AD9643_REG_VREF_MASK, 400 367 .has_dco = true, 401 368 .has_dco_invert = true, ··· 414 375 .channels = ad9649_channels, 415 376 .num_channels = ARRAY_SIZE(ad9649_channels), 416 377 .test_points = AD9649_TEST_POINTS, 378 + .test_mask = GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY, 379 + AN877_ADC_TESTMODE_OFF), 380 + .test_mask_len = AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY + 1, 417 381 .has_dco = true, 418 382 .has_dco_invert = true, 419 383 .dco_en = AN877_ADC_DCO_DELAY_ENABLE, ··· 431 389 .channels = ad9652_channels, 432 390 .num_channels = ARRAY_SIZE(ad9652_channels), 433 391 .test_points = AD9647_MAX_TEST_POINTS, 392 + .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE, 393 + AN877_ADC_TESTMODE_OFF), 394 + .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1, 434 395 .vref_mask = AD9652_REG_VREF_MASK, 435 396 .has_dco = true, 436 397 }; ··· 978 933 return -ENODEV; 979 934 } 980 935 936 + static int ad9467_test_mode_available_show(struct seq_file *s, void *ignored) 937 + { 938 + struct ad9467_state *st = s->private; 939 + unsigned int bit; 940 + 941 + for_each_set_bit(bit, &st->info->test_mask, st->info->test_mask_len) 942 + seq_printf(s, "%s\n", ad9467_test_modes[bit]); 943 + 944 + return 0; 945 + } 946 + DEFINE_SHOW_ATTRIBUTE(ad9467_test_mode_available); 947 + 948 + static ssize_t ad9467_chan_test_mode_read(struct file *file, 949 + char __user *userbuf, size_t count, 950 + loff_t *ppos) 951 + { 952 + struct ad9467_chan_test_mode *chan = file->private_data; 953 + struct ad9467_state *st = chan->st; 954 + char buf[128] = {0}; 955 + size_t len; 956 + int ret; 957 + 958 + if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ || 959 + chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) { 960 + len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test:\n\t", 961 + ad9467_test_modes[chan->mode]); 962 + 963 + ret = iio_backend_debugfs_print_chan_status(st->back, chan->idx, 964 + buf + len, 965 + sizeof(buf) - len); 966 + if (ret < 0) 967 + return ret; 968 + len += ret; 969 + } else if (chan->mode == AN877_ADC_TESTMODE_OFF) { 970 + len = scnprintf(buf, sizeof(buf), "No test Running...\n"); 971 + } else { 972 + len = scnprintf(buf, sizeof(buf), "Running \"%s\" Test on CH:%u\n", 973 + ad9467_test_modes[chan->mode], chan->idx); 974 + } 975 + 976 + return simple_read_from_buffer(userbuf, count, ppos, buf, len); 977 + } 978 + 979 + static ssize_t ad9467_chan_test_mode_write(struct file *file, 980 + const char __user *userbuf, 981 + size_t count, loff_t *ppos) 982 + { 983 + struct ad9467_chan_test_mode *chan = file->private_data; 984 + struct ad9467_state *st = chan->st; 985 + char test_mode[32] = {0}; 986 + unsigned int mode; 987 + int ret; 988 + 989 + ret = simple_write_to_buffer(test_mode, sizeof(test_mode) - 1, ppos, 990 + userbuf, count); 991 + if (ret < 0) 992 + return ret; 993 + 994 + for_each_set_bit(mode, &st->info->test_mask, st->info->test_mask_len) { 995 + if (sysfs_streq(test_mode, ad9467_test_modes[mode])) 996 + break; 997 + } 998 + 999 + if (mode == st->info->test_mask_len) 1000 + return -EINVAL; 1001 + 1002 + guard(mutex)(&st->lock); 1003 + 1004 + if (mode == AN877_ADC_TESTMODE_OFF) { 1005 + unsigned int out_mode; 1006 + 1007 + if (chan->mode == AN877_ADC_TESTMODE_PN9_SEQ || 1008 + chan->mode == AN877_ADC_TESTMODE_PN23_SEQ) { 1009 + ret = ad9467_backend_testmode_off(st, chan->idx); 1010 + if (ret) 1011 + return ret; 1012 + } 1013 + 1014 + ret = ad9467_testmode_set(st, chan->idx, mode); 1015 + if (ret) 1016 + return ret; 1017 + 1018 + out_mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT; 1019 + ret = ad9467_outputmode_set(st, out_mode); 1020 + if (ret) 1021 + return ret; 1022 + } else { 1023 + ret = ad9467_outputmode_set(st, st->info->default_output_mode); 1024 + if (ret) 1025 + return ret; 1026 + 1027 + ret = ad9467_testmode_set(st, chan->idx, mode); 1028 + if (ret) 1029 + return ret; 1030 + 1031 + /* some patterns have a backend matching monitoring block */ 1032 + if (mode == AN877_ADC_TESTMODE_PN9_SEQ) { 1033 + ret = ad9467_backend_testmode_on(st, chan->idx, 1034 + IIO_BACKEND_ADI_PRBS_9A); 1035 + if (ret) 1036 + return ret; 1037 + } else if (mode == AN877_ADC_TESTMODE_PN23_SEQ) { 1038 + ret = ad9467_backend_testmode_on(st, chan->idx, 1039 + IIO_BACKEND_ADI_PRBS_23A); 1040 + if (ret) 1041 + return ret; 1042 + } 1043 + } 1044 + 1045 + chan->mode = mode; 1046 + 1047 + return count; 1048 + } 1049 + 1050 + static const struct file_operations ad9467_chan_test_mode_fops = { 1051 + .open = simple_open, 1052 + .read = ad9467_chan_test_mode_read, 1053 + .write = ad9467_chan_test_mode_write, 1054 + .llseek = default_llseek, 1055 + .owner = THIS_MODULE, 1056 + }; 1057 + 981 1058 static ssize_t ad9467_dump_calib_table(struct file *file, 982 1059 char __user *userbuf, 983 1060 size_t count, loff_t *ppos) ··· 1138 971 { 1139 972 struct dentry *d = iio_get_debugfs_dentry(indio_dev); 1140 973 struct ad9467_state *st = iio_priv(indio_dev); 974 + char attr_name[32]; 975 + unsigned int chan; 1141 976 1142 977 if (!IS_ENABLED(CONFIG_DEBUG_FS)) 1143 978 return; 1144 979 980 + st->chan_test = devm_kcalloc(&st->spi->dev, st->info->num_channels, 981 + sizeof(*st->chan_test), GFP_KERNEL); 982 + if (!st->chan_test) 983 + return; 984 + 1145 985 debugfs_create_file("calibration_table_dump", 0400, d, st, 1146 986 &ad9467_calib_table_fops); 987 + 988 + for (chan = 0; chan < st->info->num_channels; chan++) { 989 + snprintf(attr_name, sizeof(attr_name), "in_voltage%u_test_mode", 990 + chan); 991 + st->chan_test[chan].idx = chan; 992 + st->chan_test[chan].st = st; 993 + debugfs_create_file(attr_name, 0600, d, &st->chan_test[chan], 994 + &ad9467_chan_test_mode_fops); 995 + } 996 + 997 + debugfs_create_file("in_voltage_test_mode_available", 0400, d, st, 998 + &ad9467_test_mode_available_fops); 999 + 1000 + iio_backend_debugfs_add(st->back, indio_dev); 1147 1001 } 1148 1002 1149 1003 static int ad9467_probe(struct spi_device *spi)