wsa881x-temp-sensor.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /* Copyright (c) 2015, 2017-2019 The Linux Foundation. All rights reserved.
  3. */
  4. #include <linux/bitops.h>
  5. #include <linux/kernel.h>
  6. #include <linux/suspend.h>
  7. #include <linux/errno.h>
  8. #include <linux/delay.h>
  9. #include <linux/thermal.h>
  10. #include <sound/soc.h>
  11. #include "wsa881x-temp-sensor.h"
  12. #define T1_TEMP -10
  13. #define T2_TEMP 150
  14. #define LOW_TEMP_THRESHOLD 5
  15. #define HIGH_TEMP_THRESHOLD 45
  16. #define TEMP_INVALID 0xFFFF
  17. #define WSA881X_TEMP_RETRY 3
  18. /*
  19. * wsa881x_get_temp - get wsa temperature
  20. * @thermal: thermal zone device
  21. * @temp: temperature value
  22. *
  23. * Get the temperature of wsa881x.
  24. *
  25. * Return: 0 on success or negative error code on failure.
  26. */
  27. int wsa881x_get_temp(struct thermal_zone_device *thermal,
  28. int *temp)
  29. {
  30. struct wsa881x_tz_priv *pdata;
  31. struct snd_soc_component *component;
  32. struct wsa_temp_register reg;
  33. int dmeas, d1, d2;
  34. int ret = 0;
  35. int temp_val;
  36. int t1 = T1_TEMP;
  37. int t2 = T2_TEMP;
  38. u8 retry = WSA881X_TEMP_RETRY;
  39. if (!thermal)
  40. return -EINVAL;
  41. if (thermal->devdata) {
  42. pdata = thermal->devdata;
  43. if (pdata->component) {
  44. component = pdata->component;
  45. } else {
  46. pr_err("%s: codec is NULL\n", __func__);
  47. return -EINVAL;
  48. }
  49. } else {
  50. pr_err("%s: pdata is NULL\n", __func__);
  51. return -EINVAL;
  52. }
  53. if (atomic_cmpxchg(&pdata->is_suspend_spk, 1, 0)) {
  54. /*
  55. * get_temp query happens as part of POST_PM_SUSPEND
  56. * from thermal core. To avoid calls to slimbus
  57. * as part of this thermal query, return default temp
  58. * and reset the suspend flag.
  59. */
  60. if (!pdata->t0_init) {
  61. if (temp)
  62. *temp = pdata->curr_temp;
  63. return 0;
  64. }
  65. }
  66. temp_retry:
  67. if (pdata->wsa_temp_reg_read) {
  68. ret = pdata->wsa_temp_reg_read(component, &reg);
  69. if (ret) {
  70. pr_err("%s: temp read failed: %d, current temp: %d\n",
  71. __func__, ret, pdata->curr_temp);
  72. if (temp)
  73. *temp = pdata->curr_temp;
  74. return 0;
  75. }
  76. } else {
  77. pr_err("%s: wsa_temp_reg_read is NULL\n", __func__);
  78. return -EINVAL;
  79. }
  80. /*
  81. * Temperature register values are expected to be in the
  82. * following range.
  83. * d1_msb = 68 - 92 and d1_lsb = 0, 64, 128, 192
  84. * d2_msb = 185 -218 and d2_lsb = 0, 64, 128, 192
  85. */
  86. if ((reg.d1_msb < 68 || reg.d1_msb > 92) ||
  87. (!(reg.d1_lsb == 0 || reg.d1_lsb == 64 || reg.d1_lsb == 128 ||
  88. reg.d1_lsb == 192)) ||
  89. (reg.d2_msb < 185 || reg.d2_msb > 218) ||
  90. (!(reg.d2_lsb == 0 || reg.d2_lsb == 64 || reg.d2_lsb == 128 ||
  91. reg.d2_lsb == 192))) {
  92. printk_ratelimited("%s: Temperature registers[%d %d %d %d] are out of range\n",
  93. __func__, reg.d1_msb, reg.d1_lsb, reg.d2_msb,
  94. reg.d2_lsb);
  95. }
  96. dmeas = ((reg.dmeas_msb << 0x8) | reg.dmeas_lsb) >> 0x6;
  97. d1 = ((reg.d1_msb << 0x8) | reg.d1_lsb) >> 0x6;
  98. d2 = ((reg.d2_msb << 0x8) | reg.d2_lsb) >> 0x6;
  99. if (d1 == d2)
  100. temp_val = TEMP_INVALID;
  101. else
  102. temp_val = t1 + (((dmeas - d1) * (t2 - t1))/(d2 - d1));
  103. if (temp_val <= LOW_TEMP_THRESHOLD ||
  104. temp_val >= HIGH_TEMP_THRESHOLD) {
  105. pr_debug("%s: T0: %d is out of range[%d, %d]\n", __func__,
  106. temp_val, LOW_TEMP_THRESHOLD, HIGH_TEMP_THRESHOLD);
  107. if (retry--) {
  108. msleep(20);
  109. goto temp_retry;
  110. }
  111. }
  112. pdata->curr_temp = temp_val;
  113. if (temp)
  114. *temp = temp_val;
  115. pr_debug("%s: t0 measured: %d dmeas = %d, d1 = %d, d2 = %d\n",
  116. __func__, temp_val, dmeas, d1, d2);
  117. return ret;
  118. }
  119. EXPORT_SYMBOL(wsa881x_get_temp);
  120. static struct thermal_zone_device_ops wsa881x_thermal_ops = {
  121. .get_temp = wsa881x_get_temp,
  122. };
  123. static int wsa881x_pm_notify(struct notifier_block *nb,
  124. unsigned long mode, void *_unused)
  125. {
  126. struct wsa881x_tz_priv *pdata =
  127. container_of(nb, struct wsa881x_tz_priv, pm_nb);
  128. switch (mode) {
  129. case PM_SUSPEND_PREPARE:
  130. atomic_set(&pdata->is_suspend_spk, 1);
  131. break;
  132. default:
  133. break;
  134. }
  135. return 0;
  136. }
  137. int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata)
  138. {
  139. struct thermal_zone_device *tz_dev;
  140. if (tz_pdata == NULL) {
  141. pr_err("%s: thermal pdata is NULL\n", __func__);
  142. return -EINVAL;
  143. }
  144. /* Register with the thermal zone */
  145. tz_dev = thermal_zone_device_register(tz_pdata->name,
  146. 0, 0, tz_pdata,
  147. &wsa881x_thermal_ops, NULL, 0, 0);
  148. if (IS_ERR(tz_dev)) {
  149. pr_err("%s: thermal device register failed.\n", __func__);
  150. return -EINVAL;
  151. }
  152. tz_pdata->tz_dev = tz_dev;
  153. tz_pdata->pm_nb.notifier_call = wsa881x_pm_notify;
  154. register_pm_notifier(&tz_pdata->pm_nb);
  155. atomic_set(&tz_pdata->is_suspend_spk, 0);
  156. return 0;
  157. }
  158. EXPORT_SYMBOL(wsa881x_init_thermal);
  159. void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev)
  160. {
  161. struct wsa881x_tz_priv *pdata;
  162. if (tz_dev && tz_dev->devdata) {
  163. pdata = tz_dev->devdata;
  164. if (pdata)
  165. unregister_pm_notifier(&pdata->pm_nb);
  166. }
  167. if (tz_dev)
  168. thermal_zone_device_unregister(tz_dev);
  169. }
  170. EXPORT_SYMBOL(wsa881x_deinit_thermal);