arm64: report CPU number in bad_mode
[linux-2.6/btrfs-unstable.git] / drivers / soc / qcom / smem_state.c
blob54261decb369250c7cef09108d63517a972bb6ee
1 /*
2 * Copyright (c) 2015, Sony Mobile Communications Inc.
3 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 and
7 * only version 2 as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 #include <linux/device.h>
15 #include <linux/list.h>
16 #include <linux/module.h>
17 #include <linux/of.h>
18 #include <linux/slab.h>
19 #include <linux/soc/qcom/smem_state.h>
21 static LIST_HEAD(smem_states);
22 static DEFINE_MUTEX(list_lock);
24 /**
25 * struct qcom_smem_state - state context
26 * @refcount: refcount for the state
27 * @orphan: boolean indicator that this state has been unregistered
28 * @list: entry in smem_states list
29 * @of_node: of_node to use for matching the state in DT
30 * @priv: implementation private data
31 * @ops: ops for the state
33 struct qcom_smem_state {
34 struct kref refcount;
35 bool orphan;
37 struct list_head list;
38 struct device_node *of_node;
40 void *priv;
42 struct qcom_smem_state_ops ops;
45 /**
46 * qcom_smem_state_update_bits() - update the masked bits in state with value
47 * @state: state handle acquired by calling qcom_smem_state_get()
48 * @mask: bit mask for the change
49 * @value: new value for the masked bits
51 * Returns 0 on success, otherwise negative errno.
53 int qcom_smem_state_update_bits(struct qcom_smem_state *state,
54 u32 mask,
55 u32 value)
57 if (state->orphan)
58 return -ENXIO;
60 if (!state->ops.update_bits)
61 return -ENOTSUPP;
63 return state->ops.update_bits(state->priv, mask, value);
65 EXPORT_SYMBOL_GPL(qcom_smem_state_update_bits);
67 static struct qcom_smem_state *of_node_to_state(struct device_node *np)
69 struct qcom_smem_state *state;
71 mutex_lock(&list_lock);
73 list_for_each_entry(state, &smem_states, list) {
74 if (state->of_node == np) {
75 kref_get(&state->refcount);
76 goto unlock;
79 state = ERR_PTR(-EPROBE_DEFER);
81 unlock:
82 mutex_unlock(&list_lock);
84 return state;
87 /**
88 * qcom_smem_state_get() - acquire handle to a state
89 * @dev: client device pointer
90 * @con_id: name of the state to lookup
91 * @bit: flags from the state reference, indicating which bit's affected
93 * Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() must be
94 * called to release the returned state handle.
96 struct qcom_smem_state *qcom_smem_state_get(struct device *dev,
97 const char *con_id,
98 unsigned *bit)
100 struct qcom_smem_state *state;
101 struct of_phandle_args args;
102 int index = 0;
103 int ret;
105 if (con_id) {
106 index = of_property_match_string(dev->of_node,
107 "qcom,state-names",
108 con_id);
109 if (index < 0) {
110 dev_err(dev, "missing qcom,state-names\n");
111 return ERR_PTR(index);
115 ret = of_parse_phandle_with_args(dev->of_node,
116 "qcom,state",
117 "#qcom,state-cells",
118 index,
119 &args);
120 if (ret) {
121 dev_err(dev, "failed to parse qcom,state property\n");
122 return ERR_PTR(ret);
125 if (args.args_count != 1) {
126 dev_err(dev, "invalid #qcom,state-cells\n");
127 return ERR_PTR(-EINVAL);
130 state = of_node_to_state(args.np);
131 if (IS_ERR(state))
132 goto put;
134 *bit = args.args[0];
136 put:
137 of_node_put(args.np);
138 return state;
140 EXPORT_SYMBOL_GPL(qcom_smem_state_get);
142 static void qcom_smem_state_release(struct kref *ref)
144 struct qcom_smem_state *state = container_of(ref, struct qcom_smem_state, refcount);
146 list_del(&state->list);
147 kfree(state);
151 * qcom_smem_state_put() - release state handle
152 * @state: state handle to be released
154 void qcom_smem_state_put(struct qcom_smem_state *state)
156 mutex_lock(&list_lock);
157 kref_put(&state->refcount, qcom_smem_state_release);
158 mutex_unlock(&list_lock);
160 EXPORT_SYMBOL_GPL(qcom_smem_state_put);
163 * qcom_smem_state_register() - register a new state
164 * @of_node: of_node used for matching client lookups
165 * @ops: implementation ops
166 * @priv: implementation specific private data
168 struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node,
169 const struct qcom_smem_state_ops *ops,
170 void *priv)
172 struct qcom_smem_state *state;
174 state = kzalloc(sizeof(*state), GFP_KERNEL);
175 if (!state)
176 return ERR_PTR(-ENOMEM);
178 kref_init(&state->refcount);
180 state->of_node = of_node;
181 state->ops = *ops;
182 state->priv = priv;
184 mutex_lock(&list_lock);
185 list_add(&state->list, &smem_states);
186 mutex_unlock(&list_lock);
188 return state;
190 EXPORT_SYMBOL_GPL(qcom_smem_state_register);
193 * qcom_smem_state_unregister() - unregister a registered state
194 * @state: state handle to be unregistered
196 void qcom_smem_state_unregister(struct qcom_smem_state *state)
198 state->orphan = true;
199 qcom_smem_state_put(state);
201 EXPORT_SYMBOL_GPL(qcom_smem_state_unregister);