brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · e848cc9 Raw
231 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 context18 * @refcount:	refcount for the state19 * @orphan:	boolean indicator that this state has been unregistered20 * @list:	entry in smem_states list21 * @of_node:	of_node to use for matching the state in DT22 * @priv:	implementation private data23 * @ops:	ops for the state24 */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 value39 * @state:	state handle acquired by calling qcom_smem_state_get()40 * @mask:	bit mask for the change41 * @value:	new value for the masked bits42 *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 state81 * @dev:	client device pointer82 * @con_id:	name of the state to lookup83 * @bit:	flags from the state reference, indicating which bit's affected84 *85 * Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() must be86 * 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	of_node_put(state->of_node);140	kfree(state);141}142 143/**144 * qcom_smem_state_put() - release state handle145 * @state:	state handle to be released146 */147void qcom_smem_state_put(struct qcom_smem_state *state)148{149	mutex_lock(&list_lock);150	kref_put(&state->refcount, qcom_smem_state_release);151	mutex_unlock(&list_lock);152}153EXPORT_SYMBOL_GPL(qcom_smem_state_put);154 155static void devm_qcom_smem_state_release(struct device *dev, void *res)156{157	qcom_smem_state_put(*(struct qcom_smem_state **)res);158}159 160/**161 * devm_qcom_smem_state_get() - acquire handle to a devres managed state162 * @dev:	client device pointer163 * @con_id:	name of the state to lookup164 * @bit:	flags from the state reference, indicating which bit's affected165 *166 * Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() is called167 * automatically when @dev is removed.168 */169struct qcom_smem_state *devm_qcom_smem_state_get(struct device *dev,170						 const char *con_id,171						 unsigned *bit)172{173	struct qcom_smem_state **ptr, *state;174 175	ptr = devres_alloc(devm_qcom_smem_state_release, sizeof(*ptr), GFP_KERNEL);176	if (!ptr)177		return ERR_PTR(-ENOMEM);178 179	state = qcom_smem_state_get(dev, con_id, bit);180	if (!IS_ERR(state)) {181		*ptr = state;182		devres_add(dev, ptr);183	} else {184		devres_free(ptr);185	}186 187	return state;188}189EXPORT_SYMBOL_GPL(devm_qcom_smem_state_get);190 191/**192 * qcom_smem_state_register() - register a new state193 * @of_node:	of_node used for matching client lookups194 * @ops:	implementation ops195 * @priv:	implementation specific private data196 */197struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node,198						 const struct qcom_smem_state_ops *ops,199						 void *priv)200{201	struct qcom_smem_state *state;202 203	state = kzalloc(sizeof(*state), GFP_KERNEL);204	if (!state)205		return ERR_PTR(-ENOMEM);206 207	kref_init(&state->refcount);208 209	state->of_node = of_node_get(of_node);210	state->ops = *ops;211	state->priv = priv;212 213	mutex_lock(&list_lock);214	list_add(&state->list, &smem_states);215	mutex_unlock(&list_lock);216 217	return state;218}219EXPORT_SYMBOL_GPL(qcom_smem_state_register);220 221/**222 * qcom_smem_state_unregister() - unregister a registered state223 * @state:	state handle to be unregistered224 */225void qcom_smem_state_unregister(struct qcom_smem_state *state)226{227	state->orphan = true;228	qcom_smem_state_put(state);229}230EXPORT_SYMBOL_GPL(qcom_smem_state_unregister);231