pm-light.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. /*
  3. * Copyright (C) 2022 Alibaba Group Holding Limited.
  4. */
  5. #include <asm/cpuidle.h>
  6. #include <linux/delay.h>
  7. #include <linux/kernel.h>
  8. #include <linux/suspend.h>
  9. #include <linux/io.h>
  10. #include <linux/of_address.h>
  11. #include <linux/of.h>
  12. #include <linux/types.h>
  13. #undef pr_fmt
  14. #define pr_fmt(fmt) "light-system-suspend" ": " fmt
  15. #ifdef CONFIG_PLIC_INT_CLEAR
  16. void __iomem *hart0_sbase;
  17. /*
  18. * Each hart context has a set of control registers associated with it. Right
  19. * now there's only two: a source priority threshold over which the hart will
  20. * take an interrupt, and a register to claim interrupts.
  21. */
  22. #define CONTEXT_BASE 0x200000
  23. #define CONTEXT_PER_HART 0x1000
  24. #define CONTEXT_THRESHOLD 0x00
  25. #define CONTEXT_CLAIM 0x04
  26. #endif
  27. static int light_suspend_prepare_late(void)
  28. {
  29. #ifdef CONFIG_PLIC_INT_CLEAR
  30. void __iomem *claim = hart0_sbase + CONTEXT_CLAIM;
  31. irq_hw_number_t hwirq;
  32. pr_debug("clear plic pending interrupt\n");
  33. pr_debug("claim base = 0x%lx\n", (unsigned long)claim);
  34. while ((hwirq = readl(claim))) {
  35. pr_debug("claim hwirq%ld\n", hwirq);
  36. writel(hwirq, claim);
  37. }
  38. #endif
  39. /*
  40. * Two-level switch for interrupt control: it needs to disable interrupts in SIE, not just in SSTATUS,
  41. * otherwise the pending interrupts will wakup the cpu immediately after wfi.
  42. */
  43. csr_write(CSR_IE, 0);
  44. return 0;
  45. }
  46. static int light_suspend_enter(suspend_state_t state)
  47. {
  48. if (!IS_ENABLED(CONFIG_PM))
  49. return 0;
  50. switch (state) {
  51. case PM_SUSPEND_MEM:
  52. pr_debug("enter platform system suspend...\n");
  53. cpu_do_idle();
  54. pr_debug("wakeup from wfi...\n");
  55. break;
  56. default:
  57. return -EINVAL;
  58. }
  59. return 0;
  60. }
  61. static const struct platform_suspend_ops light_suspend_ops = {
  62. .enter = light_suspend_enter,
  63. .valid = suspend_valid_only_mem,
  64. .prepare_late = light_suspend_prepare_late,
  65. };
  66. static int __init pm_light_init(void)
  67. {
  68. #ifdef CONFIG_PLIC_INT_CLEAR
  69. struct device_node *np;
  70. void __iomem *regs;
  71. np = of_find_node_by_path("/soc/interrupt-controller@ffd8000000");
  72. if (!np) {
  73. pr_err("no plic interrupt controller found\n");
  74. return -EINVAL;
  75. }
  76. regs = of_iomap(np, 0);
  77. if (WARN_ON(!regs)) {
  78. pr_err("failed to ioremap interrupt regs\n");
  79. return -EIO;
  80. }
  81. hart0_sbase = regs + CONTEXT_BASE + 1 * CONTEXT_PER_HART; /* 1 means s mode */
  82. pr_debug("hart0_sbase = 0x%lx\n", (unsigned long)hart0_sbase);
  83. #endif
  84. pr_info("set system suspend platform callbacks\n");
  85. suspend_set_ops(&light_suspend_ops);
  86. return 0;
  87. }
  88. late_initcall(pm_light_init);