19
19
#include <linux/i2c.h>
20
20
#include <linux/init.h>
21
21
#include <linux/interrupt.h>
22
+ #include <linux/math64.h>
22
23
#include <linux/module.h>
23
24
#include <linux/mutex.h>
24
25
#include <linux/pm.h>
66
67
67
68
#define HDC3020_CRC8_POLYNOMIAL 0x31
68
69
69
- #define HDC3020_MIN_TEMP -40
70
- #define HDC3020_MAX_TEMP 125
70
+ #define HDC3020_MIN_TEMP_MICRO -39872968
71
+ #define HDC3020_MAX_TEMP_MICRO 124875639
72
+ #define HDC3020_MAX_TEMP_HYST_MICRO 164748607
73
+ #define HDC3020_MAX_HUM_MICRO 99220264
71
74
72
75
struct hdc3020_data {
73
76
struct i2c_client * client ;
@@ -368,6 +371,105 @@ static int hdc3020_write_raw(struct iio_dev *indio_dev,
368
371
return - EINVAL ;
369
372
}
370
373
374
+ static int hdc3020_thresh_get_temp (u16 thresh )
375
+ {
376
+ int temp ;
377
+
378
+ /*
379
+ * Get the temperature threshold from 9 LSBs, shift them to get
380
+ * the truncated temperature threshold representation and
381
+ * calculate the threshold according to the formula in the
382
+ * datasheet. Result is degree celsius scaled by 65535.
383
+ */
384
+ temp = FIELD_GET (HDC3020_THRESH_TEMP_MASK , thresh ) <<
385
+ HDC3020_THRESH_TEMP_TRUNC_SHIFT ;
386
+
387
+ return -2949075 + (175 * temp );
388
+ }
389
+
390
+ static int hdc3020_thresh_get_hum (u16 thresh )
391
+ {
392
+ int hum ;
393
+
394
+ /*
395
+ * Get the humidity threshold from 7 MSBs, shift them to get the
396
+ * truncated humidity threshold representation and calculate the
397
+ * threshold according to the formula in the datasheet. Result is
398
+ * percent scaled by 65535.
399
+ */
400
+ hum = FIELD_GET (HDC3020_THRESH_HUM_MASK , thresh ) <<
401
+ HDC3020_THRESH_HUM_TRUNC_SHIFT ;
402
+
403
+ return hum * 100 ;
404
+ }
405
+
406
+ static u16 hdc3020_thresh_set_temp (int s_temp , u16 curr_thresh )
407
+ {
408
+ u64 temp ;
409
+ u16 thresh ;
410
+
411
+ /*
412
+ * Calculate temperature threshold, shift it down to get the
413
+ * truncated threshold representation in the 9LSBs while keeping
414
+ * the current humidity threshold in the 7 MSBs.
415
+ */
416
+ temp = (u64 )(s_temp + 45000000 ) * 65535ULL ;
417
+ temp = div_u64 (temp , 1000000 * 175 ) >> HDC3020_THRESH_TEMP_TRUNC_SHIFT ;
418
+ thresh = FIELD_PREP (HDC3020_THRESH_TEMP_MASK , temp );
419
+ thresh |= (FIELD_GET (HDC3020_THRESH_HUM_MASK , curr_thresh ) <<
420
+ HDC3020_THRESH_HUM_TRUNC_SHIFT );
421
+
422
+ return thresh ;
423
+ }
424
+
425
+ static u16 hdc3020_thresh_set_hum (int s_hum , u16 curr_thresh )
426
+ {
427
+ u64 hum ;
428
+ u16 thresh ;
429
+
430
+ /*
431
+ * Calculate humidity threshold, shift it down and up to get the
432
+ * truncated threshold representation in the 7MSBs while keeping
433
+ * the current temperature threshold in the 9 LSBs.
434
+ */
435
+ hum = (u64 )(s_hum ) * 65535ULL ;
436
+ hum = div_u64 (hum , 1000000 * 100 ) >> HDC3020_THRESH_HUM_TRUNC_SHIFT ;
437
+ thresh = FIELD_PREP (HDC3020_THRESH_HUM_MASK , hum );
438
+ thresh |= FIELD_GET (HDC3020_THRESH_TEMP_MASK , curr_thresh );
439
+
440
+ return thresh ;
441
+ }
442
+
443
+ static
444
+ int hdc3020_thresh_clr (s64 s_thresh , s64 s_hyst , enum iio_event_direction dir )
445
+ {
446
+ s64 s_clr ;
447
+
448
+ /*
449
+ * Include directions when calculation the clear value,
450
+ * since hysteresis is unsigned by definition and the
451
+ * clear value is an absolute value which is signed.
452
+ */
453
+ if (dir == IIO_EV_DIR_RISING )
454
+ s_clr = s_thresh - s_hyst ;
455
+ else
456
+ s_clr = s_thresh + s_hyst ;
457
+
458
+ /* Divide by 65535 to get units of micro */
459
+ return div_s64 (s_clr , 65535 );
460
+ }
461
+
462
+ static int _hdc3020_write_thresh (struct hdc3020_data * data , u16 reg , u16 val )
463
+ {
464
+ u8 buf [5 ];
465
+
466
+ put_unaligned_be16 (reg , buf );
467
+ put_unaligned_be16 (val , buf + 2 );
468
+ buf [4 ] = crc8 (hdc3020_crc8_table , buf + 2 , 2 , CRC8_INIT_VALUE );
469
+
470
+ return hdc3020_write_bytes (data , buf , 5 );
471
+ }
472
+
371
473
static int hdc3020_write_thresh (struct iio_dev * indio_dev ,
372
474
const struct iio_chan_spec * chan ,
373
475
enum iio_event_type type ,
@@ -376,67 +478,126 @@ static int hdc3020_write_thresh(struct iio_dev *indio_dev,
376
478
int val , int val2 )
377
479
{
378
480
struct hdc3020_data * data = iio_priv (indio_dev );
379
- u8 buf [5 ];
380
- u64 tmp ;
381
- u16 reg ;
382
- int ret ;
383
-
384
- /* Supported temperature range is from –40 to 125 degree celsius */
385
- if (val < HDC3020_MIN_TEMP || val > HDC3020_MAX_TEMP )
386
- return - EINVAL ;
387
-
388
- /* Select threshold register */
389
- if (info == IIO_EV_INFO_VALUE ) {
390
- if (dir == IIO_EV_DIR_RISING )
391
- reg = HDC3020_S_T_RH_THRESH_HIGH ;
392
- else
393
- reg = HDC3020_S_T_RH_THRESH_LOW ;
481
+ u16 reg , reg_val , reg_thresh_rd , reg_clr_rd , reg_thresh_wr , reg_clr_wr ;
482
+ s64 s_thresh , s_hyst , s_clr ;
483
+ int s_val , thresh , clr , ret ;
484
+
485
+ /* Select threshold registers */
486
+ if (dir == IIO_EV_DIR_RISING ) {
487
+ reg_thresh_rd = HDC3020_R_T_RH_THRESH_HIGH ;
488
+ reg_thresh_wr = HDC3020_S_T_RH_THRESH_HIGH ;
489
+ reg_clr_rd = HDC3020_R_T_RH_THRESH_HIGH_CLR ;
490
+ reg_clr_wr = HDC3020_S_T_RH_THRESH_HIGH_CLR ;
394
491
} else {
395
- if ( dir == IIO_EV_DIR_RISING )
396
- reg = HDC3020_S_T_RH_THRESH_HIGH_CLR ;
397
- else
398
- reg = HDC3020_S_T_RH_THRESH_LOW_CLR ;
492
+ reg_thresh_rd = HDC3020_R_T_RH_THRESH_LOW ;
493
+ reg_thresh_wr = HDC3020_S_T_RH_THRESH_LOW ;
494
+ reg_clr_rd = HDC3020_R_T_RH_THRESH_LOW_CLR ;
495
+ reg_clr_wr = HDC3020_S_T_RH_THRESH_LOW_CLR ;
399
496
}
400
497
401
498
guard (mutex )(& data -> lock );
402
- ret = hdc3020_read_be16 (data , reg );
499
+ ret = hdc3020_read_be16 (data , reg_thresh_rd );
500
+ if (ret < 0 )
501
+ return ret ;
502
+
503
+ thresh = ret ;
504
+ ret = hdc3020_read_be16 (data , reg_clr_rd );
403
505
if (ret < 0 )
404
506
return ret ;
405
507
508
+ clr = ret ;
509
+ /* Scale value to include decimal part into calculations */
510
+ s_val = (val < 0 ) ? (val * 1000000 - val2 ) : (val * 1000000 + val2 );
406
511
switch (chan -> type ) {
407
512
case IIO_TEMP :
408
- /*
409
- * Calculate temperature threshold, shift it down to get the
410
- * truncated threshold representation in the 9LSBs while keeping
411
- * the current humidity threshold in the 7 MSBs.
412
- */
413
- tmp = ((u64 )(((val + 45 ) * MICRO ) + val2 )) * 65535ULL ;
414
- tmp = div_u64 (tmp , MICRO * 175 );
415
- val = tmp >> HDC3020_THRESH_TEMP_TRUNC_SHIFT ;
416
- val = FIELD_PREP (HDC3020_THRESH_TEMP_MASK , val );
417
- val |= (FIELD_GET (HDC3020_THRESH_HUM_MASK , ret ) <<
418
- HDC3020_THRESH_HUM_TRUNC_SHIFT );
513
+ switch (info ) {
514
+ case IIO_EV_INFO_VALUE :
515
+ s_val = max (s_val , HDC3020_MIN_TEMP_MICRO );
516
+ s_val = min (s_val , HDC3020_MAX_TEMP_MICRO );
517
+ reg = reg_thresh_wr ;
518
+ reg_val = hdc3020_thresh_set_temp (s_val , thresh );
519
+ ret = _hdc3020_write_thresh (data , reg , reg_val );
520
+ if (ret < 0 )
521
+ return ret ;
522
+
523
+ /* Calculate old hysteresis */
524
+ s_thresh = (s64 )hdc3020_thresh_get_temp (thresh ) * 1000000 ;
525
+ s_clr = (s64 )hdc3020_thresh_get_temp (clr ) * 1000000 ;
526
+ s_hyst = div_s64 (abs (s_thresh - s_clr ), 65535 );
527
+ /* Set new threshold */
528
+ thresh = reg_val ;
529
+ /* Set old hysteresis */
530
+ s_val = s_hyst ;
531
+ fallthrough ;
532
+ case IIO_EV_INFO_HYSTERESIS :
533
+ /*
534
+ * Function hdc3020_thresh_get_temp returns temperature
535
+ * in degree celsius scaled by 65535. Scale by 1000000
536
+ * to be able to subtract scaled hysteresis value.
537
+ */
538
+ s_thresh = (s64 )hdc3020_thresh_get_temp (thresh ) * 1000000 ;
539
+ /*
540
+ * Units of s_val are in micro degree celsius, scale by
541
+ * 65535 to get same units as s_thresh.
542
+ */
543
+ s_val = min (abs (s_val ), HDC3020_MAX_TEMP_HYST_MICRO );
544
+ s_hyst = (s64 )s_val * 65535 ;
545
+ s_clr = hdc3020_thresh_clr (s_thresh , s_hyst , dir );
546
+ s_clr = max (s_clr , HDC3020_MIN_TEMP_MICRO );
547
+ s_clr = min (s_clr , HDC3020_MAX_TEMP_MICRO );
548
+ reg = reg_clr_wr ;
549
+ reg_val = hdc3020_thresh_set_temp (s_clr , clr );
550
+ break ;
551
+ default :
552
+ return - EOPNOTSUPP ;
553
+ }
419
554
break ;
420
555
case IIO_HUMIDITYRELATIVE :
421
- /*
422
- * Calculate humidity threshold, shift it down and up to get the
423
- * truncated threshold representation in the 7MSBs while keeping
424
- * the current temperature threshold in the 9 LSBs.
425
- */
426
- tmp = ((u64 )((val * MICRO ) + val2 )) * 65535ULL ;
427
- tmp = div_u64 (tmp , MICRO * 100 );
428
- val = tmp >> HDC3020_THRESH_HUM_TRUNC_SHIFT ;
429
- val = FIELD_PREP (HDC3020_THRESH_HUM_MASK , val );
430
- val |= FIELD_GET (HDC3020_THRESH_TEMP_MASK , ret );
556
+ s_val = (s_val < 0 ) ? 0 : min (s_val , HDC3020_MAX_HUM_MICRO );
557
+ switch (info ) {
558
+ case IIO_EV_INFO_VALUE :
559
+ reg = reg_thresh_wr ;
560
+ reg_val = hdc3020_thresh_set_hum (s_val , thresh );
561
+ ret = _hdc3020_write_thresh (data , reg , reg_val );
562
+ if (ret < 0 )
563
+ return ret ;
564
+
565
+ /* Calculate old hysteresis */
566
+ s_thresh = (s64 )hdc3020_thresh_get_hum (thresh ) * 1000000 ;
567
+ s_clr = (s64 )hdc3020_thresh_get_hum (clr ) * 1000000 ;
568
+ s_hyst = div_s64 (abs (s_thresh - s_clr ), 65535 );
569
+ /* Set new threshold */
570
+ thresh = reg_val ;
571
+ /* Try to set old hysteresis */
572
+ s_val = min (abs (s_hyst ), HDC3020_MAX_HUM_MICRO );
573
+ fallthrough ;
574
+ case IIO_EV_INFO_HYSTERESIS :
575
+ /*
576
+ * Function hdc3020_thresh_get_hum returns relative
577
+ * humidity in percent scaled by 65535. Scale by 1000000
578
+ * to be able to subtract scaled hysteresis value.
579
+ */
580
+ s_thresh = (s64 )hdc3020_thresh_get_hum (thresh ) * 1000000 ;
581
+ /*
582
+ * Units of s_val are in micro percent, scale by 65535
583
+ * to get same units as s_thresh.
584
+ */
585
+ s_hyst = (s64 )s_val * 65535 ;
586
+ s_clr = hdc3020_thresh_clr (s_thresh , s_hyst , dir );
587
+ s_clr = max (s_clr , 0 );
588
+ s_clr = min (s_clr , HDC3020_MAX_HUM_MICRO );
589
+ reg = reg_clr_wr ;
590
+ reg_val = hdc3020_thresh_set_hum (s_clr , clr );
591
+ break ;
592
+ default :
593
+ return - EOPNOTSUPP ;
594
+ }
431
595
break ;
432
596
default :
433
597
return - EOPNOTSUPP ;
434
598
}
435
599
436
- put_unaligned_be16 (reg , buf );
437
- put_unaligned_be16 (val , buf + 2 );
438
- buf [4 ] = crc8 (hdc3020_crc8_table , buf + 2 , 2 , CRC8_INIT_VALUE );
439
- return hdc3020_write_bytes (data , buf , 5 );
600
+ return _hdc3020_write_thresh (data , reg , reg_val );
440
601
}
441
602
442
603
static int hdc3020_read_thresh (struct iio_dev * indio_dev ,
@@ -447,48 +608,60 @@ static int hdc3020_read_thresh(struct iio_dev *indio_dev,
447
608
int * val , int * val2 )
448
609
{
449
610
struct hdc3020_data * data = iio_priv (indio_dev );
450
- u16 reg ;
451
- int ret ;
611
+ u16 reg_thresh , reg_clr ;
612
+ int thresh , clr , ret ;
452
613
453
- /* Select threshold register */
454
- if (info == IIO_EV_INFO_VALUE ) {
455
- if (dir == IIO_EV_DIR_RISING )
456
- reg = HDC3020_R_T_RH_THRESH_HIGH ;
457
- else
458
- reg = HDC3020_R_T_RH_THRESH_LOW ;
614
+ /* Select threshold registers */
615
+ if (dir == IIO_EV_DIR_RISING ) {
616
+ reg_thresh = HDC3020_R_T_RH_THRESH_HIGH ;
617
+ reg_clr = HDC3020_R_T_RH_THRESH_HIGH_CLR ;
459
618
} else {
460
- if (dir == IIO_EV_DIR_RISING )
461
- reg = HDC3020_R_T_RH_THRESH_HIGH_CLR ;
462
- else
463
- reg = HDC3020_R_T_RH_THRESH_LOW_CLR ;
619
+ reg_thresh = HDC3020_R_T_RH_THRESH_LOW ;
620
+ reg_clr = HDC3020_R_T_RH_THRESH_LOW_CLR ;
464
621
}
465
622
466
623
guard (mutex )(& data -> lock );
467
- ret = hdc3020_read_be16 (data , reg );
624
+ ret = hdc3020_read_be16 (data , reg_thresh );
468
625
if (ret < 0 )
469
626
return ret ;
470
627
471
628
switch (chan -> type ) {
472
629
case IIO_TEMP :
473
- /*
474
- * Get the temperature threshold from 9 LSBs, shift them to get
475
- * the truncated temperature threshold representation and
476
- * calculate the threshold according to the formula in the
477
- * datasheet.
478
- */
479
- * val = FIELD_GET (HDC3020_THRESH_TEMP_MASK , ret );
480
- * val = * val << HDC3020_THRESH_TEMP_TRUNC_SHIFT ;
481
- * val = -2949075 + (175 * (* val ));
630
+ thresh = hdc3020_thresh_get_temp (ret );
631
+ switch (info ) {
632
+ case IIO_EV_INFO_VALUE :
633
+ * val = thresh ;
634
+ break ;
635
+ case IIO_EV_INFO_HYSTERESIS :
636
+ ret = hdc3020_read_be16 (data , reg_clr );
637
+ if (ret < 0 )
638
+ return ret ;
639
+
640
+ clr = hdc3020_thresh_get_temp (ret );
641
+ * val = abs (thresh - clr );
642
+ break ;
643
+ default :
644
+ return - EOPNOTSUPP ;
645
+ }
482
646
* val2 = 65535 ;
483
647
return IIO_VAL_FRACTIONAL ;
484
648
case IIO_HUMIDITYRELATIVE :
485
- /*
486
- * Get the humidity threshold from 7 MSBs, shift them to get the
487
- * truncated humidity threshold representation and calculate the
488
- * threshold according to the formula in the datasheet.
489
- */
490
- * val = FIELD_GET (HDC3020_THRESH_HUM_MASK , ret );
491
- * val = (* val << HDC3020_THRESH_HUM_TRUNC_SHIFT ) * 100 ;
649
+ thresh = hdc3020_thresh_get_hum (ret );
650
+ switch (info ) {
651
+ case IIO_EV_INFO_VALUE :
652
+ * val = thresh ;
653
+ break ;
654
+ case IIO_EV_INFO_HYSTERESIS :
655
+ ret = hdc3020_read_be16 (data , reg_clr );
656
+ if (ret < 0 )
657
+ return ret ;
658
+
659
+ clr = hdc3020_thresh_get_hum (ret );
660
+ * val = abs (thresh - clr );
661
+ break ;
662
+ default :
663
+ return - EOPNOTSUPP ;
664
+ }
492
665
* val2 = 65535 ;
493
666
return IIO_VAL_FRACTIONAL ;
494
667
default :
0 commit comments