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: accel: adxl313: add activity sensing

Add support for configuring an activity detection threshold. Extend the
interrupt handler to process activity-related interrupts, and provide
functions to set the threshold as well as to enable or disable activity
sensing. Additionally, introduce a virtual channel that represents the
logical AND of the x, y, and z axes in the IIO channel.

This patch serves as a preparatory step; some definitions and functions
introduced here are intended to be extended later to support inactivity
detection.

Signed-off-by: Lothar Rubusch <l.rubusch@gmail.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://patch.msgid.link/20250702230819.19353-5-l.rubusch@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Lothar Rubusch and committed by
Jonathan Cameron
385eb69e ff8093fa

+306
+306
drivers/iio/accel/adxl313_core.c
··· 13 13 #include <linux/overflow.h> 14 14 #include <linux/property.h> 15 15 #include <linux/regmap.h> 16 + #include <linux/units.h> 16 17 17 18 #include <linux/iio/buffer.h> 19 + #include <linux/iio/events.h> 18 20 #include <linux/iio/kfifo_buf.h> 19 21 20 22 #include "adxl313.h" ··· 26 24 #define ADXL313_INT2 2 27 25 28 26 #define ADXL313_REG_XYZ_BASE ADXL313_REG_DATA_AXIS(0) 27 + 28 + #define ADXL313_ACT_XYZ_EN GENMASK(6, 4) 29 + 30 + /* activity/inactivity */ 31 + enum adxl313_activity_type { 32 + ADXL313_ACTIVITY, 33 + }; 34 + 35 + static const unsigned int adxl313_act_int_reg[] = { 36 + [ADXL313_ACTIVITY] = ADXL313_INT_ACTIVITY, 37 + }; 38 + 39 + static const unsigned int adxl313_act_thresh_reg[] = { 40 + [ADXL313_ACTIVITY] = ADXL313_REG_THRESH_ACT, 41 + }; 29 42 30 43 static const struct regmap_range adxl312_readable_reg_range[] = { 31 44 regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0), ··· 244 227 }, \ 245 228 } 246 229 230 + static const struct iio_event_spec adxl313_activity_events[] = { 231 + { 232 + .type = IIO_EV_TYPE_MAG, 233 + .dir = IIO_EV_DIR_RISING, 234 + .mask_separate = BIT(IIO_EV_INFO_ENABLE), 235 + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), 236 + }, 237 + }; 238 + 247 239 enum adxl313_chans { 248 240 chan_x, chan_y, chan_z, 249 241 }; ··· 261 235 ADXL313_ACCEL_CHANNEL(0, chan_x, X), 262 236 ADXL313_ACCEL_CHANNEL(1, chan_y, Y), 263 237 ADXL313_ACCEL_CHANNEL(2, chan_z, Z), 238 + { 239 + .type = IIO_ACCEL, 240 + .modified = 1, 241 + .channel2 = IIO_MOD_X_OR_Y_OR_Z, 242 + .scan_index = -1, /* Fake channel for axis OR'ing */ 243 + .event_spec = adxl313_activity_events, 244 + .num_event_specs = ARRAY_SIZE(adxl313_activity_events), 245 + }, 264 246 }; 265 247 266 248 static const unsigned long adxl313_scan_masks[] = { ··· 329 295 default: 330 296 return -EINVAL; 331 297 } 298 + } 299 + 300 + static int adxl313_is_act_inact_en(struct adxl313_data *data, 301 + enum adxl313_activity_type type) 302 + { 303 + unsigned int axis_ctrl; 304 + unsigned int regval; 305 + int ret; 306 + 307 + ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, &axis_ctrl); 308 + if (ret) 309 + return ret; 310 + 311 + /* Check if axis for activity are enabled */ 312 + switch (type) { 313 + case ADXL313_ACTIVITY: 314 + if (!FIELD_GET(ADXL313_ACT_XYZ_EN, axis_ctrl)) 315 + return false; 316 + break; 317 + default: 318 + return -EINVAL; 319 + } 320 + 321 + /* Check if specific interrupt is enabled */ 322 + ret = regmap_read(data->regmap, ADXL313_REG_INT_ENABLE, &regval); 323 + if (ret) 324 + return ret; 325 + 326 + return adxl313_act_int_reg[type] & regval; 327 + } 328 + 329 + static int adxl313_set_act_inact_en(struct adxl313_data *data, 330 + enum adxl313_activity_type type, 331 + bool cmd_en) 332 + { 333 + unsigned int axis_ctrl; 334 + unsigned int threshold; 335 + int ret; 336 + 337 + if (cmd_en) { 338 + /* When turning on, check if threshold is valid */ 339 + ret = regmap_read(data->regmap, adxl313_act_thresh_reg[type], 340 + &threshold); 341 + if (ret) 342 + return ret; 343 + 344 + if (!threshold) /* Just ignore the command if threshold is 0 */ 345 + return 0; 346 + } 347 + 348 + /* Start modifying configuration registers */ 349 + ret = adxl313_set_measure_en(data, false); 350 + if (ret) 351 + return ret; 352 + 353 + /* Enable axis according to the command */ 354 + switch (type) { 355 + case ADXL313_ACTIVITY: 356 + axis_ctrl = ADXL313_ACT_XYZ_EN; 357 + break; 358 + default: 359 + return -EINVAL; 360 + } 361 + ret = regmap_assign_bits(data->regmap, ADXL313_REG_ACT_INACT_CTL, 362 + axis_ctrl, cmd_en); 363 + if (ret) 364 + return ret; 365 + 366 + /* Enable the interrupt line, according to the command */ 367 + ret = regmap_assign_bits(data->regmap, ADXL313_REG_INT_ENABLE, 368 + adxl313_act_int_reg[type], cmd_en); 369 + if (ret) 370 + return ret; 371 + 372 + return adxl313_set_measure_en(data, true); 332 373 } 333 374 334 375 static int adxl313_read_raw(struct iio_dev *indio_dev, ··· 474 365 val / 4); 475 366 case IIO_CHAN_INFO_SAMP_FREQ: 476 367 return adxl313_set_odr(data, val, val2); 368 + default: 369 + return -EINVAL; 370 + } 371 + } 372 + 373 + static int adxl313_read_mag_config(struct adxl313_data *data, 374 + enum iio_event_direction dir, 375 + enum adxl313_activity_type type_act) 376 + { 377 + switch (dir) { 378 + case IIO_EV_DIR_RISING: 379 + return !!adxl313_is_act_inact_en(data, type_act); 380 + default: 381 + return -EINVAL; 382 + } 383 + } 384 + 385 + static int adxl313_write_mag_config(struct adxl313_data *data, 386 + enum iio_event_direction dir, 387 + enum adxl313_activity_type type_act, 388 + bool state) 389 + { 390 + switch (dir) { 391 + case IIO_EV_DIR_RISING: 392 + return adxl313_set_act_inact_en(data, type_act, state); 393 + default: 394 + return -EINVAL; 395 + } 396 + } 397 + 398 + static int adxl313_read_event_config(struct iio_dev *indio_dev, 399 + const struct iio_chan_spec *chan, 400 + enum iio_event_type type, 401 + enum iio_event_direction dir) 402 + { 403 + struct adxl313_data *data = iio_priv(indio_dev); 404 + 405 + switch (type) { 406 + case IIO_EV_TYPE_MAG: 407 + return adxl313_read_mag_config(data, dir, 408 + ADXL313_ACTIVITY); 409 + default: 410 + return -EINVAL; 411 + } 412 + } 413 + 414 + static int adxl313_write_event_config(struct iio_dev *indio_dev, 415 + const struct iio_chan_spec *chan, 416 + enum iio_event_type type, 417 + enum iio_event_direction dir, 418 + bool state) 419 + { 420 + struct adxl313_data *data = iio_priv(indio_dev); 421 + 422 + switch (type) { 423 + case IIO_EV_TYPE_MAG: 424 + return adxl313_write_mag_config(data, dir, 425 + ADXL313_ACTIVITY, 426 + state); 427 + default: 428 + return -EINVAL; 429 + } 430 + } 431 + 432 + static int adxl313_read_mag_value(struct adxl313_data *data, 433 + enum iio_event_direction dir, 434 + enum iio_event_info info, 435 + enum adxl313_activity_type type_act, 436 + int *val, int *val2) 437 + { 438 + unsigned int threshold; 439 + int ret; 440 + 441 + switch (info) { 442 + case IIO_EV_INFO_VALUE: 443 + switch (dir) { 444 + case IIO_EV_DIR_RISING: 445 + ret = regmap_read(data->regmap, 446 + adxl313_act_thresh_reg[type_act], 447 + &threshold); 448 + if (ret) 449 + return ret; 450 + *val = threshold * 15625; 451 + *val2 = MICRO; 452 + return IIO_VAL_FRACTIONAL; 453 + default: 454 + return -EINVAL; 455 + } 456 + default: 457 + return -EINVAL; 458 + } 459 + } 460 + 461 + static int adxl313_write_mag_value(struct adxl313_data *data, 462 + enum iio_event_direction dir, 463 + enum iio_event_info info, 464 + enum adxl313_activity_type type_act, 465 + int val, int val2) 466 + { 467 + unsigned int regval; 468 + 469 + switch (info) { 470 + case IIO_EV_INFO_VALUE: 471 + /* Scale factor 15.625 mg/LSB */ 472 + regval = DIV_ROUND_CLOSEST(MICRO * val + val2, 15625); 473 + switch (dir) { 474 + case IIO_EV_DIR_RISING: 475 + return regmap_write(data->regmap, 476 + adxl313_act_thresh_reg[type_act], 477 + regval); 478 + default: 479 + return -EINVAL; 480 + } 481 + default: 482 + return -EINVAL; 483 + } 484 + } 485 + 486 + static int adxl313_read_event_value(struct iio_dev *indio_dev, 487 + const struct iio_chan_spec *chan, 488 + enum iio_event_type type, 489 + enum iio_event_direction dir, 490 + enum iio_event_info info, 491 + int *val, int *val2) 492 + { 493 + struct adxl313_data *data = iio_priv(indio_dev); 494 + 495 + switch (type) { 496 + case IIO_EV_TYPE_MAG: 497 + return adxl313_read_mag_value(data, dir, info, 498 + ADXL313_ACTIVITY, 499 + val, val2); 500 + default: 501 + return -EINVAL; 502 + } 503 + } 504 + 505 + static int adxl313_write_event_value(struct iio_dev *indio_dev, 506 + const struct iio_chan_spec *chan, 507 + enum iio_event_type type, 508 + enum iio_event_direction dir, 509 + enum iio_event_info info, 510 + int val, int val2) 511 + { 512 + struct adxl313_data *data = iio_priv(indio_dev); 513 + 514 + switch (type) { 515 + case IIO_EV_TYPE_MAG: 516 + return adxl313_write_mag_value(data, dir, info, 517 + ADXL313_ACTIVITY, 518 + val, val2); 477 519 default: 478 520 return -EINVAL; 479 521 } ··· 768 508 return 0; 769 509 } 770 510 511 + static int adxl313_push_events(struct iio_dev *indio_dev, int int_stat) 512 + { 513 + s64 ts = iio_get_time_ns(indio_dev); 514 + int ret = -ENOENT; 515 + 516 + if (FIELD_GET(ADXL313_INT_ACTIVITY, int_stat)) { 517 + ret = iio_push_event(indio_dev, 518 + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, 519 + IIO_MOD_X_OR_Y_OR_Z, 520 + IIO_EV_TYPE_MAG, 521 + IIO_EV_DIR_RISING), 522 + ts); 523 + if (ret) 524 + return ret; 525 + } 526 + 527 + return ret; 528 + } 529 + 771 530 static irqreturn_t adxl313_irq_handler(int irq, void *p) 772 531 { 773 532 struct iio_dev *indio_dev = p; ··· 795 516 796 517 if (regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, &int_stat)) 797 518 return IRQ_NONE; 519 + 520 + /* 521 + * In cases of sensor events not handled (still not implemented) by 522 + * this driver, the FIFO needs to be drained to become operational 523 + * again. In general the sensor configuration only should issue events 524 + * which were configured by this driver. Anyway a miss-configuration 525 + * easily might end up in a hanging sensor FIFO. 526 + */ 527 + if (adxl313_push_events(indio_dev, int_stat)) 528 + goto err_reset_fifo; 798 529 799 530 if (FIELD_GET(ADXL313_INT_WATERMARK, int_stat)) { 800 531 samples = adxl313_get_samples(data); ··· 839 550 static const struct iio_info adxl313_info = { 840 551 .read_raw = adxl313_read_raw, 841 552 .write_raw = adxl313_write_raw, 553 + .read_event_config = adxl313_read_event_config, 554 + .write_event_config = adxl313_write_event_config, 555 + .read_event_value = adxl313_read_event_value, 556 + .write_event_value = adxl313_write_event_value, 842 557 .read_avail = adxl313_read_freq_avail, 843 558 .hwfifo_set_watermark = adxl313_set_watermark, 844 559 .debugfs_reg_access = &adxl313_reg_access, ··· 977 684 ADXL313_INT_OVERRUN; 978 685 ret = regmap_assign_bits(data->regmap, ADXL313_REG_INT_MAP, 979 686 int_map_msk, int_line == ADXL313_INT2); 687 + if (ret) 688 + return ret; 689 + 690 + /* 691 + * Reset or configure the registers with reasonable default 692 + * values. As having 0 in most cases may result in undesirable 693 + * behavior if the interrupts are enabled. 694 + */ 695 + ret = regmap_write(data->regmap, ADXL313_REG_ACT_INACT_CTL, 0x00); 696 + if (ret) 697 + return ret; 698 + 699 + ret = regmap_write(data->regmap, ADXL313_REG_THRESH_ACT, 0x52); 980 700 if (ret) 981 701 return ret; 982 702