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(&notify->lock);
49 
50 	/* Find already registered event of this type */
51 	if (flags & NOTIFIER_FLAG_AGGREGATE &&
52 	    !list_is_empty(&notify->list[type])) {
53 		handle = container_of((&notify->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, &notify->list[type]);
75 
76 out:
77 	spin_unlock(&notify->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(&notify->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, &notify->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(&notify->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, &notify->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(&notify->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(&notify_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