diff --git a/qdf/inc/qdf_cpuhp.h b/qdf/inc/qdf_cpuhp.h new file mode 100644 index 0000000000..c3e99bad90 --- /dev/null +++ b/qdf/inc/qdf_cpuhp.h @@ -0,0 +1,106 @@ +/* + * 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 + */ + +#ifndef __QDF_CPUHP_H +#define __QDF_CPUHP_H + +#include "qdf_status.h" +#include "qdf_types.h" + +/** + * struct qdf_cpuhp_handler - an opaque hotplug event registration handle + */ +struct qdf_cpuhp_handler; + +typedef void (*qdf_cpuhp_callback)(void *context, uint32_t cpu); + +#ifdef QCA_CONFIG_SMP +/** + * qdf_cpuhp_init() - Initialize the CPU hotplug event infrastructure + * + * To be called once, globally. + * + * Return: None + */ +QDF_STATUS qdf_cpuhp_init(void); + +/** + * qdf_cpuhp_deinit() - De-initialize the CPU hotplug event infrastructure + * + * To be called once, globally. + * + * Return: None + */ +QDF_STATUS qdf_cpuhp_deinit(void); + +/** + * qdf_cpuhp_register() - Register for CPU up/down event notifications + * @handler: a double pointer to the event registration handle to allocate + * @context: an opaque context to pass back to event listeners + * @up_callback: the function pointer to invoke for CPU up events + * @down_callback: the function pointer to invoke for CPU down events + * + * "Up" happens just after the CPU is up. Inversely, "down" happens just before + * the CPU goes down. + * + * @handler will point to a valid memory address on success, or NULL on failure. + * + * Return: QDF_STATUS + */ +QDF_STATUS qdf_cpuhp_register(struct qdf_cpuhp_handler **handler, + void *context, + qdf_cpuhp_callback up_callback, + qdf_cpuhp_callback down_callback); + +/** + * qdf_cpuhp_unregister() - Un-register for CPU up/down event notifications + * @handler: a double pointer to the event registration handle to de-allocate + * + * @handler point to NULL upon completion + * + * Return: None + */ +void qdf_cpuhp_unregister(struct qdf_cpuhp_handler **handler); +#else +QDF_STATUS qdf_cpuhp_init(void) +{ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS qdf_cpuhp_deinit(void) +{ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS qdf_cpuhp_register(struct qdf_cpuhp_handler **handler, + void *context, + qdf_cpuhp_callback up_callback, + qdf_cpuhp_callback down_callback) +{ + return QDF_STATUS_SUCCESS; +} + +void qdf_cpuhp_unregister(struct qdf_cpuhp_handler **handler) {} +#endif /* QCA_CONFIG_SMP */ + +#endif /* __QDF_CPUHP_H */ diff --git a/qdf/linux/src/i_qdf_cpuhp.h b/qdf/linux/src/i_qdf_cpuhp.h new file mode 100644 index 0000000000..7cee453c65 --- /dev/null +++ b/qdf/linux/src/i_qdf_cpuhp.h @@ -0,0 +1,34 @@ +/* + * 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: i_qdf_cpuhp.h (CPU hotplug) + * Linux-specific definitions for QDF CPU hotplug API's + */ + +#ifndef __I_QDF_CPUHP_H +#define __I_QDF_CPUHP_H + +#include "linux/types.h" + +typedef void (*__qdf_cpuhp_emit)(uint32_t cpu); + +void __qdf_cpuhp_os_init(__qdf_cpuhp_emit on_up, __qdf_cpuhp_emit on_down); +void __qdf_cpuhp_os_deinit(void); + +#endif /* __I_QDF_CPUHP_H */ diff --git a/qdf/linux/src/qdf_cpuhp.c b/qdf/linux/src/qdf_cpuhp.c new file mode 100644 index 0000000000..e192055a2e --- /dev/null +++ b/qdf/linux/src/qdf_cpuhp.c @@ -0,0 +1,119 @@ +/* + * 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 + * This file provides OS dependent QDF CPU hotplug APIs + */ + +#include "i_qdf_cpuhp.h" +#include "qdf_trace.h" +#include "linux/cpu.h" +#include "linux/notifier.h" +#include "linux/version.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) +#include "linux/cpuhotplug.h" +#endif + +static __qdf_cpuhp_emit __qdf_cpuhp_on_up; +static __qdf_cpuhp_emit __qdf_cpuhp_on_down; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) +static int qdf_cpuhp_legacy_handler(struct notifier_block *block, + unsigned long state, + void *hcpu) +{ + unsigned long cpu = (unsigned long)hcpu; + + switch (state) { + case CPU_ONLINE: + __qdf_cpuhp_on_up(cpu); + break; + + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + __qdf_cpuhp_on_down(cpu); + break; + + default: + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block qdf_cpuhp_notifier_block = { + .notifier_call = qdf_cpuhp_legacy_handler, +}; + +static inline void qdf_cpuhp_register_callbacks(void) +{ + register_hotcpu_notifier(&qdf_cpuhp_notifier_block); +} + +static inline void qdf_cpuhp_unregister_callbacks(void) +{ + unregister_hotcpu_notifier(&qdf_cpuhp_notifier_block); +} +#else +static enum cpuhp_state registered_hotplug_state; + +static int qdf_cpuhp_up_handler(unsigned int cpu) +{ + __qdf_cpuhp_on_up(cpu); + + return 0; +} + +static int qdf_cpuhp_down_handler(unsigned int cpu) +{ + __qdf_cpuhp_on_down(cpu); + + return 0; +} + +static inline void qdf_cpuhp_register_callbacks(void) +{ + registered_hotplug_state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "wlan/qca-qdf:online", + qdf_cpuhp_up_handler, + qdf_cpuhp_down_handler); +} + +static inline void qdf_cpuhp_unregister_callbacks(void) +{ + QDF_BUG(registered_hotplug_state); + if (registered_hotplug_state) + cpuhp_remove_state(registered_hotplug_state); +} +#endif /* KERNEL_VERSION(4, 6, 0) */ + +void __qdf_cpuhp_os_init(__qdf_cpuhp_emit on_up, __qdf_cpuhp_emit on_down) +{ + __qdf_cpuhp_on_up = on_up; + __qdf_cpuhp_on_down = on_down; + + qdf_cpuhp_register_callbacks(); +} + +void __qdf_cpuhp_os_deinit(void) +{ + qdf_cpuhp_unregister_callbacks(); +} + diff --git a/qdf/src/qdf_cpuhp.c b/qdf/src/qdf_cpuhp.c new file mode 100644 index 0000000000..ccccafd8f7 --- /dev/null +++ b/qdf/src/qdf_cpuhp.c @@ -0,0 +1,157 @@ +/* + * 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); + + *out_handler = NULL; +} +