/* * Copyright (c) 2017 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /** * DOC: qdf_cpuhp (CPU hotplug) * QCA driver framework (QDF) CPU hotplug APIs */ #include "qdf_cpuhp.h" #include "i_qdf_cpuhp.h" #include "qdf_list.h" #include "qdf_lock.h" static qdf_mutex_t qdf_cpuhp_lock; static qdf_list_t qdf_cpuhp_handlers; struct qdf_cpuhp_handler { qdf_list_node_t node; void *context; qdf_cpuhp_callback up_callback; qdf_cpuhp_callback down_callback; }; static void qdf_cpuhp_on_up(uint32_t cpu) { QDF_STATUS status; qdf_list_node_t *node; qdf_mutex_acquire(&qdf_cpuhp_lock); status = qdf_list_peek_front(&qdf_cpuhp_handlers, &node); while (QDF_IS_STATUS_SUCCESS(status)) { struct qdf_cpuhp_handler *handler = qdf_container_of(node, struct qdf_cpuhp_handler, node); if (handler->up_callback) handler->up_callback(handler->context, cpu); status = qdf_list_peek_next(&qdf_cpuhp_handlers, node, &node); } qdf_mutex_release(&qdf_cpuhp_lock); } static void qdf_cpuhp_on_down(uint32_t cpu) { QDF_STATUS status; qdf_list_node_t *node; qdf_mutex_acquire(&qdf_cpuhp_lock); status = qdf_list_peek_front(&qdf_cpuhp_handlers, &node); while (QDF_IS_STATUS_SUCCESS(status)) { struct qdf_cpuhp_handler *handler = qdf_container_of(node, struct qdf_cpuhp_handler, node); if (handler->down_callback) handler->down_callback(handler->context, cpu); status = qdf_list_peek_next(&qdf_cpuhp_handlers, node, &node); } qdf_mutex_release(&qdf_cpuhp_lock); } QDF_STATUS qdf_cpuhp_init(void) { QDF_STATUS status; status = qdf_mutex_create(&qdf_cpuhp_lock); if (QDF_IS_STATUS_ERROR(status)) return status; qdf_list_create(&qdf_cpuhp_handlers, 0); __qdf_cpuhp_os_init(qdf_cpuhp_on_up, qdf_cpuhp_on_down); return QDF_STATUS_SUCCESS; } QDF_STATUS qdf_cpuhp_deinit(void) { __qdf_cpuhp_os_deinit(); qdf_list_destroy(&qdf_cpuhp_handlers); return qdf_mutex_destroy(&qdf_cpuhp_lock); } QDF_STATUS qdf_cpuhp_register(struct qdf_cpuhp_handler **out_handler, void *context, qdf_cpuhp_callback up_callback, qdf_cpuhp_callback down_callback) { QDF_STATUS status; struct qdf_cpuhp_handler *handler; *out_handler = NULL; handler = qdf_mem_malloc(sizeof(*handler)); if (!handler) return QDF_STATUS_E_NOMEM; handler->context = context; handler->up_callback = up_callback; handler->down_callback = down_callback; status = qdf_mutex_acquire(&qdf_cpuhp_lock); if (QDF_IS_STATUS_ERROR(status)) goto free_handler; status = qdf_list_insert_back(&qdf_cpuhp_handlers, &handler->node); if (QDF_IS_STATUS_ERROR(status)) goto release_lock; /* this can fail, but there isn't a good way to recover... */ qdf_mutex_release(&qdf_cpuhp_lock); *out_handler = handler; return QDF_STATUS_SUCCESS; release_lock: qdf_mutex_release(&qdf_cpuhp_lock); free_handler: qdf_mem_free(handler); return status; } void qdf_cpuhp_unregister(struct qdf_cpuhp_handler **out_handler) { struct qdf_cpuhp_handler *handler = *out_handler; QDF_BUG(handler); if (!handler) return; qdf_mutex_acquire(&qdf_cpuhp_lock); qdf_list_remove_node(&qdf_cpuhp_handlers, &handler->node); qdf_mutex_release(&qdf_cpuhp_lock); qdf_mem_free(handler); *out_handler = NULL; }