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: ad7380: add alert support

The alert functionality is an out of range indicator and can be used as
an early indicator of an out of bounds conversion result.

ALERT_LOW_THRESHOLD and ALERT_HIGH_THRESHOLD registers are common to all
channels.

When using 1 SDO line (only mode supported by the driver right now), i.e
data outputs only on SDOA, SDOB (or SDOD for 4 channels variants) is
used as an alert pin. The alert pin is updated at the end of the
conversion (set to low if an alert occurs) and is cleared on a falling
edge of CS.

The ALERT register contains information about the exact alert status:
channel and direction. ALERT register can be accessed using debugfs if
enabled.

User can set high/low thresholds and enable alert detection using the
regular iio events attributes:

events/in_thresh_falling_value events/in_thresh_rising_value
events/thresh_either_en

In most use cases, user will hardwire the alert pin to trigger a shutdown.

In theory, we could generate userspace IIO events for alerts, but this
is not implemented yet for several reasons [1]. This can be implemented
later if a real use case actually requires it.

Signed-off-by: Julien Stephan <jstephan@baylibre.com>

[1] https://lore.kernel.org/all/4be16272-5197-4fa1-918c-c4cdfcaee02e@baylibre.com/

Reviewed-by: David Lechner <dlechner@baylibre.com>
Link: https://patch.msgid.link/20250108-ad7380-add-alert-support-v4-4-1751802471ba@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Julien Stephan and committed by
Jonathan Cameron
27d1a4db adc59fe0

+192
+192
drivers/iio/adc/ad7380.c
··· 34 34 #include <linux/util_macros.h> 35 35 36 36 #include <linux/iio/buffer.h> 37 + #include <linux/iio/events.h> 37 38 #include <linux/iio/iio.h> 38 39 #include <linux/iio/trigger_consumer.h> 39 40 #include <linux/iio/triggered_buffer.h> ··· 111 110 unsigned int num_vcm_supplies; 112 111 const unsigned long *available_scan_masks; 113 112 const struct ad7380_timing_specs *timing_specs; 113 + }; 114 + 115 + static const struct iio_event_spec ad7380_events[] = { 116 + { 117 + .type = IIO_EV_TYPE_THRESH, 118 + .dir = IIO_EV_DIR_RISING, 119 + .mask_shared_by_dir = BIT(IIO_EV_INFO_VALUE), 120 + }, 121 + { 122 + .type = IIO_EV_TYPE_THRESH, 123 + .dir = IIO_EV_DIR_FALLING, 124 + .mask_shared_by_dir = BIT(IIO_EV_INFO_VALUE), 125 + }, 126 + { 127 + .type = IIO_EV_TYPE_THRESH, 128 + .dir = IIO_EV_DIR_EITHER, 129 + .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE), 130 + }, 114 131 }; 115 132 116 133 enum { ··· 233 214 .has_ext_scan_type = 1, \ 234 215 .ext_scan_type = ad7380_scan_type_##bits##_##sign, \ 235 216 .num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits##_##sign), \ 217 + .event_spec = ad7380_events, \ 218 + .num_event_specs = ARRAY_SIZE(ad7380_events), \ 236 219 } 237 220 238 221 #define AD7380_CHANNEL(index, bits, diff, sign) \ ··· 1178 1157 : AD7380_SCAN_TYPE_NORMAL; 1179 1158 } 1180 1159 1160 + static int ad7380_read_event_config(struct iio_dev *indio_dev, 1161 + const struct iio_chan_spec *chan, 1162 + enum iio_event_type type, 1163 + enum iio_event_direction dir) 1164 + { 1165 + struct ad7380_state *st = iio_priv(indio_dev); 1166 + int tmp, ret; 1167 + 1168 + ret = iio_device_claim_direct_mode(indio_dev); 1169 + if (ret) 1170 + return ret; 1171 + 1172 + ret = regmap_read(st->regmap, AD7380_REG_ADDR_CONFIG1, &tmp); 1173 + 1174 + iio_device_release_direct_mode(indio_dev); 1175 + 1176 + if (ret) 1177 + return ret; 1178 + 1179 + return FIELD_GET(AD7380_CONFIG1_ALERTEN, tmp); 1180 + } 1181 + 1182 + static int ad7380_write_event_config(struct iio_dev *indio_dev, 1183 + const struct iio_chan_spec *chan, 1184 + enum iio_event_type type, 1185 + enum iio_event_direction dir, 1186 + bool state) 1187 + { 1188 + struct ad7380_state *st = iio_priv(indio_dev); 1189 + int ret; 1190 + 1191 + ret = iio_device_claim_direct_mode(indio_dev); 1192 + if (ret) 1193 + return ret; 1194 + 1195 + ret = regmap_update_bits(st->regmap, 1196 + AD7380_REG_ADDR_CONFIG1, 1197 + AD7380_CONFIG1_ALERTEN, 1198 + FIELD_PREP(AD7380_CONFIG1_ALERTEN, state)); 1199 + 1200 + iio_device_release_direct_mode(indio_dev); 1201 + 1202 + return ret; 1203 + } 1204 + 1205 + static int ad7380_get_alert_th(struct ad7380_state *st, 1206 + enum iio_event_direction dir, 1207 + int *val) 1208 + { 1209 + int ret, tmp; 1210 + 1211 + switch (dir) { 1212 + case IIO_EV_DIR_RISING: 1213 + ret = regmap_read(st->regmap, 1214 + AD7380_REG_ADDR_ALERT_HIGH_TH, 1215 + &tmp); 1216 + if (ret) 1217 + return ret; 1218 + 1219 + *val = FIELD_GET(AD7380_ALERT_HIGH_TH, tmp); 1220 + return IIO_VAL_INT; 1221 + case IIO_EV_DIR_FALLING: 1222 + ret = regmap_read(st->regmap, 1223 + AD7380_REG_ADDR_ALERT_LOW_TH, 1224 + &tmp); 1225 + if (ret) 1226 + return ret; 1227 + 1228 + *val = FIELD_GET(AD7380_ALERT_LOW_TH, tmp); 1229 + return IIO_VAL_INT; 1230 + default: 1231 + return -EINVAL; 1232 + } 1233 + } 1234 + 1235 + static int ad7380_read_event_value(struct iio_dev *indio_dev, 1236 + const struct iio_chan_spec *chan, 1237 + enum iio_event_type type, 1238 + enum iio_event_direction dir, 1239 + enum iio_event_info info, 1240 + int *val, int *val2) 1241 + { 1242 + struct ad7380_state *st = iio_priv(indio_dev); 1243 + int ret; 1244 + 1245 + switch (info) { 1246 + case IIO_EV_INFO_VALUE: 1247 + ret = iio_device_claim_direct_mode(indio_dev); 1248 + if (ret) 1249 + return ret; 1250 + 1251 + ret = ad7380_get_alert_th(st, dir, val); 1252 + 1253 + iio_device_release_direct_mode(indio_dev); 1254 + return ret; 1255 + default: 1256 + return -EINVAL; 1257 + } 1258 + } 1259 + 1260 + static int ad7380_set_alert_th(struct iio_dev *indio_dev, 1261 + const struct iio_chan_spec *chan, 1262 + enum iio_event_direction dir, 1263 + int val) 1264 + { 1265 + struct ad7380_state *st = iio_priv(indio_dev); 1266 + const struct iio_scan_type *scan_type; 1267 + u16 th; 1268 + 1269 + /* 1270 + * According to the datasheet, 1271 + * AD7380_REG_ADDR_ALERT_HIGH_TH[11:0] are the 12 MSB of the 1272 + * 16-bits internal alert high register. LSB are set to 0xf. 1273 + * AD7380_REG_ADDR_ALERT_LOW_TH[11:0] are the 12 MSB of the 1274 + * 16 bits internal alert low register. LSB are set to 0x0. 1275 + * 1276 + * When alert is enabled the conversion from the adc is compared 1277 + * immediately to the alert high/low thresholds, before any 1278 + * oversampling. This means that the thresholds are the same for 1279 + * normal mode and oversampling mode. 1280 + */ 1281 + 1282 + /* Extract the 12 MSB of val */ 1283 + scan_type = iio_get_current_scan_type(indio_dev, chan); 1284 + if (IS_ERR(scan_type)) 1285 + return PTR_ERR(scan_type); 1286 + 1287 + th = val >> (scan_type->realbits - 12); 1288 + 1289 + switch (dir) { 1290 + case IIO_EV_DIR_RISING: 1291 + return regmap_write(st->regmap, 1292 + AD7380_REG_ADDR_ALERT_HIGH_TH, 1293 + th); 1294 + case IIO_EV_DIR_FALLING: 1295 + return regmap_write(st->regmap, 1296 + AD7380_REG_ADDR_ALERT_LOW_TH, 1297 + th); 1298 + default: 1299 + return -EINVAL; 1300 + } 1301 + } 1302 + 1303 + static int ad7380_write_event_value(struct iio_dev *indio_dev, 1304 + const struct iio_chan_spec *chan, 1305 + enum iio_event_type type, 1306 + enum iio_event_direction dir, 1307 + enum iio_event_info info, 1308 + int val, int val2) 1309 + { 1310 + int ret; 1311 + 1312 + switch (info) { 1313 + case IIO_EV_INFO_VALUE: 1314 + ret = iio_device_claim_direct_mode(indio_dev); 1315 + if (ret) 1316 + return ret; 1317 + 1318 + ret = ad7380_set_alert_th(indio_dev, chan, dir, val); 1319 + 1320 + iio_device_release_direct_mode(indio_dev); 1321 + return ret; 1322 + default: 1323 + return -EINVAL; 1324 + } 1325 + } 1326 + 1181 1327 static const struct iio_info ad7380_info = { 1182 1328 .read_raw = &ad7380_read_raw, 1183 1329 .read_avail = &ad7380_read_avail, 1184 1330 .write_raw = &ad7380_write_raw, 1185 1331 .get_current_scan_type = &ad7380_get_current_scan_type, 1186 1332 .debugfs_reg_access = &ad7380_debugfs_reg_access, 1333 + .read_event_config = &ad7380_read_event_config, 1334 + .write_event_config = &ad7380_write_event_config, 1335 + .read_event_value = &ad7380_read_event_value, 1336 + .write_event_value = &ad7380_write_event_value, 1187 1337 }; 1188 1338 1189 1339 static int ad7380_init(struct ad7380_state *st, bool external_ref_en)