sun20i-d1.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. * SPDX-License-Identifier: BSD-2-Clause
  3. *
  4. * Copyright (c) 2022 Samuel Holland <samuel@sholland.org>
  5. */
  6. #include <platform_override.h>
  7. #include <thead_c9xx.h>
  8. #include <sbi/riscv_asm.h>
  9. #include <sbi/riscv_io.h>
  10. #include <sbi/sbi_bitops.h>
  11. #include <sbi/sbi_ecall_interface.h>
  12. #include <sbi/sbi_error.h>
  13. #include <sbi/sbi_hsm.h>
  14. #include <sbi/sbi_pmu.h>
  15. #include <sbi/sbi_scratch.h>
  16. #include <sbi_utils/fdt/fdt_fixup.h>
  17. #include <sbi_utils/fdt/fdt_helper.h>
  18. #include <sbi_utils/irqchip/fdt_irqchip_plic.h>
  19. #define SUN20I_D1_CCU_BASE ((void *)0x02001000)
  20. #define SUN20I_D1_RISCV_CFG_BASE ((void *)0x06010000)
  21. #define SUN20I_D1_PPU_BASE ((void *)0x07001000)
  22. #define SUN20I_D1_PRCM_BASE ((void *)0x07010000)
  23. /*
  24. * CCU
  25. */
  26. #define CCU_BGR_ENABLE (BIT(16) | BIT(0))
  27. #define RISCV_CFG_BGR_REG 0xd0c
  28. #define PPU_BGR_REG 0x1ac
  29. /*
  30. * CSRs
  31. */
  32. #define CSR_MXSTATUS 0x7c0
  33. #define CSR_MHCR 0x7c1
  34. #define CSR_MCOR 0x7c2
  35. #define CSR_MHINT 0x7c5
  36. static unsigned long csr_mxstatus;
  37. static unsigned long csr_mhcr;
  38. static unsigned long csr_mhint;
  39. static void sun20i_d1_csr_save(void)
  40. {
  41. /* Save custom CSRs. */
  42. csr_mxstatus = csr_read(CSR_MXSTATUS);
  43. csr_mhcr = csr_read(CSR_MHCR);
  44. csr_mhint = csr_read(CSR_MHINT);
  45. /* Flush and disable caches. */
  46. csr_write(CSR_MCOR, 0x22);
  47. csr_write(CSR_MHCR, 0x0);
  48. }
  49. static void sun20i_d1_csr_restore(void)
  50. {
  51. /* Invalidate caches and the branch predictor. */
  52. csr_write(CSR_MCOR, 0x70013);
  53. /* Restore custom CSRs, including the cache state. */
  54. csr_write(CSR_MXSTATUS, csr_mxstatus);
  55. csr_write(CSR_MHCR, csr_mhcr);
  56. csr_write(CSR_MHINT, csr_mhint);
  57. }
  58. /*
  59. * PLIC
  60. */
  61. #define PLIC_SOURCES 175
  62. #define PLIC_IE_WORDS (PLIC_SOURCES / 32 + 1)
  63. static u8 plic_priority[1 + PLIC_SOURCES];
  64. static u32 plic_sie[PLIC_IE_WORDS];
  65. static u32 plic_threshold;
  66. static void sun20i_d1_plic_save(void)
  67. {
  68. fdt_plic_context_save(true, plic_sie, &plic_threshold, PLIC_IE_WORDS);
  69. fdt_plic_priority_save(plic_priority, PLIC_SOURCES);
  70. }
  71. static void sun20i_d1_plic_restore(void)
  72. {
  73. thead_plic_restore();
  74. fdt_plic_priority_restore(plic_priority, PLIC_SOURCES);
  75. fdt_plic_context_restore(true, plic_sie, plic_threshold,
  76. PLIC_IE_WORDS);
  77. }
  78. /*
  79. * PPU
  80. */
  81. #define PPU_PD_ACTIVE_CTRL 0x2c
  82. static void sun20i_d1_ppu_save(void)
  83. {
  84. /* Enable MMIO access. Do not assume S-mode leaves the clock enabled. */
  85. writel_relaxed(CCU_BGR_ENABLE, SUN20I_D1_PRCM_BASE + PPU_BGR_REG);
  86. /* Activate automatic power-down during the next WFI. */
  87. writel_relaxed(1, SUN20I_D1_PPU_BASE + PPU_PD_ACTIVE_CTRL);
  88. }
  89. static void sun20i_d1_ppu_restore(void)
  90. {
  91. /* Disable automatic power-down. */
  92. writel_relaxed(0, SUN20I_D1_PPU_BASE + PPU_PD_ACTIVE_CTRL);
  93. }
  94. /*
  95. * RISCV_CFG
  96. */
  97. #define RESET_ENTRY_LO_REG 0x0004
  98. #define RESET_ENTRY_HI_REG 0x0008
  99. #define WAKEUP_EN_REG 0x0020
  100. #define WAKEUP_MASK_REG(i) (0x0024 + 4 * (i))
  101. static void sun20i_d1_riscv_cfg_save(void)
  102. {
  103. /* Enable MMIO access. Do not assume S-mode leaves the clock enabled. */
  104. writel_relaxed(CCU_BGR_ENABLE, SUN20I_D1_CCU_BASE + RISCV_CFG_BGR_REG);
  105. /*
  106. * Copy the SIE bits to the wakeup registers. D1 has 160 "real"
  107. * interrupt sources, numbered 16-175. These are the ones that map to
  108. * the wakeup mask registers (the offset is for GIC compatibility). So
  109. * copying SIE to the wakeup mask needs some bit manipulation.
  110. */
  111. for (int i = 0; i < PLIC_IE_WORDS - 1; i++)
  112. writel_relaxed(plic_sie[i] >> 16 | plic_sie[i + 1] << 16,
  113. SUN20I_D1_RISCV_CFG_BASE + WAKEUP_MASK_REG(i));
  114. /* Enable PPU wakeup for interrupts. */
  115. writel_relaxed(1, SUN20I_D1_RISCV_CFG_BASE + WAKEUP_EN_REG);
  116. }
  117. static void sun20i_d1_riscv_cfg_restore(void)
  118. {
  119. /* Disable PPU wakeup for interrupts. */
  120. writel_relaxed(0, SUN20I_D1_RISCV_CFG_BASE + WAKEUP_EN_REG);
  121. }
  122. static void sun20i_d1_riscv_cfg_init(void)
  123. {
  124. u64 entry = sbi_hartid_to_scratch(0)->warmboot_addr;
  125. /* Enable MMIO access. */
  126. writel_relaxed(CCU_BGR_ENABLE, SUN20I_D1_CCU_BASE + RISCV_CFG_BGR_REG);
  127. /* Program the reset entry address. */
  128. writel_relaxed(entry, SUN20I_D1_RISCV_CFG_BASE + RESET_ENTRY_LO_REG);
  129. writel_relaxed(entry >> 32, SUN20I_D1_RISCV_CFG_BASE + RESET_ENTRY_HI_REG);
  130. }
  131. static int sun20i_d1_hart_suspend(u32 suspend_type)
  132. {
  133. /* Use the generic code for retentive suspend. */
  134. if (!(suspend_type & SBI_HSM_SUSP_NON_RET_BIT))
  135. return SBI_ENOTSUPP;
  136. sun20i_d1_plic_save();
  137. sun20i_d1_ppu_save();
  138. sun20i_d1_riscv_cfg_save();
  139. sun20i_d1_csr_save();
  140. /*
  141. * If no interrupt is pending, this will power down the CPU power
  142. * domain. Otherwise, this will fall through, and the generic HSM
  143. * code will jump to the resume address.
  144. */
  145. wfi();
  146. return 0;
  147. }
  148. static void sun20i_d1_hart_resume(void)
  149. {
  150. sun20i_d1_csr_restore();
  151. sun20i_d1_riscv_cfg_restore();
  152. sun20i_d1_ppu_restore();
  153. sun20i_d1_plic_restore();
  154. }
  155. static const struct sbi_hsm_device sun20i_d1_ppu = {
  156. .name = "sun20i-d1-ppu",
  157. .hart_suspend = sun20i_d1_hart_suspend,
  158. .hart_resume = sun20i_d1_hart_resume,
  159. };
  160. static int sun20i_d1_final_init(bool cold_boot, const struct fdt_match *match)
  161. {
  162. if (cold_boot) {
  163. sun20i_d1_riscv_cfg_init();
  164. sbi_hsm_set_device(&sun20i_d1_ppu);
  165. }
  166. return 0;
  167. }
  168. static const struct sbi_cpu_idle_state sun20i_d1_cpu_idle_states[] = {
  169. {
  170. .name = "cpu-nonretentive",
  171. .suspend_param = SBI_HSM_SUSPEND_NON_RET_DEFAULT,
  172. .local_timer_stop = true,
  173. .entry_latency_us = 40,
  174. .exit_latency_us = 67,
  175. .min_residency_us = 1100,
  176. .wakeup_latency_us = 67,
  177. },
  178. { }
  179. };
  180. static int sun20i_d1_fdt_fixup(void *fdt, const struct fdt_match *match)
  181. {
  182. return fdt_add_cpu_idle_states(fdt, sun20i_d1_cpu_idle_states);
  183. }
  184. static void thead_c9xx_pmu_ctr_enable_irq(uint32_t ctr_idx)
  185. {
  186. unsigned long mip_val;
  187. if (ctr_idx >= SBI_PMU_HW_CTR_MAX)
  188. return;
  189. mip_val = csr_read(CSR_MIP);
  190. /**
  191. * Clear out the OF bit so that next interrupt can be enabled.
  192. * This should be done only when the corresponding overflow interrupt
  193. * bit is cleared. That indicates that software has already handled the
  194. * previous interrupts or the hardware yet to set an overflow interrupt.
  195. * Otherwise, there will be race conditions where we may clear the bit
  196. * the software is yet to handle the interrupt.
  197. */
  198. if (!(mip_val & THEAD_C9XX_MIP_MOIP))
  199. csr_clear(THEAD_C9XX_CSR_MCOUNTEROF, BIT(ctr_idx));
  200. /**
  201. * SSCOFPMF uses the OF bit for enabling/disabling the interrupt,
  202. * while the C9XX has designated enable bits.
  203. * So enable per-counter interrupt on C9xx here.
  204. */
  205. csr_set(THEAD_C9XX_CSR_MCOUNTERINTEN, BIT(ctr_idx));
  206. }
  207. static void thead_c9xx_pmu_ctr_disable_irq(uint32_t ctr_idx)
  208. {
  209. csr_clear(THEAD_C9XX_CSR_MCOUNTERINTEN, BIT(ctr_idx));
  210. }
  211. static int thead_c9xx_pmu_irq_bit(void)
  212. {
  213. return THEAD_C9XX_MIP_MOIP;
  214. }
  215. const struct sbi_pmu_device thead_c9xx_pmu_device = {
  216. .hw_counter_enable_irq = thead_c9xx_pmu_ctr_enable_irq,
  217. .hw_counter_disable_irq = thead_c9xx_pmu_ctr_disable_irq,
  218. .hw_counter_irq_bit = thead_c9xx_pmu_irq_bit,
  219. };
  220. static int sun20i_d1_extensions_init(const struct fdt_match *match,
  221. struct sbi_hart_features *hfeatures)
  222. {
  223. sbi_pmu_set_device(&thead_c9xx_pmu_device);
  224. /* auto-detection doesn't work on t-head c9xx cores */
  225. hfeatures->mhpm_count = 29;
  226. hfeatures->mhpm_bits = 64;
  227. return 0;
  228. }
  229. static const struct fdt_match sun20i_d1_match[] = {
  230. { .compatible = "allwinner,sun20i-d1" },
  231. { },
  232. };
  233. const struct platform_override sun20i_d1 = {
  234. .match_table = sun20i_d1_match,
  235. .final_init = sun20i_d1_final_init,
  236. .fdt_fixup = sun20i_d1_fdt_fixup,
  237. .extensions_init = sun20i_d1_extensions_init,
  238. };