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