summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2018-11-15 17:49:41 -0800
committerLinux Build Service Account <lnxbuild@localhost>2018-11-15 17:49:41 -0800
commit60e9df34e4699833eca19e10d4497dcab37508aa (patch)
tree55888043166c90382e4bbe6c81c4a791e10c29e1
parent92e6ee5cc2a1041efa8bba06fb245da8c233c90e (diff)
parent3514d2d0a1a2c6fd989e0e91963a9ba2aebab69f (diff)
Merge 3514d2d0a1a2c6fd989e0e91963a9ba2aebab69f on remote branch
Change-Id: I3dbd035c5b4cae34c0c8caed989c1848add7afa6
-rw-r--r--hif/inc/hif.h2
-rw-r--r--hif/src/hif_napi.c155
-rw-r--r--qdf/inc/qdf_cpuhp.h106
-rw-r--r--qdf/linux/src/i_qdf_cpuhp.h34
-rw-r--r--qdf/linux/src/i_qdf_nbuf.h10
-rw-r--r--qdf/linux/src/qdf_cpuhp.c119
-rw-r--r--qdf/src/qdf_cpuhp.c158
7 files changed, 499 insertions, 85 deletions
diff --git a/hif/inc/hif.h b/hif/inc/hif.h
index 9d4aae5a8dba..db34ff80438c 100644
--- a/hif/inc/hif.h
+++ b/hif/inc/hif.h
@@ -200,7 +200,7 @@ struct qca_napi_data {
struct qca_napi_cpu napi_cpu[NR_CPUS];
int lilcl_head, bigcl_head;
enum qca_napi_tput_state napi_mode;
- struct notifier_block hnc_cpu_notifier;
+ struct qdf_cpuhp_handler *cpuhp_handler;
uint8_t flags;
};
diff --git a/hif/src/hif_napi.c b/hif/src/hif_napi.c
index b42be4ea3037..492028f1e477 100644
--- a/hif/src/hif_napi.c
+++ b/hif/src/hif_napi.c
@@ -45,6 +45,7 @@
#include <hif_io32.h>
#include <ce_api.h>
#include <ce_internal.h>
+#include "qdf_cpuhp.h"
enum napi_decision_vector {
HIF_NAPI_NOEVENT = 0,
@@ -1155,103 +1156,91 @@ static int hnc_link_clusters(struct qca_napi_data *napid)
*/
/**
- * hnc_cpu_notify_cb() - handles CPU hotplug events
+ * hnc_cpu_online_cb() - handles CPU hotplug "up" events
+ * @context: the associated HIF context
+ * @cpu: the CPU Id of the CPU the event happened on
*
- * On transitions to online, we onlu handle the ONLINE event,
- * and ignore the PREP events, because we dont want to act too
- * early.
- * On transtion to offline, we act on PREP events, because
- * we may need to move the irqs/NAPIs to another CPU before
- * it is actually off-lined.
- *
- * Return: NOTIFY_OK (dont block action)
+ * Return: None
*/
-static int hnc_cpu_notify_cb(struct notifier_block *nb,
- unsigned long action,
- void *hcpu)
+static void hnc_cpu_online_cb(void *context, uint32_t cpu)
{
- int rc = NOTIFY_OK;
- unsigned long cpu = (unsigned long)hcpu;
- struct hif_opaque_softc *hif;
- struct qca_napi_data *napid = NULL;
+ struct hif_softc *hif = context;
+ struct qca_napi_data *napid = &hif->napi_data;
- NAPI_DEBUG("-->%s(act=%ld, cpu=%ld)", __func__, action, cpu);
+ if (cpu >= NR_CPUS)
+ return;
- napid = qdf_container_of(nb, struct qca_napi_data, hnc_cpu_notifier);
- hif = &qdf_container_of(napid, struct hif_softc, napi_data)->osc;
+ NAPI_DEBUG("-->%s(act=online, cpu=%u)", __func__, cpu);
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- napid->napi_cpu[cpu].state = QCA_NAPI_CPU_UP;
- NAPI_DEBUG("%s: CPU %ld marked %d",
- __func__, cpu, napid->napi_cpu[cpu].state);
- break;
- case CPU_DEAD: /* already dead; we have marked it before, but ... */
- case CPU_DEAD_FROZEN:
- napid->napi_cpu[cpu].state = QCA_NAPI_CPU_DOWN;
- NAPI_DEBUG("%s: CPU %ld marked %d",
- __func__, cpu, napid->napi_cpu[cpu].state);
- break;
- case CPU_DOWN_PREPARE:
- case CPU_DOWN_PREPARE_FROZEN:
- napid->napi_cpu[cpu].state = QCA_NAPI_CPU_DOWN;
-
- NAPI_DEBUG("%s: CPU %ld marked %d; updating affinity",
- __func__, cpu, napid->napi_cpu[cpu].state);
+ napid->napi_cpu[cpu].state = QCA_NAPI_CPU_UP;
+ NAPI_DEBUG("%s: CPU %u marked %d",
+ __func__, cpu, napid->napi_cpu[cpu].state);
- /**
- * we need to move any NAPIs on this CPU out.
- * if we are in LO throughput mode, then this is valid
- * if the CPU is the the low designated CPU.
- */
- hif_napi_event(hif,
- NAPI_EVT_CPU_STATE,
- (void *)
- ((cpu << 16) | napid->napi_cpu[cpu].state));
- break;
- default:
- NAPI_DEBUG("%s: ignored. action: %ld", __func__, action);
- break;
- } /* switch */
- NAPI_DEBUG("<--%s [%d]", __func__, rc);
- return rc;
+ NAPI_DEBUG("<--%s", __func__);
}
/**
- * hnc_hotplug_hook() - installs a hotplug notifier
- * @hif_sc: hif_sc context
- * @register: !0 => register , =0 => deregister
- *
- * Because the callback relies on the data layout of
- * struct hif_softc & its napi_data member, this callback
- * registration requires that the hif_softc is passed in.
+ * hnc_cpu_before_offline_cb() - handles CPU hotplug "prepare down" events
+ * @context: the associated HIF context
+ * @cpu: the CPU Id of the CPU the event happened on
*
- * Note that this is different from the cpu notifier used by
- * rx_thread (cds_schedule.c).
- * We may consider combining these modifiers in the future.
+ * On transtion to offline, we act on PREP events, because we may need to move
+ * the irqs/NAPIs to another CPU before it is actually off-lined.
*
- * Return: 0: success
- * <0: error
+ * Return: None
*/
-static int hnc_hotplug_hook(struct hif_softc *hif_sc, int install)
+static void hnc_cpu_before_offline_cb(void *context, uint32_t cpu)
{
- int rc = 0;
+ struct hif_softc *hif = context;
+ struct qca_napi_data *napid = &hif->napi_data;
- NAPI_DEBUG("-->%s(%d)", __func__, install);
+ if (cpu >= NR_CPUS)
+ return;
- if (install) {
- hif_sc->napi_data.hnc_cpu_notifier.notifier_call
- = hnc_cpu_notify_cb;
- rc = register_hotcpu_notifier(
- &hif_sc->napi_data.hnc_cpu_notifier);
- } else {
- unregister_hotcpu_notifier(
- &hif_sc->napi_data.hnc_cpu_notifier);
- }
+ NAPI_DEBUG("-->%s(act=before_offline, cpu=%u)", __func__, cpu);
- NAPI_DEBUG("<--%s()[%d]", __func__, rc);
- return rc;
+ napid->napi_cpu[cpu].state = QCA_NAPI_CPU_DOWN;
+
+ NAPI_DEBUG("%s: CPU %u marked %d; updating affinity",
+ __func__, cpu, napid->napi_cpu[cpu].state);
+
+ /**
+ * we need to move any NAPIs on this CPU out.
+ * if we are in LO throughput mode, then this is valid
+ * if the CPU is the the low designated CPU.
+ */
+ hif_napi_event(GET_HIF_OPAQUE_HDL(hif),
+ NAPI_EVT_CPU_STATE,
+ (void *)
+ ((size_t)cpu << 16 | napid->napi_cpu[cpu].state));
+
+ NAPI_DEBUG("<--%s", __func__);
+}
+
+static int hnc_hotplug_register(struct hif_softc *hif_sc)
+{
+ QDF_STATUS status;
+
+ NAPI_DEBUG("-->%s", __func__);
+
+ status = qdf_cpuhp_register(&hif_sc->napi_data.cpuhp_handler,
+ hif_sc,
+ hnc_cpu_online_cb,
+ hnc_cpu_before_offline_cb);
+
+ NAPI_DEBUG("<--%s [%d]", __func__, status);
+
+ return qdf_status_to_os_return(status);
+}
+
+static void hnc_hotplug_unregister(struct hif_softc *hif_sc)
+{
+ NAPI_DEBUG("-->%s", __func__);
+
+ if (hif_sc->napi_data.cpuhp_handler)
+ qdf_cpuhp_unregister(&hif_sc->napi_data.cpuhp_handler);
+
+ NAPI_DEBUG("<--%s", __func__);
}
/**
@@ -1332,7 +1321,7 @@ int hif_napi_cpu_init(struct hif_opaque_softc *hif)
goto lab_err_topology;
/* install hotplug notifier */
- rc = hnc_hotplug_hook(HIF_GET_SOFTC(hif), 1);
+ rc = hnc_hotplug_register(HIF_GET_SOFTC(hif));
if (0 != rc)
goto lab_err_hotplug;
@@ -1343,7 +1332,7 @@ int hif_napi_cpu_init(struct hif_opaque_softc *hif)
lab_err_hotplug:
hnc_tput_hook(0);
- hnc_hotplug_hook(HIF_GET_SOFTC(hif), 0);
+ hnc_hotplug_unregister(HIF_GET_SOFTC(hif));
lab_err_topology:
memset(napid->napi_cpu, 0, sizeof(struct qca_napi_cpu) * NR_CPUS);
lab_rss_init:
@@ -1370,7 +1359,7 @@ int hif_napi_cpu_deinit(struct hif_opaque_softc *hif)
rc = hnc_tput_hook(0);
/* uninstall hotplug notifier */
- rc = hnc_hotplug_hook(HIF_GET_SOFTC(hif), 0);
+ hnc_hotplug_unregister(HIF_GET_SOFTC(hif));
/* clear the topology table */
memset(napid->napi_cpu, 0, sizeof(struct qca_napi_cpu) * NR_CPUS);
diff --git a/qdf/inc/qdf_cpuhp.h b/qdf/inc/qdf_cpuhp.h
new file mode 100644
index 000000000000..586f4a2a831e
--- /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
+static inline QDF_STATUS qdf_cpuhp_init(void)
+{
+ return QDF_STATUS_SUCCESS;
+}
+
+static inline QDF_STATUS qdf_cpuhp_deinit(void)
+{
+ return QDF_STATUS_SUCCESS;
+}
+
+static inline 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;
+}
+
+static inline 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/i_qdf_nbuf.h b/qdf/linux/src/i_qdf_nbuf.h
index 4e797fe8629a..ab65c2655853 100644
--- a/qdf/linux/src/i_qdf_nbuf.h
+++ b/qdf/linux/src/i_qdf_nbuf.h
@@ -117,6 +117,8 @@ typedef union {
* @tx.trace.vdev_id : vdev (for protocol trace)
* @tx.ipa.owned : packet owned by IPA
* @tx.ipa.priv : private data, used by IPA
+ * @tx.dma_option.bi_map: flag to do special dma map with QDF_DMA_BIDIRECTIONAL
+ * @tx.dma_option.reserved: reserved bits for future use
* @tx.desc_id : tx desc id, used to sync between host and fw
*/
struct qdf_nbuf_cb {
@@ -192,8 +194,12 @@ struct qdf_nbuf_cb {
uint32_t owned:1,
priv:31;
} ipa; /* 4 */
+ struct {
+ uint8_t bi_map:1,
+ reserved:7;
+ } dma_option; /* 1 byte */
uint16_t desc_id; /* 2 bytes */
- } mcl;/* 14 bytes*/
+ } mcl;/* 15 bytes*/
} dev;
} tx; /* 40 bytes */
} u;
@@ -298,6 +304,8 @@ struct qdf_nbuf_cb {
(((struct qdf_nbuf_cb *)((skb)->cb))->u.tx.dev.mcl.ipa.owned)
#define QDF_NBUF_CB_TX_IPA_PRIV(skb) \
(((struct qdf_nbuf_cb *)((skb)->cb))->u.tx.dev.mcl.ipa.priv)
+#define QDF_NBUF_CB_TX_DMA_BI_MAP(skb) \
+ (((struct qdf_nbuf_cb *)((skb)->cb))->u.tx.dev.mcl.dma_option.bi_map)
#define QDF_NBUF_CB_TX_DESC_ID(skb) \
(((struct qdf_nbuf_cb *)((skb)->cb))->u.tx.dev.mcl.desc_id)
#define QDF_NBUF_CB_TX_FTYPE(skb) \
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..214c92743e6e
--- /dev/null
+++ b/qdf/src/qdf_cpuhp.c
@@ -0,0 +1,158 @@
+/*
+ * 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);
+
+ qdf_mem_free(handler);
+ *out_handler = NULL;
+}
+