sbi_timer.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * SPDX-License-Identifier: BSD-2-Clause
  3. *
  4. * Copyright (c) 2019 Western Digital Corporation or its affiliates.
  5. *
  6. * Authors:
  7. * Anup Patel <anup.patel@wdc.com>
  8. */
  9. #include <sbi/riscv_asm.h>
  10. #include <sbi/riscv_barrier.h>
  11. #include <sbi/riscv_encoding.h>
  12. #include <sbi/sbi_console.h>
  13. #include <sbi/sbi_error.h>
  14. #include <sbi/sbi_hart.h>
  15. #include <sbi/sbi_platform.h>
  16. #include <sbi/sbi_pmu.h>
  17. #include <sbi/sbi_scratch.h>
  18. #include <sbi/sbi_timer.h>
  19. static unsigned long time_delta_off;
  20. static u64 (*get_time_val)(void);
  21. static const struct sbi_timer_device *timer_dev = NULL;
  22. #if __riscv_xlen == 32
  23. static u64 get_ticks(void)
  24. {
  25. u32 lo, hi, tmp;
  26. __asm__ __volatile__("1:\n"
  27. "rdtimeh %0\n"
  28. "rdtime %1\n"
  29. "rdtimeh %2\n"
  30. "bne %0, %2, 1b"
  31. : "=&r"(hi), "=&r"(lo), "=&r"(tmp));
  32. return ((u64)hi << 32) | lo;
  33. }
  34. #else
  35. static u64 get_ticks(void)
  36. {
  37. unsigned long n;
  38. __asm__ __volatile__("rdtime %0" : "=r"(n));
  39. return n;
  40. }
  41. #endif
  42. static void nop_delay_fn(void *opaque)
  43. {
  44. cpu_relax();
  45. }
  46. void sbi_timer_delay_loop(ulong units, u64 unit_freq,
  47. void (*delay_fn)(void *), void *opaque)
  48. {
  49. u64 start_val, delta;
  50. /* Do nothing if we don't have timer device */
  51. if (!timer_dev || !get_time_val) {
  52. sbi_printf("%s: called without timer device\n", __func__);
  53. return;
  54. }
  55. /* Save starting timer value */
  56. start_val = get_time_val();
  57. /* Compute desired timer value delta */
  58. delta = ((u64)timer_dev->timer_freq * (u64)units);
  59. delta = delta / unit_freq;
  60. /* Use NOP delay function if delay function not available */
  61. if (!delay_fn)
  62. delay_fn = nop_delay_fn;
  63. /* Busy loop until desired timer value delta reached */
  64. while ((get_time_val() - start_val) < delta)
  65. delay_fn(opaque);
  66. }
  67. bool sbi_timer_waitms_until(bool (*predicate)(void *), void *arg,
  68. uint64_t timeout_ms)
  69. {
  70. uint64_t start_time = sbi_timer_value();
  71. uint64_t ticks =
  72. (sbi_timer_get_device()->timer_freq / 1000) *
  73. timeout_ms;
  74. while(!predicate(arg))
  75. if (sbi_timer_value() - start_time >= ticks)
  76. return false;
  77. return true;
  78. }
  79. u64 sbi_timer_value(void)
  80. {
  81. if (get_time_val)
  82. return get_time_val();
  83. return 0;
  84. }
  85. u64 sbi_timer_virt_value(void)
  86. {
  87. u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(),
  88. time_delta_off);
  89. return sbi_timer_value() + *time_delta;
  90. }
  91. u64 sbi_timer_get_delta(void)
  92. {
  93. u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(),
  94. time_delta_off);
  95. return *time_delta;
  96. }
  97. void sbi_timer_set_delta(ulong delta)
  98. {
  99. u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(),
  100. time_delta_off);
  101. *time_delta = (u64)delta;
  102. }
  103. void sbi_timer_set_delta_upper(ulong delta_upper)
  104. {
  105. u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(),
  106. time_delta_off);
  107. *time_delta &= 0xffffffffULL;
  108. *time_delta |= ((u64)delta_upper << 32);
  109. }
  110. void sbi_timer_event_start(u64 next_event)
  111. {
  112. sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SET_TIMER);
  113. /**
  114. * Update the stimecmp directly if available. This allows
  115. * the older software to leverage sstc extension on newer hardware.
  116. */
  117. if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SSTC)) {
  118. #if __riscv_xlen == 32
  119. csr_write(CSR_STIMECMP, next_event & 0xFFFFFFFF);
  120. csr_write(CSR_STIMECMPH, next_event >> 32);
  121. #else
  122. csr_write(CSR_STIMECMP, next_event);
  123. #endif
  124. } else if (timer_dev && timer_dev->timer_event_start) {
  125. timer_dev->timer_event_start(next_event);
  126. csr_clear(CSR_MIP, MIP_STIP);
  127. }
  128. csr_set(CSR_MIE, MIP_MTIP);
  129. }
  130. void sbi_timer_process(void)
  131. {
  132. csr_clear(CSR_MIE, MIP_MTIP);
  133. /*
  134. * If sstc extension is available, supervisor can receive the timer
  135. * directly without M-mode come in between. This function should
  136. * only invoked if M-mode programs the timer for its own purpose.
  137. */
  138. if (!sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SSTC))
  139. csr_set(CSR_MIP, MIP_STIP);
  140. }
  141. const struct sbi_timer_device *sbi_timer_get_device(void)
  142. {
  143. return timer_dev;
  144. }
  145. void sbi_timer_set_device(const struct sbi_timer_device *dev)
  146. {
  147. if (!dev || timer_dev)
  148. return;
  149. timer_dev = dev;
  150. if (!get_time_val && timer_dev->timer_value)
  151. get_time_val = timer_dev->timer_value;
  152. }
  153. int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot)
  154. {
  155. u64 *time_delta;
  156. const struct sbi_platform *plat = sbi_platform_ptr(scratch);
  157. if (cold_boot) {
  158. time_delta_off = sbi_scratch_alloc_offset(sizeof(*time_delta));
  159. if (!time_delta_off)
  160. return SBI_ENOMEM;
  161. if (sbi_hart_has_extension(scratch, SBI_HART_EXT_TIME))
  162. get_time_val = get_ticks;
  163. } else {
  164. if (!time_delta_off)
  165. return SBI_ENOMEM;
  166. }
  167. time_delta = sbi_scratch_offset_ptr(scratch, time_delta_off);
  168. *time_delta = 0;
  169. return sbi_platform_timer_init(plat, cold_boot);
  170. }
  171. void sbi_timer_exit(struct sbi_scratch *scratch)
  172. {
  173. if (timer_dev && timer_dev->timer_event_stop)
  174. timer_dev->timer_event_stop();
  175. csr_clear(CSR_MIP, MIP_STIP);
  176. csr_clear(CSR_MIE, MIP_MTIP);
  177. sbi_platform_timer_exit(sbi_platform_ptr(scratch));
  178. }