armada_thermal.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. * Copyright (C) 2019 Marvell International Ltd.
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. * https://spdx.org/licenses
  6. */
  7. #include <common/debug.h>
  8. #include <drivers/delay_timer.h>
  9. #include <errno.h>
  10. #include <lib/mmio.h>
  11. #include <mvebu.h>
  12. #include <stdbool.h>
  13. #include "dfx.h"
  14. /* #define DEBUG_DFX */
  15. #ifdef DEBUG_DFX
  16. #define debug(format...) NOTICE(format)
  17. #else
  18. #define debug(format, arg...)
  19. #endif
  20. #define TSEN_CTRL0 0xf06f8084
  21. #define TSEN_CTRL0_START BIT(0)
  22. #define TSEN_CTRL0_RESET BIT(1)
  23. #define TSEN_CTRL0_ENABLE BIT(2)
  24. #define TSEN_CTRL0_AVG_BYPASS BIT(6)
  25. #define TSEN_CTRL0_CHAN_SHIFT 13
  26. #define TSEN_CTRL0_CHAN_MASK 0xF
  27. #define TSEN_CTRL0_OSR_SHIFT 24
  28. #define TSEN_CTRL0_OSR_MAX 0x3
  29. #define TSEN_CTRL0_MODE_SHIFT 30
  30. #define TSEN_CTRL0_MODE_EXTERNAL 0x2U
  31. #define TSEN_CTRL0_MODE_MASK 0x3U
  32. #define TSEN_CTRL1 0xf06f8088
  33. #define TSEN_CTRL1_INT_EN BIT(25)
  34. #define TSEN_CTRL1_HYST_SHIFT 19
  35. #define TSEN_CTRL1_HYST_MASK (0x3 << TSEN_CTRL1_HYST_SHIFT)
  36. #define TSEN_CTRL1_THRESH_SHIFT 3
  37. #define TSEN_CTRL1_THRESH_MASK (0x3ff << TSEN_CTRL1_THRESH_SHIFT)
  38. #define TSEN_STATUS 0xf06f808c
  39. #define TSEN_STATUS_VALID_OFFSET 16
  40. #define TSEN_STATUS_VALID_MASK (0x1 << TSEN_STATUS_VALID_OFFSET)
  41. #define TSEN_STATUS_TEMP_OUT_OFFSET 0
  42. #define TSEN_STATUS_TEMP_OUT_MASK (0x3FF << TSEN_STATUS_TEMP_OUT_OFFSET)
  43. #define DFX_SERVER_IRQ_SUM_MASK_REG 0xf06f8104
  44. #define DFX_SERVER_IRQ_EN BIT(1)
  45. #define DFX_IRQ_CAUSE_REG 0xf06f8108
  46. #define DFX_IRQ_MASK_REG 0xf06f810c
  47. #define DFX_IRQ_TSEN_OVERHEAT_OFFSET BIT(22)
  48. #define THERMAL_SEN_OUTPUT_MSB 512
  49. #define THERMAL_SEN_OUTPUT_COMP 1024
  50. #define COEF_M 423
  51. #define COEF_B -150000LL
  52. static void armada_ap806_thermal_read(u_register_t *temp)
  53. {
  54. uint32_t reg;
  55. reg = mmio_read_32(TSEN_STATUS);
  56. reg = ((reg & TSEN_STATUS_TEMP_OUT_MASK) >>
  57. TSEN_STATUS_TEMP_OUT_OFFSET);
  58. /*
  59. * TSEN output format is signed as a 2s complement number
  60. * ranging from-512 to +511. when MSB is set, need to
  61. * calculate the complement number
  62. */
  63. if (reg >= THERMAL_SEN_OUTPUT_MSB)
  64. reg -= THERMAL_SEN_OUTPUT_COMP;
  65. *temp = ((COEF_M * ((signed int)reg)) - COEF_B);
  66. }
  67. static void armada_ap806_thermal_irq(void)
  68. {
  69. /* Dummy read, register ROC */
  70. mmio_read_32(DFX_IRQ_CAUSE_REG);
  71. }
  72. static void armada_ap806_thermal_overheat_irq_init(void)
  73. {
  74. uint32_t reg;
  75. /* Clear DFX temperature IRQ cause */
  76. reg = mmio_read_32(DFX_IRQ_CAUSE_REG);
  77. /* Enable DFX Temperature IRQ */
  78. reg = mmio_read_32(DFX_IRQ_MASK_REG);
  79. reg |= DFX_IRQ_TSEN_OVERHEAT_OFFSET;
  80. mmio_write_32(DFX_IRQ_MASK_REG, reg);
  81. /* Enable DFX server IRQ */
  82. reg = mmio_read_32(DFX_SERVER_IRQ_SUM_MASK_REG);
  83. reg |= DFX_SERVER_IRQ_EN;
  84. mmio_write_32(DFX_SERVER_IRQ_SUM_MASK_REG, reg);
  85. /* Enable overheat interrupt */
  86. reg = mmio_read_32(TSEN_CTRL1);
  87. reg |= TSEN_CTRL1_INT_EN;
  88. mmio_write_32(TSEN_CTRL1, reg);
  89. }
  90. static unsigned int armada_mc_to_reg_temp(unsigned int temp_mc)
  91. {
  92. unsigned int sample;
  93. sample = (temp_mc + COEF_B) / COEF_M;
  94. return sample & 0x3ff;
  95. }
  96. /*
  97. * The documentation states:
  98. * high/low watermark = threshold +/- 0.4761 * 2^(hysteresis + 2)
  99. * which is the mathematical derivation for:
  100. * 0x0 <=> 1.9°C, 0x1 <=> 3.8°C, 0x2 <=> 7.6°C, 0x3 <=> 15.2°C
  101. */
  102. static unsigned int hyst_levels_mc[] = {1900, 3800, 7600, 15200};
  103. static unsigned int armada_mc_to_reg_hyst(int hyst_mc)
  104. {
  105. int i;
  106. /*
  107. * We will always take the smallest possible hysteresis to avoid risking
  108. * the hardware integrity by enlarging the threshold by +8°C in the
  109. * worst case.
  110. */
  111. for (i = ARRAY_SIZE(hyst_levels_mc) - 1; i > 0; i--)
  112. if (hyst_mc >= hyst_levels_mc[i])
  113. break;
  114. return i;
  115. }
  116. static void armada_ap806_thermal_threshold(int thresh_mc, int hyst_mc)
  117. {
  118. uint32_t ctrl1;
  119. unsigned int threshold = armada_mc_to_reg_temp(thresh_mc);
  120. unsigned int hysteresis = armada_mc_to_reg_hyst(hyst_mc);
  121. ctrl1 = mmio_read_32(TSEN_CTRL1);
  122. /* Set Threshold */
  123. if (thresh_mc >= 0) {
  124. ctrl1 &= ~(TSEN_CTRL1_THRESH_MASK);
  125. ctrl1 |= threshold << TSEN_CTRL1_THRESH_SHIFT;
  126. }
  127. /* Set Hysteresis */
  128. if (hyst_mc >= 0) {
  129. ctrl1 &= ~(TSEN_CTRL1_HYST_MASK);
  130. ctrl1 |= hysteresis << TSEN_CTRL1_HYST_SHIFT;
  131. }
  132. mmio_write_32(TSEN_CTRL1, ctrl1);
  133. }
  134. static void armada_select_channel(int channel)
  135. {
  136. uint32_t ctrl0;
  137. /* Stop the measurements */
  138. ctrl0 = mmio_read_32(TSEN_CTRL0);
  139. ctrl0 &= ~TSEN_CTRL0_START;
  140. mmio_write_32(TSEN_CTRL0, ctrl0);
  141. /* Reset the mode, internal sensor will be automatically selected */
  142. ctrl0 &= ~(TSEN_CTRL0_MODE_MASK << TSEN_CTRL0_MODE_SHIFT);
  143. /* Other channels are external and should be selected accordingly */
  144. if (channel) {
  145. /* Change the mode to external */
  146. ctrl0 |= TSEN_CTRL0_MODE_EXTERNAL <<
  147. TSEN_CTRL0_MODE_SHIFT;
  148. /* Select the sensor */
  149. ctrl0 &= ~(TSEN_CTRL0_CHAN_MASK << TSEN_CTRL0_CHAN_SHIFT);
  150. ctrl0 |= (channel - 1) << TSEN_CTRL0_CHAN_SHIFT;
  151. }
  152. /* Actually set the mode/channel */
  153. mmio_write_32(TSEN_CTRL0, ctrl0);
  154. /* Re-start the measurements */
  155. ctrl0 |= TSEN_CTRL0_START;
  156. mmio_write_32(TSEN_CTRL0, ctrl0);
  157. }
  158. static void armada_ap806_thermal_init(void)
  159. {
  160. uint32_t reg;
  161. reg = mmio_read_32(TSEN_CTRL0);
  162. reg &= ~TSEN_CTRL0_RESET;
  163. reg |= TSEN_CTRL0_START | TSEN_CTRL0_ENABLE;
  164. /* Sample every ~2ms */
  165. reg |= TSEN_CTRL0_OSR_MAX << TSEN_CTRL0_OSR_SHIFT;
  166. /* Enable average (2 samples by default) */
  167. reg &= ~TSEN_CTRL0_AVG_BYPASS;
  168. mmio_write_32(TSEN_CTRL0, reg);
  169. debug("thermal: Initialization done\n");
  170. }
  171. static void armada_is_valid(u_register_t *read)
  172. {
  173. *read = (mmio_read_32(TSEN_STATUS) & TSEN_STATUS_VALID_MASK);
  174. }
  175. int mvebu_dfx_thermal_handle(u_register_t func, u_register_t *read,
  176. u_register_t x2, u_register_t x3)
  177. {
  178. debug_enter();
  179. switch (func) {
  180. case MV_SIP_DFX_THERMAL_INIT:
  181. armada_ap806_thermal_init();
  182. break;
  183. case MV_SIP_DFX_THERMAL_READ:
  184. armada_ap806_thermal_read(read);
  185. break;
  186. case MV_SIP_DFX_THERMAL_IRQ:
  187. armada_ap806_thermal_irq();
  188. break;
  189. case MV_SIP_DFX_THERMAL_THRESH:
  190. armada_ap806_thermal_threshold(x2, x3);
  191. armada_ap806_thermal_overheat_irq_init();
  192. break;
  193. case MV_SIP_DFX_THERMAL_IS_VALID:
  194. armada_is_valid(read);
  195. break;
  196. case MV_SIP_DFX_THERMAL_SEL_CHANNEL:
  197. armada_select_channel(x2);
  198. break;
  199. default:
  200. ERROR("unsupported dfx func\n");
  201. return -EINVAL;
  202. }
  203. debug_exit();
  204. return 0;
  205. }