1 /*
2  * Copyright (c) 2017 The Linux Foundation. All rights reserved.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for
5  * any purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all
7  * copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /**
20  * DOC: qdf_cpuhp (CPU hotplug)
21  * QCA driver framework (QDF) CPU hotplug APIs
22  */
23 
24 #include "qdf_cpuhp.h"
25 #include "i_qdf_cpuhp.h"
26 #include "qdf_list.h"
27 #include "qdf_lock.h"
28 
29 static qdf_mutex_t qdf_cpuhp_lock;
30 static qdf_list_t qdf_cpuhp_handlers;
31 
32 struct qdf_cpuhp_handler {
33 	qdf_list_node_t node;
34 	void *context;
35 	qdf_cpuhp_callback up_callback;
36 	qdf_cpuhp_callback down_callback;
37 };
38 
qdf_cpuhp_on_up(uint32_t cpu)39 static void qdf_cpuhp_on_up(uint32_t cpu)
40 {
41 	QDF_STATUS status;
42 	qdf_list_node_t *node;
43 
44 	qdf_mutex_acquire(&qdf_cpuhp_lock);
45 
46 	status = qdf_list_peek_front(&qdf_cpuhp_handlers, &node);
47 	while (QDF_IS_STATUS_SUCCESS(status)) {
48 		struct qdf_cpuhp_handler *handler =
49 			qdf_container_of(node, struct qdf_cpuhp_handler, node);
50 		if (handler->up_callback)
51 			handler->up_callback(handler->context, cpu);
52 
53 		status = qdf_list_peek_next(&qdf_cpuhp_handlers, node, &node);
54 	}
55 
56 	qdf_mutex_release(&qdf_cpuhp_lock);
57 }
58 
qdf_cpuhp_on_down(uint32_t cpu)59 static void qdf_cpuhp_on_down(uint32_t cpu)
60 {
61 	QDF_STATUS status;
62 	qdf_list_node_t *node;
63 
64 	qdf_mutex_acquire(&qdf_cpuhp_lock);
65 
66 	status = qdf_list_peek_front(&qdf_cpuhp_handlers, &node);
67 	while (QDF_IS_STATUS_SUCCESS(status)) {
68 		struct qdf_cpuhp_handler *handler =
69 			qdf_container_of(node, struct qdf_cpuhp_handler, node);
70 		if (handler->down_callback)
71 			handler->down_callback(handler->context, cpu);
72 
73 		status = qdf_list_peek_next(&qdf_cpuhp_handlers, node, &node);
74 	}
75 
76 	qdf_mutex_release(&qdf_cpuhp_lock);
77 }
78 
qdf_cpuhp_init(void)79 QDF_STATUS qdf_cpuhp_init(void)
80 {
81 	QDF_STATUS status;
82 
83 	status = qdf_mutex_create(&qdf_cpuhp_lock);
84 	if (QDF_IS_STATUS_ERROR(status))
85 		return status;
86 
87 	qdf_list_create(&qdf_cpuhp_handlers, 0);
88 
89 	__qdf_cpuhp_os_init(qdf_cpuhp_on_up, qdf_cpuhp_on_down);
90 
91 	return QDF_STATUS_SUCCESS;
92 }
93 
qdf_cpuhp_deinit(void)94 QDF_STATUS qdf_cpuhp_deinit(void)
95 {
96 	__qdf_cpuhp_os_deinit();
97 	qdf_list_destroy(&qdf_cpuhp_handlers);
98 	return qdf_mutex_destroy(&qdf_cpuhp_lock);
99 }
100 
qdf_cpuhp_register(struct qdf_cpuhp_handler ** out_handler,void * context,qdf_cpuhp_callback up_callback,qdf_cpuhp_callback down_callback)101 QDF_STATUS qdf_cpuhp_register(struct qdf_cpuhp_handler **out_handler,
102 			      void *context,
103 			      qdf_cpuhp_callback up_callback,
104 			      qdf_cpuhp_callback down_callback)
105 {
106 	QDF_STATUS status;
107 	struct qdf_cpuhp_handler *handler;
108 
109 	*out_handler = NULL;
110 
111 	handler = qdf_mem_malloc(sizeof(*handler));
112 	if (!handler)
113 		return QDF_STATUS_E_NOMEM;
114 
115 	handler->context = context;
116 	handler->up_callback = up_callback;
117 	handler->down_callback = down_callback;
118 
119 	status = qdf_mutex_acquire(&qdf_cpuhp_lock);
120 	if (QDF_IS_STATUS_ERROR(status))
121 		goto free_handler;
122 
123 	status = qdf_list_insert_back(&qdf_cpuhp_handlers, &handler->node);
124 	if (QDF_IS_STATUS_ERROR(status))
125 		goto release_lock;
126 
127 	/* this can fail, but there isn't a good way to recover... */
128 	qdf_mutex_release(&qdf_cpuhp_lock);
129 
130 	*out_handler = handler;
131 
132 	return QDF_STATUS_SUCCESS;
133 
134 release_lock:
135 	qdf_mutex_release(&qdf_cpuhp_lock);
136 
137 free_handler:
138 	qdf_mem_free(handler);
139 
140 	return status;
141 }
142 
qdf_cpuhp_unregister(struct qdf_cpuhp_handler ** out_handler)143 void qdf_cpuhp_unregister(struct qdf_cpuhp_handler **out_handler)
144 {
145 	struct qdf_cpuhp_handler *handler = *out_handler;
146 
147 	QDF_BUG(handler);
148 	if (!handler)
149 		return;
150 
151 	qdf_mutex_acquire(&qdf_cpuhp_lock);
152 	qdf_list_remove_node(&qdf_cpuhp_handlers, &handler->node);
153 	qdf_mutex_release(&qdf_cpuhp_lock);
154 
155 	qdf_mem_free(handler);
156 	*out_handler = NULL;
157 }
158 
159