1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2015, Sony Mobile Communications Inc.
4 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
6 #include <linux/device.h>
7 #include <linux/list.h>
8 #include <linux/module.h>
10 #include <linux/slab.h>
11 #include <linux/soc/qcom/smem_state.h>
13 static LIST_HEAD(smem_states
);
14 static DEFINE_MUTEX(list_lock
);
17 * struct qcom_smem_state - state context
18 * @refcount: refcount for the state
19 * @orphan: boolean indicator that this state has been unregistered
20 * @list: entry in smem_states list
21 * @of_node: of_node to use for matching the state in DT
22 * @priv: implementation private data
23 * @ops: ops for the state
25 struct qcom_smem_state
{
29 struct list_head list
;
30 struct device_node
*of_node
;
34 struct qcom_smem_state_ops ops
;
38 * qcom_smem_state_update_bits() - update the masked bits in state with value
39 * @state: state handle acquired by calling qcom_smem_state_get()
40 * @mask: bit mask for the change
41 * @value: new value for the masked bits
43 * Returns 0 on success, otherwise negative errno.
45 int qcom_smem_state_update_bits(struct qcom_smem_state
*state
,
52 if (!state
->ops
.update_bits
)
55 return state
->ops
.update_bits(state
->priv
, mask
, value
);
57 EXPORT_SYMBOL_GPL(qcom_smem_state_update_bits
);
59 static struct qcom_smem_state
*of_node_to_state(struct device_node
*np
)
61 struct qcom_smem_state
*state
;
63 mutex_lock(&list_lock
);
65 list_for_each_entry(state
, &smem_states
, list
) {
66 if (state
->of_node
== np
) {
67 kref_get(&state
->refcount
);
71 state
= ERR_PTR(-EPROBE_DEFER
);
74 mutex_unlock(&list_lock
);
80 * qcom_smem_state_get() - acquire handle to a state
81 * @dev: client device pointer
82 * @con_id: name of the state to lookup
83 * @bit: flags from the state reference, indicating which bit's affected
85 * Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() must be
86 * called to release the returned state handle.
88 struct qcom_smem_state
*qcom_smem_state_get(struct device
*dev
,
92 struct qcom_smem_state
*state
;
93 struct of_phandle_args args
;
98 index
= of_property_match_string(dev
->of_node
,
99 "qcom,smem-state-names",
102 dev_err(dev
, "missing qcom,smem-state-names\n");
103 return ERR_PTR(index
);
107 ret
= of_parse_phandle_with_args(dev
->of_node
,
109 "#qcom,smem-state-cells",
113 dev_err(dev
, "failed to parse qcom,smem-states property\n");
117 if (args
.args_count
!= 1) {
118 dev_err(dev
, "invalid #qcom,smem-state-cells\n");
119 return ERR_PTR(-EINVAL
);
122 state
= of_node_to_state(args
.np
);
129 of_node_put(args
.np
);
132 EXPORT_SYMBOL_GPL(qcom_smem_state_get
);
134 static void qcom_smem_state_release(struct kref
*ref
)
136 struct qcom_smem_state
*state
= container_of(ref
, struct qcom_smem_state
, refcount
);
138 list_del(&state
->list
);
143 * qcom_smem_state_put() - release state handle
144 * @state: state handle to be released
146 void qcom_smem_state_put(struct qcom_smem_state
*state
)
148 mutex_lock(&list_lock
);
149 kref_put(&state
->refcount
, qcom_smem_state_release
);
150 mutex_unlock(&list_lock
);
152 EXPORT_SYMBOL_GPL(qcom_smem_state_put
);
155 * qcom_smem_state_register() - register a new state
156 * @of_node: of_node used for matching client lookups
157 * @ops: implementation ops
158 * @priv: implementation specific private data
160 struct qcom_smem_state
*qcom_smem_state_register(struct device_node
*of_node
,
161 const struct qcom_smem_state_ops
*ops
,
164 struct qcom_smem_state
*state
;
166 state
= kzalloc(sizeof(*state
), GFP_KERNEL
);
168 return ERR_PTR(-ENOMEM
);
170 kref_init(&state
->refcount
);
172 state
->of_node
= of_node
;
176 mutex_lock(&list_lock
);
177 list_add(&state
->list
, &smem_states
);
178 mutex_unlock(&list_lock
);
182 EXPORT_SYMBOL_GPL(qcom_smem_state_register
);
185 * qcom_smem_state_unregister() - unregister a registered state
186 * @state: state handle to be unregistered
188 void qcom_smem_state_unregister(struct qcom_smem_state
*state
)
190 state
->orphan
= true;
191 qcom_smem_state_put(state
);
193 EXPORT_SYMBOL_GPL(qcom_smem_state_unregister
);