5c123a6c9d8dded56ad4024f104dad5b08bd61be
[platform/kernel/opensbi.git] / lib / sbi / sbi_system.c
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  *   Nick Kossifidis <mick@ics.forth.gr>
9  */
10
11 #include <sbi/riscv_asm.h>
12 #include <sbi/sbi_bitops.h>
13 #include <sbi/sbi_domain.h>
14 #include <sbi/sbi_hart.h>
15 #include <sbi/sbi_hsm.h>
16 #include <sbi/sbi_platform.h>
17 #include <sbi/sbi_system.h>
18 #include <sbi/sbi_ipi.h>
19 #include <sbi/sbi_init.h>
20
21 static SBI_LIST_HEAD(reset_devices_list);
22
23 const struct sbi_system_reset_device *sbi_system_reset_get_device(
24                                         u32 reset_type, u32 reset_reason)
25 {
26         struct sbi_system_reset_device *reset_dev = NULL;
27         struct sbi_dlist *pos;
28         /** lowest priority - any non zero is our candidate */
29         int priority = 0;
30
31         /* Check each reset device registered for supported reset type */
32         sbi_list_for_each(pos, &(reset_devices_list)) {
33                 struct sbi_system_reset_device *dev =
34                         to_system_reset_device(pos);
35                 if (dev->system_reset_check) {
36                         int status = dev->system_reset_check(reset_type,
37                                                              reset_reason);
38                         /** reset_type not supported */
39                         if (status == 0)
40                                 continue;
41
42                         if (status > priority) {
43                                 reset_dev = dev;
44                                 priority = status;
45                         }
46                 }
47         }
48
49         return reset_dev;
50 }
51
52 void sbi_system_reset_add_device(struct sbi_system_reset_device *dev)
53 {
54         if (!dev || !dev->system_reset_check)
55                 return;
56
57         sbi_list_add(&(dev->node), &(reset_devices_list));
58 }
59
60 bool sbi_system_reset_supported(u32 reset_type, u32 reset_reason)
61 {
62         return !!sbi_system_reset_get_device(reset_type, reset_reason);
63 }
64
65 void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason)
66 {
67         ulong hbase = 0, hmask;
68         u32 cur_hartid = current_hartid();
69         struct sbi_domain *dom = sbi_domain_thishart_ptr();
70         struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
71
72         /* Send HALT IPI to every hart other than the current hart */
73         while (!sbi_hsm_hart_interruptible_mask(dom, hbase, &hmask)) {
74                 if (hbase <= cur_hartid)
75                         hmask &= ~(1UL << (cur_hartid - hbase));
76                 if (hmask)
77                         sbi_ipi_send_halt(hmask, hbase);
78                 hbase += BITS_PER_LONG;
79         }
80
81         /* Stop current HART */
82         sbi_hsm_hart_stop(scratch, false);
83
84         /* Platform specific reset if domain allowed system reset */
85         if (dom->system_reset_allowed) {
86                 const struct sbi_system_reset_device *dev =
87                         sbi_system_reset_get_device(reset_type, reset_reason);
88                 if (dev)
89                         dev->system_reset(reset_type, reset_reason);
90         }
91
92         /* If platform specific reset did not work then do sbi_exit() */
93         sbi_exit(scratch);
94 }
95
96 static const struct sbi_system_suspend_device *suspend_dev = NULL;
97
98 const struct sbi_system_suspend_device *sbi_system_suspend_get_device(void)
99 {
100         return suspend_dev;
101 }
102
103 void sbi_system_suspend_set_device(struct sbi_system_suspend_device *dev)
104 {
105         if (!dev || suspend_dev)
106                 return;
107
108         suspend_dev = dev;
109 }
110
111 bool sbi_system_suspend_supported(u32 sleep_type)
112 {
113         return suspend_dev && suspend_dev->system_suspend_check &&
114                suspend_dev->system_suspend_check(sleep_type);
115 }
116
117 int sbi_system_suspend(u32 sleep_type, ulong resume_addr, ulong opaque)
118 {
119         return 0;
120 }