smem_state.c (5532B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (c) 2015, Sony Mobile Communications Inc. 4 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. 5 */ 6#include <linux/device.h> 7#include <linux/list.h> 8#include <linux/module.h> 9#include <linux/of.h> 10#include <linux/slab.h> 11#include <linux/soc/qcom/smem_state.h> 12 13static LIST_HEAD(smem_states); 14static DEFINE_MUTEX(list_lock); 15 16/** 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 24 */ 25struct qcom_smem_state { 26 struct kref refcount; 27 bool orphan; 28 29 struct list_head list; 30 struct device_node *of_node; 31 32 void *priv; 33 34 struct qcom_smem_state_ops ops; 35}; 36 37/** 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 42 * 43 * Returns 0 on success, otherwise negative errno. 44 */ 45int qcom_smem_state_update_bits(struct qcom_smem_state *state, 46 u32 mask, 47 u32 value) 48{ 49 if (state->orphan) 50 return -ENXIO; 51 52 if (!state->ops.update_bits) 53 return -ENOTSUPP; 54 55 return state->ops.update_bits(state->priv, mask, value); 56} 57EXPORT_SYMBOL_GPL(qcom_smem_state_update_bits); 58 59static struct qcom_smem_state *of_node_to_state(struct device_node *np) 60{ 61 struct qcom_smem_state *state; 62 63 mutex_lock(&list_lock); 64 65 list_for_each_entry(state, &smem_states, list) { 66 if (state->of_node == np) { 67 kref_get(&state->refcount); 68 goto unlock; 69 } 70 } 71 state = ERR_PTR(-EPROBE_DEFER); 72 73unlock: 74 mutex_unlock(&list_lock); 75 76 return state; 77} 78 79/** 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 84 * 85 * Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() must be 86 * called to release the returned state handle. 87 */ 88struct qcom_smem_state *qcom_smem_state_get(struct device *dev, 89 const char *con_id, 90 unsigned *bit) 91{ 92 struct qcom_smem_state *state; 93 struct of_phandle_args args; 94 int index = 0; 95 int ret; 96 97 if (con_id) { 98 index = of_property_match_string(dev->of_node, 99 "qcom,smem-state-names", 100 con_id); 101 if (index < 0) { 102 dev_err(dev, "missing qcom,smem-state-names\n"); 103 return ERR_PTR(index); 104 } 105 } 106 107 ret = of_parse_phandle_with_args(dev->of_node, 108 "qcom,smem-states", 109 "#qcom,smem-state-cells", 110 index, 111 &args); 112 if (ret) { 113 dev_err(dev, "failed to parse qcom,smem-states property\n"); 114 return ERR_PTR(ret); 115 } 116 117 if (args.args_count != 1) { 118 dev_err(dev, "invalid #qcom,smem-state-cells\n"); 119 return ERR_PTR(-EINVAL); 120 } 121 122 state = of_node_to_state(args.np); 123 if (IS_ERR(state)) 124 goto put; 125 126 *bit = args.args[0]; 127 128put: 129 of_node_put(args.np); 130 return state; 131} 132EXPORT_SYMBOL_GPL(qcom_smem_state_get); 133 134static void qcom_smem_state_release(struct kref *ref) 135{ 136 struct qcom_smem_state *state = container_of(ref, struct qcom_smem_state, refcount); 137 138 list_del(&state->list); 139 kfree(state); 140} 141 142/** 143 * qcom_smem_state_put() - release state handle 144 * @state: state handle to be released 145 */ 146void qcom_smem_state_put(struct qcom_smem_state *state) 147{ 148 mutex_lock(&list_lock); 149 kref_put(&state->refcount, qcom_smem_state_release); 150 mutex_unlock(&list_lock); 151} 152EXPORT_SYMBOL_GPL(qcom_smem_state_put); 153 154static void devm_qcom_smem_state_release(struct device *dev, void *res) 155{ 156 qcom_smem_state_put(*(struct qcom_smem_state **)res); 157} 158 159/** 160 * devm_qcom_smem_state_get() - acquire handle to a devres managed state 161 * @dev: client device pointer 162 * @con_id: name of the state to lookup 163 * @bit: flags from the state reference, indicating which bit's affected 164 * 165 * Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() is called 166 * automatically when @dev is removed. 167 */ 168struct qcom_smem_state *devm_qcom_smem_state_get(struct device *dev, 169 const char *con_id, 170 unsigned *bit) 171{ 172 struct qcom_smem_state **ptr, *state; 173 174 ptr = devres_alloc(devm_qcom_smem_state_release, sizeof(*ptr), GFP_KERNEL); 175 if (!ptr) 176 return ERR_PTR(-ENOMEM); 177 178 state = qcom_smem_state_get(dev, con_id, bit); 179 if (!IS_ERR(state)) { 180 *ptr = state; 181 devres_add(dev, ptr); 182 } else { 183 devres_free(ptr); 184 } 185 186 return state; 187} 188EXPORT_SYMBOL_GPL(devm_qcom_smem_state_get); 189 190/** 191 * qcom_smem_state_register() - register a new state 192 * @of_node: of_node used for matching client lookups 193 * @ops: implementation ops 194 * @priv: implementation specific private data 195 */ 196struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node, 197 const struct qcom_smem_state_ops *ops, 198 void *priv) 199{ 200 struct qcom_smem_state *state; 201 202 state = kzalloc(sizeof(*state), GFP_KERNEL); 203 if (!state) 204 return ERR_PTR(-ENOMEM); 205 206 kref_init(&state->refcount); 207 208 state->of_node = of_node; 209 state->ops = *ops; 210 state->priv = priv; 211 212 mutex_lock(&list_lock); 213 list_add(&state->list, &smem_states); 214 mutex_unlock(&list_lock); 215 216 return state; 217} 218EXPORT_SYMBOL_GPL(qcom_smem_state_register); 219 220/** 221 * qcom_smem_state_unregister() - unregister a registered state 222 * @state: state handle to be unregistered 223 */ 224void qcom_smem_state_unregister(struct qcom_smem_state *state) 225{ 226 state->orphan = true; 227 qcom_smem_state_put(state); 228} 229EXPORT_SYMBOL_GPL(qcom_smem_state_unregister);