1
2
3
4
5
6
7
8
9
10
11
12
13
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>
20
21static LIST_HEAD(smem_states);
22static DEFINE_MUTEX(list_lock);
23
24
25
26
27
28
29
30
31
32
33struct qcom_smem_state {
34 struct kref refcount;
35 bool orphan;
36
37 struct list_head list;
38 struct device_node *of_node;
39
40 void *priv;
41
42 struct qcom_smem_state_ops ops;
43};
44
45
46
47
48
49
50
51
52
53int qcom_smem_state_update_bits(struct qcom_smem_state *state,
54 u32 mask,
55 u32 value)
56{
57 if (state->orphan)
58 return -ENXIO;
59
60 if (!state->ops.update_bits)
61 return -ENOTSUPP;
62
63 return state->ops.update_bits(state->priv, mask, value);
64}
65EXPORT_SYMBOL_GPL(qcom_smem_state_update_bits);
66
67static struct qcom_smem_state *of_node_to_state(struct device_node *np)
68{
69 struct qcom_smem_state *state;
70
71 mutex_lock(&list_lock);
72
73 list_for_each_entry(state, &smem_states, list) {
74 if (state->of_node == np) {
75 kref_get(&state->refcount);
76 goto unlock;
77 }
78 }
79 state = ERR_PTR(-EPROBE_DEFER);
80
81unlock:
82 mutex_unlock(&list_lock);
83
84 return state;
85}
86
87
88
89
90
91
92
93
94
95
96struct qcom_smem_state *qcom_smem_state_get(struct device *dev,
97 const char *con_id,
98 unsigned *bit)
99{
100 struct qcom_smem_state *state;
101 struct of_phandle_args args;
102 int index = 0;
103 int ret;
104
105 if (con_id) {
106 index = of_property_match_string(dev->of_node,
107 "qcom,smem-state-names",
108 con_id);
109 if (index < 0) {
110 dev_err(dev, "missing qcom,smem-state-names\n");
111 return ERR_PTR(index);
112 }
113 }
114
115 ret = of_parse_phandle_with_args(dev->of_node,
116 "qcom,smem-states",
117 "#qcom,smem-state-cells",
118 index,
119 &args);
120 if (ret) {
121 dev_err(dev, "failed to parse qcom,smem-states property\n");
122 return ERR_PTR(ret);
123 }
124
125 if (args.args_count != 1) {
126 dev_err(dev, "invalid #qcom,smem-state-cells\n");
127 return ERR_PTR(-EINVAL);
128 }
129
130 state = of_node_to_state(args.np);
131 if (IS_ERR(state))
132 goto put;
133
134 *bit = args.args[0];
135
136put:
137 of_node_put(args.np);
138 return state;
139}
140EXPORT_SYMBOL_GPL(qcom_smem_state_get);
141
142static void qcom_smem_state_release(struct kref *ref)
143{
144 struct qcom_smem_state *state = container_of(ref, struct qcom_smem_state, refcount);
145
146 list_del(&state->list);
147 kfree(state);
148}
149
150
151
152
153
154void qcom_smem_state_put(struct qcom_smem_state *state)
155{
156 mutex_lock(&list_lock);
157 kref_put(&state->refcount, qcom_smem_state_release);
158 mutex_unlock(&list_lock);
159}
160EXPORT_SYMBOL_GPL(qcom_smem_state_put);
161
162
163
164
165
166
167
168struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node,
169 const struct qcom_smem_state_ops *ops,
170 void *priv)
171{
172 struct qcom_smem_state *state;
173
174 state = kzalloc(sizeof(*state), GFP_KERNEL);
175 if (!state)
176 return ERR_PTR(-ENOMEM);
177
178 kref_init(&state->refcount);
179
180 state->of_node = of_node;
181 state->ops = *ops;
182 state->priv = priv;
183
184 mutex_lock(&list_lock);
185 list_add(&state->list, &smem_states);
186 mutex_unlock(&list_lock);
187
188 return state;
189}
190EXPORT_SYMBOL_GPL(qcom_smem_state_register);
191
192
193
194
195
196void qcom_smem_state_unregister(struct qcom_smem_state *state)
197{
198 state->orphan = true;
199 qcom_smem_state_put(state);
200}
201EXPORT_SYMBOL_GPL(qcom_smem_state_unregister);
202