summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--qdf/inc/qdf_cpuhp.h106
-rw-r--r--qdf/linux/src/i_qdf_cpuhp.h34
-rw-r--r--qdf/linux/src/qdf_cpuhp.c119
-rw-r--r--qdf/src/qdf_cpuhp.c157
4 files changed, 416 insertions, 0 deletions
diff --git a/qdf/inc/qdf_cpuhp.h b/qdf/inc/qdf_cpuhp.h
new file mode 100644
index 000000000000..ace5e0f3b2db
--- /dev/null
+++ b/qdf/inc/qdf_cpuhp.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2017-2018 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 000000000000..982320efcd9f
--- /dev/null
+++ b/qdf/linux/src/i_qdf_cpuhp.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017-2018 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 000000000000..199213da32dc
--- /dev/null
+++ b/qdf/linux/src/qdf_cpuhp.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2017-2018 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 000000000000..b535d53251b8
--- /dev/null
+++ b/qdf/src/qdf_cpuhp.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2017-2018 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;
+}
+