1919#include <linux/i2c.h>
2020#include <linux/init.h>
2121#include <linux/interrupt.h>
22+ #include <linux/math64.h>
2223#include <linux/module.h>
2324#include <linux/mutex.h>
2425#include <linux/pm.h>
6667
6768#define HDC3020_CRC8_POLYNOMIAL 0x31
6869
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
7174
7275struct hdc3020_data {
7376 struct i2c_client * client ;
@@ -368,6 +371,105 @@ static int hdc3020_write_raw(struct iio_dev *indio_dev,
368371 return - EINVAL ;
369372}
370373
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+
371473static int hdc3020_write_thresh (struct iio_dev * indio_dev ,
372474 const struct iio_chan_spec * chan ,
373475 enum iio_event_type type ,
@@ -376,67 +478,126 @@ static int hdc3020_write_thresh(struct iio_dev *indio_dev,
376478 int val , int val2 )
377479{
378480 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 ;
394491 } 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 ;
399496 }
400497
401498 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 );
403505 if (ret < 0 )
404506 return ret ;
405507
508+ clr = ret ;
509+ /* Scale value to include decimal part into calculations */
510+ s_val = (val < 0 ) ? (val * 1000000 - val2 ) : (val * 1000000 + val2 );
406511 switch (chan -> type ) {
407512 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+ }
419554 break ;
420555 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+ }
431595 break ;
432596 default :
433597 return - EOPNOTSUPP ;
434598 }
435599
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 );
440601}
441602
442603static int hdc3020_read_thresh (struct iio_dev * indio_dev ,
@@ -447,48 +608,60 @@ static int hdc3020_read_thresh(struct iio_dev *indio_dev,
447608 int * val , int * val2 )
448609{
449610 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 ;
452613
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 ;
459618 } 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 ;
464621 }
465622
466623 guard (mutex )(& data -> lock );
467- ret = hdc3020_read_be16 (data , reg );
624+ ret = hdc3020_read_be16 (data , reg_thresh );
468625 if (ret < 0 )
469626 return ret ;
470627
471628 switch (chan -> type ) {
472629 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+ }
482646 * val2 = 65535 ;
483647 return IIO_VAL_FRACTIONAL ;
484648 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+ }
492665 * val2 = 65535 ;
493666 return IIO_VAL_FRACTIONAL ;
494667 default :
0 commit comments