1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2022, Linaro Limited
4 */
5
6 #include <assert.h>
7 #include <compiler.h>
8 #include <config.h>
9 #include <drivers/hfic.h>
10 #include <kernel/interrupt.h>
11 #include <kernel/panic.h>
12 #include <kernel/thread.h>
13
14 /*
15 * For documentation of the paravirtualized interface see:
16 * https://hafnium.readthedocs.io/en/latest/design/secure-partition-manager.html#paravirtualized-interfaces
17 */
18
19 #define HF_INTERRUPT_ENABLE 0xff03
20 #define HF_INTERRUPT_GET 0xff04
21 #define HF_INTERRUPT_DEACTIVATE 0xff08
22 #define HF_INTERRUPT_RECONFIGURE 0xff09
23
24 #define HF_INVALID_INTID 0xffffffff
25 #define HF_MAILBOX_READABLE_INTID 1
26 #define HF_MAILBOX_WRITABLE_INTID 2
27 #define HF_VIRTUAL_TIMER_INTID 3
28 #define HF_MANAGED_EXIT_INTID 4
29 #define HF_NOTIFICATION_PENDING_INTID 5
30 #define HF_IPI_INTID 9
31
32 #define HF_INTERRUPT_TYPE_IRQ 0
33 #define HF_INTERRUPT_TYPE_FIQ 1
34 #define HF_ENABLE 1
35 #define HF_DISABLE 0
36
37 #define HF_INT_RECONFIGURE_STATUS 2
38
39 struct hfic_data {
40 struct itr_chip chip;
41 };
42
43 static struct hfic_data hfic_data __nex_bss;
44
hfic_static_it(size_t it)45 static bool __maybe_unused hfic_static_it(size_t it)
46 {
47 switch (it) {
48 case HF_MAILBOX_READABLE_INTID:
49 case HF_MAILBOX_WRITABLE_INTID:
50 case HF_VIRTUAL_TIMER_INTID:
51 case HF_MANAGED_EXIT_INTID:
52 case HF_NOTIFICATION_PENDING_INTID:
53 case HF_IPI_INTID:
54 return true;
55 default:
56 return false;
57 }
58 }
59
hfic_op_configure(struct itr_chip * chip __unused,size_t it,uint32_t type __unused,uint32_t prio __unused)60 static void hfic_op_configure(struct itr_chip *chip __unused, size_t it,
61 uint32_t type __unused, uint32_t prio __unused)
62 {
63 uint32_t res __maybe_unused = 0;
64
65 res = thread_hvc(HF_INTERRUPT_ENABLE, it, HF_ENABLE,
66 HF_INTERRUPT_TYPE_IRQ);
67 assert(!res || hfic_static_it(it));
68 }
69
hfic_op_enable(struct itr_chip * chip __unused,size_t it)70 static void hfic_op_enable(struct itr_chip *chip __unused, size_t it)
71 {
72 uint32_t res __maybe_unused = 0;
73
74 res = thread_hvc(HF_INTERRUPT_RECONFIGURE, it,
75 HF_INT_RECONFIGURE_STATUS, HF_ENABLE);
76 assert(!res || hfic_static_it(it));
77 }
78
hfic_op_disable(struct itr_chip * chip __unused,size_t it)79 static void hfic_op_disable(struct itr_chip *chip __unused, size_t it)
80 {
81 uint32_t res __maybe_unused = 0;
82
83 res = thread_hvc(HF_INTERRUPT_RECONFIGURE, it,
84 HF_INT_RECONFIGURE_STATUS, HF_DISABLE);
85 assert(!res || hfic_static_it(it));
86 }
87
88 static const struct itr_ops hfic_ops = {
89 .configure = hfic_op_configure,
90 .mask = hfic_op_disable,
91 .unmask = hfic_op_enable,
92 .enable = hfic_op_enable,
93 .disable = hfic_op_disable,
94 };
95
hfic_init(void)96 void hfic_init(void)
97 {
98 hfic_data.chip.ops = &hfic_ops;
99 interrupt_main_init(&hfic_data.chip);
100 }
101
102 /* Override interrupt_main_handler() with driver implementation */
interrupt_main_handler(void)103 void interrupt_main_handler(void)
104 {
105 uint32_t id = 0;
106 uint32_t res __maybe_unused = 0;
107
108 id = thread_hvc(HF_INTERRUPT_GET, 0, 0, 0);
109 if (id == HF_INVALID_INTID) {
110 DMSG("ignoring invalid interrupt %#"PRIx32, id);
111 return;
112 }
113
114 interrupt_call_handlers(&hfic_data.chip, id);
115
116 res = thread_hvc(HF_INTERRUPT_DEACTIVATE, id, id, 0);
117 assert(!res);
118 }
119