soc: qcom: Introduce common SMEM state machine code
authorBjorn Andersson <bjorn.andersson@sonymobile.com>
Fri, 25 Sep 2015 01:25:01 +0000 (18:25 -0700)
committerAndy Gross <agross@codeaurora.org>
Tue, 8 Dec 2015 19:01:01 +0000 (13:01 -0600)
This implements a common API for handling and exposing SMP2P and SMSM
state information.

Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Signed-off-by: Andy Gross <agross@codeaurora.org>
drivers/soc/qcom/Kconfig
drivers/soc/qcom/Makefile
drivers/soc/qcom/smem_state.c [new file with mode: 0644]
include/linux/soc/qcom/smem_state.h [new file with mode: 0644]

index eec76141d9b9a64cb0e606b069c8a63984fcb1ca..df3bd5ad9600aad28465a62352090ab051a6ae29 100644 (file)
@@ -49,3 +49,6 @@ config QCOM_SMD_RPM
 
          Say M here if you want to include support for the Qualcomm RPM as a
          module. This will build a module called "qcom-smd-rpm".
+
+config QCOM_SMEM_STATE
+       bool
index 10a93d168e0edea2cefbed13608a5ef75dc50db6..d756033a46303ea95261edb6aa70eac8e320c493 100644 (file)
@@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_PM)   +=      spm.o
 obj-$(CONFIG_QCOM_SMD) +=      smd.o
 obj-$(CONFIG_QCOM_SMD_RPM)     += smd-rpm.o
 obj-$(CONFIG_QCOM_SMEM) +=     smem.o
+obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
diff --git a/drivers/soc/qcom/smem_state.c b/drivers/soc/qcom/smem_state.c
new file mode 100644 (file)
index 0000000..54261de
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2015, Sony Mobile Communications Inc.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/smem_state.h>
+
+static LIST_HEAD(smem_states);
+static DEFINE_MUTEX(list_lock);
+
+/**
+ * struct qcom_smem_state - state context
+ * @refcount:  refcount for the state
+ * @orphan:    boolean indicator that this state has been unregistered
+ * @list:      entry in smem_states list
+ * @of_node:   of_node to use for matching the state in DT
+ * @priv:      implementation private data
+ * @ops:       ops for the state
+ */
+struct qcom_smem_state {
+       struct kref refcount;
+       bool orphan;
+
+       struct list_head list;
+       struct device_node *of_node;
+
+       void *priv;
+
+       struct qcom_smem_state_ops ops;
+};
+
+/**
+ * qcom_smem_state_update_bits() - update the masked bits in state with value
+ * @state:     state handle acquired by calling qcom_smem_state_get()
+ * @mask:      bit mask for the change
+ * @value:     new value for the masked bits
+ *
+ * Returns 0 on success, otherwise negative errno.
+ */
+int qcom_smem_state_update_bits(struct qcom_smem_state *state,
+                               u32 mask,
+                               u32 value)
+{
+       if (state->orphan)
+               return -ENXIO;
+
+       if (!state->ops.update_bits)
+               return -ENOTSUPP;
+
+       return state->ops.update_bits(state->priv, mask, value);
+}
+EXPORT_SYMBOL_GPL(qcom_smem_state_update_bits);
+
+static struct qcom_smem_state *of_node_to_state(struct device_node *np)
+{
+       struct qcom_smem_state *state;
+
+       mutex_lock(&list_lock);
+
+       list_for_each_entry(state, &smem_states, list) {
+               if (state->of_node == np) {
+                       kref_get(&state->refcount);
+                       goto unlock;
+               }
+       }
+       state = ERR_PTR(-EPROBE_DEFER);
+
+unlock:
+       mutex_unlock(&list_lock);
+
+       return state;
+}
+
+/**
+ * qcom_smem_state_get() - acquire handle to a state
+ * @dev:       client device pointer
+ * @con_id:    name of the state to lookup
+ * @bit:       flags from the state reference, indicating which bit's affected
+ *
+ * Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() must be
+ * called to release the returned state handle.
+ */
+struct qcom_smem_state *qcom_smem_state_get(struct device *dev,
+                                           const char *con_id,
+                                           unsigned *bit)
+{
+       struct qcom_smem_state *state;
+       struct of_phandle_args args;
+       int index = 0;
+       int ret;
+
+       if (con_id) {
+               index = of_property_match_string(dev->of_node,
+                                                "qcom,state-names",
+                                                con_id);
+               if (index < 0) {
+                       dev_err(dev, "missing qcom,state-names\n");
+                       return ERR_PTR(index);
+               }
+       }
+
+       ret = of_parse_phandle_with_args(dev->of_node,
+                                        "qcom,state",
+                                        "#qcom,state-cells",
+                                        index,
+                                        &args);
+       if (ret) {
+               dev_err(dev, "failed to parse qcom,state property\n");
+               return ERR_PTR(ret);
+       }
+
+       if (args.args_count != 1) {
+               dev_err(dev, "invalid #qcom,state-cells\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       state = of_node_to_state(args.np);
+       if (IS_ERR(state))
+               goto put;
+
+       *bit = args.args[0];
+
+put:
+       of_node_put(args.np);
+       return state;
+}
+EXPORT_SYMBOL_GPL(qcom_smem_state_get);
+
+static void qcom_smem_state_release(struct kref *ref)
+{
+       struct qcom_smem_state *state = container_of(ref, struct qcom_smem_state, refcount);
+
+       list_del(&state->list);
+       kfree(state);
+}
+
+/**
+ * qcom_smem_state_put() - release state handle
+ * @state:     state handle to be released
+ */
+void qcom_smem_state_put(struct qcom_smem_state *state)
+{
+       mutex_lock(&list_lock);
+       kref_put(&state->refcount, qcom_smem_state_release);
+       mutex_unlock(&list_lock);
+}
+EXPORT_SYMBOL_GPL(qcom_smem_state_put);
+
+/**
+ * qcom_smem_state_register() - register a new state
+ * @of_node:   of_node used for matching client lookups
+ * @ops:       implementation ops
+ * @priv:      implementation specific private data
+ */
+struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node,
+                                                const struct qcom_smem_state_ops *ops,
+                                                void *priv)
+{
+       struct qcom_smem_state *state;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return ERR_PTR(-ENOMEM);
+
+       kref_init(&state->refcount);
+
+       state->of_node = of_node;
+       state->ops = *ops;
+       state->priv = priv;
+
+       mutex_lock(&list_lock);
+       list_add(&state->list, &smem_states);
+       mutex_unlock(&list_lock);
+
+       return state;
+}
+EXPORT_SYMBOL_GPL(qcom_smem_state_register);
+
+/**
+ * qcom_smem_state_unregister() - unregister a registered state
+ * @state:     state handle to be unregistered
+ */
+void qcom_smem_state_unregister(struct qcom_smem_state *state)
+{
+       state->orphan = true;
+       qcom_smem_state_put(state);
+}
+EXPORT_SYMBOL_GPL(qcom_smem_state_unregister);
diff --git a/include/linux/soc/qcom/smem_state.h b/include/linux/soc/qcom/smem_state.h
new file mode 100644 (file)
index 0000000..f35e151
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __QCOM_SMEM_STATE__
+#define __QCOM_SMEM_STATE__
+
+struct qcom_smem_state;
+
+struct qcom_smem_state_ops {
+       int (*update_bits)(void *, u32, u32);
+};
+
+struct qcom_smem_state *qcom_smem_state_get(struct device *dev, const char *con_id, unsigned *bit);
+void qcom_smem_state_put(struct qcom_smem_state *);
+
+int qcom_smem_state_update_bits(struct qcom_smem_state *state, u32 mask, u32 value);
+
+struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node, const struct qcom_smem_state_ops *ops, void *data);
+void qcom_smem_state_unregister(struct qcom_smem_state *state);
+
+#endif