1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2016 Intel Corporation. All rights reserved.
4 //
5 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6
7 #include <sof/common.h>
8 #include <sof/debug/panic.h>
9 #include <sof/drivers/idc.h>
10 #include <sof/drivers/interrupt.h>
11 #include <sof/lib/alloc.h>
12 #include <sof/lib/cache.h>
13 #include <sof/lib/cpu.h>
14 #include <sof/lib/memory.h>
15 #include <sof/lib/notifier.h>
16 #include <sof/lib/uuid.h>
17 #include <sof/list.h>
18 #include <sof/sof.h>
19 #include <ipc/topology.h>
20 #include <stdint.h>
21
22 /* 1fb15a7a-83cd-4c2e-8b32-4da1b2adeeaf */
23 DECLARE_SOF_UUID("notifier", notifier_uuid, 0x1fb15a7a, 0x83cd, 0x4c2e,
24 0x8b, 0x32, 0x4d, 0xa1, 0xb2, 0xad, 0xee, 0xaf);
25
26 DECLARE_TR_CTX(nt_tr, SOF_UUID(notifier_uuid), LOG_LEVEL_INFO);
27
28 static SHARED_DATA struct notify_data notify_data[CONFIG_CORE_COUNT];
29
30 struct callback_handle {
31 void *receiver;
32 void *caller;
33 void (*cb)(void *arg, enum notify_id, void *data);
34 struct list_item list;
35 uint32_t num_registrations;
36 };
37
notifier_register(void * receiver,void * caller,enum notify_id type,void (* cb)(void * arg,enum notify_id type,void * data),uint32_t flags)38 int notifier_register(void *receiver, void *caller, enum notify_id type,
39 void (*cb)(void *arg, enum notify_id type, void *data),
40 uint32_t flags)
41 {
42 struct notify *notify = *arch_notify_get();
43 struct callback_handle *handle;
44 int ret = 0;
45
46 assert(type >= NOTIFIER_ID_CPU_FREQ && type < NOTIFIER_ID_COUNT);
47
48 spin_lock(¬ify->lock);
49
50 /* Find already registered event of this type */
51 if (flags & NOTIFIER_FLAG_AGGREGATE &&
52 !list_is_empty(¬ify->list[type])) {
53 handle = container_of((¬ify->list[type])->next,
54 struct callback_handle, list);
55 handle->num_registrations++;
56
57 goto out;
58 }
59
60 handle = rzalloc(SOF_MEM_ZONE_SYS_RUNTIME, 0, SOF_MEM_CAPS_RAM,
61 sizeof(*handle));
62
63 if (!handle) {
64 tr_err(&nt_tr, "notifier_register(): callback handle allocation failed.");
65 ret = -ENOMEM;
66 goto out;
67 }
68
69 handle->receiver = receiver;
70 handle->caller = caller;
71 handle->cb = cb;
72 handle->num_registrations = 1;
73
74 list_item_prepend(&handle->list, ¬ify->list[type]);
75
76 out:
77 spin_unlock(¬ify->lock);
78 return ret;
79 }
80
notifier_unregister(void * receiver,void * caller,enum notify_id type)81 void notifier_unregister(void *receiver, void *caller, enum notify_id type)
82 {
83 struct notify *notify = *arch_notify_get();
84 struct list_item *wlist;
85 struct list_item *tlist;
86 struct callback_handle *handle;
87
88 assert(type >= NOTIFIER_ID_CPU_FREQ && type < NOTIFIER_ID_COUNT);
89
90 spin_lock(¬ify->lock);
91
92 /*
93 * Unregister all matching callbacks
94 * If receiver is NULL, unregister all callbacks with matching callers
95 * If caller is NULL, unregister all callbacks with matching receivers
96 *
97 * Event producer might force unregister all receivers by passing
98 * receiver NULL
99 * Event consumer might unregister from all callers by passing caller
100 * NULL
101 */
102 list_for_item_safe(wlist, tlist, ¬ify->list[type]) {
103 handle = container_of(wlist, struct callback_handle, list);
104 if ((!receiver || handle->receiver == receiver) &&
105 (!caller || handle->caller == caller)) {
106 if (!--handle->num_registrations) {
107 list_item_del(&handle->list);
108 rfree(handle);
109 }
110 }
111 }
112
113 spin_unlock(¬ify->lock);
114 }
115
notifier_unregister_all(void * receiver,void * caller)116 void notifier_unregister_all(void *receiver, void *caller)
117 {
118 int i;
119
120 for (i = NOTIFIER_ID_CPU_FREQ; i < NOTIFIER_ID_COUNT; i++)
121 notifier_unregister(receiver, caller, i);
122 }
123
notifier_notify(const void * caller,enum notify_id type,void * data)124 static void notifier_notify(const void *caller, enum notify_id type, void *data)
125 {
126 struct notify *notify = *arch_notify_get();
127 struct list_item *wlist;
128 struct list_item *tlist;
129 struct callback_handle *handle;
130
131 /* iterate through notifiers and send event to
132 * interested clients
133 */
134 list_for_item_safe(wlist, tlist, ¬ify->list[type]) {
135 handle = container_of(wlist, struct callback_handle, list);
136 if (!caller || !handle->caller || handle->caller == caller)
137 handle->cb(handle->receiver, type, data);
138 }
139 }
140
notifier_notify_remote(void)141 void notifier_notify_remote(void)
142 {
143 struct notify *notify = *arch_notify_get();
144 struct notify_data *notify_data = notify_data_get() + cpu_get_id();
145
146 if (!list_is_empty(¬ify->list[notify_data->type])) {
147 dcache_invalidate_region(notify_data->data,
148 notify_data->data_size);
149 notifier_notify(notify_data->caller, notify_data->type,
150 notify_data->data);
151 }
152
153 }
154
notifier_event(const void * caller,enum notify_id type,uint32_t core_mask,void * data,uint32_t data_size)155 void notifier_event(const void *caller, enum notify_id type, uint32_t core_mask,
156 void *data, uint32_t data_size)
157 {
158 struct notify_data *notify_data;
159 struct idc_msg notify_msg = { IDC_MSG_NOTIFY, IDC_MSG_NOTIFY_EXT };
160 int i;
161
162 /* notify selected targets */
163 for (i = 0; i < CONFIG_CORE_COUNT; i++) {
164 if (core_mask & NOTIFIER_TARGET_CORE_MASK(i)) {
165 if (i == cpu_get_id()) {
166 notifier_notify(caller, type, data);
167 } else if (cpu_is_core_enabled(i)) {
168 notify_msg.core = i;
169 notify_data = notify_data_get() + i;
170 notify_data->caller = caller;
171 notify_data->type = type;
172
173 /* NOTE: for transcore events, payload has to
174 * be allocated on heap, not on stack
175 */
176 notify_data->data = data;
177 notify_data->data_size = data_size;
178
179 dcache_writeback_region(notify_data->data,
180 data_size);
181
182 idc_send_msg(¬ify_msg, IDC_NON_BLOCKING);
183 }
184 }
185 }
186 }
187
init_system_notify(struct sof * sof)188 void init_system_notify(struct sof *sof)
189 {
190 struct notify **notify = arch_notify_get();
191 int i;
192 *notify = rzalloc(SOF_MEM_ZONE_SYS, 0, SOF_MEM_CAPS_RAM,
193 sizeof(**notify));
194
195 spinlock_init(&(*notify)->lock);
196 for (i = NOTIFIER_ID_CPU_FREQ; i < NOTIFIER_ID_COUNT; i++)
197 list_init(&(*notify)->list[i]);
198
199 if (cpu_get_id() == PLATFORM_PRIMARY_CORE_ID)
200 sof->notify_data = platform_shared_get(notify_data,
201 sizeof(notify_data));
202 }
203
free_system_notify(void)204 void free_system_notify(void)
205 {
206 }
207