xref: /linux/drivers/soc/qcom/smem_state.c (revision 97fb5e8d9b57f10f294303c9a5d1bd033eded6bf)
1*97fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29460ae2fSBjorn Andersson /*
39460ae2fSBjorn Andersson  * Copyright (c) 2015, Sony Mobile Communications Inc.
49460ae2fSBjorn Andersson  * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
59460ae2fSBjorn Andersson  */
69460ae2fSBjorn Andersson #include <linux/device.h>
79460ae2fSBjorn Andersson #include <linux/list.h>
89460ae2fSBjorn Andersson #include <linux/module.h>
99460ae2fSBjorn Andersson #include <linux/of.h>
109460ae2fSBjorn Andersson #include <linux/slab.h>
119460ae2fSBjorn Andersson #include <linux/soc/qcom/smem_state.h>
129460ae2fSBjorn Andersson 
139460ae2fSBjorn Andersson static LIST_HEAD(smem_states);
149460ae2fSBjorn Andersson static DEFINE_MUTEX(list_lock);
159460ae2fSBjorn Andersson 
169460ae2fSBjorn Andersson /**
179460ae2fSBjorn Andersson  * struct qcom_smem_state - state context
189460ae2fSBjorn Andersson  * @refcount:	refcount for the state
199460ae2fSBjorn Andersson  * @orphan:	boolean indicator that this state has been unregistered
209460ae2fSBjorn Andersson  * @list:	entry in smem_states list
219460ae2fSBjorn Andersson  * @of_node:	of_node to use for matching the state in DT
229460ae2fSBjorn Andersson  * @priv:	implementation private data
239460ae2fSBjorn Andersson  * @ops:	ops for the state
249460ae2fSBjorn Andersson  */
259460ae2fSBjorn Andersson struct qcom_smem_state {
269460ae2fSBjorn Andersson 	struct kref refcount;
279460ae2fSBjorn Andersson 	bool orphan;
289460ae2fSBjorn Andersson 
299460ae2fSBjorn Andersson 	struct list_head list;
309460ae2fSBjorn Andersson 	struct device_node *of_node;
319460ae2fSBjorn Andersson 
329460ae2fSBjorn Andersson 	void *priv;
339460ae2fSBjorn Andersson 
349460ae2fSBjorn Andersson 	struct qcom_smem_state_ops ops;
359460ae2fSBjorn Andersson };
369460ae2fSBjorn Andersson 
379460ae2fSBjorn Andersson /**
389460ae2fSBjorn Andersson  * qcom_smem_state_update_bits() - update the masked bits in state with value
399460ae2fSBjorn Andersson  * @state:	state handle acquired by calling qcom_smem_state_get()
409460ae2fSBjorn Andersson  * @mask:	bit mask for the change
419460ae2fSBjorn Andersson  * @value:	new value for the masked bits
429460ae2fSBjorn Andersson  *
439460ae2fSBjorn Andersson  * Returns 0 on success, otherwise negative errno.
449460ae2fSBjorn Andersson  */
459460ae2fSBjorn Andersson int qcom_smem_state_update_bits(struct qcom_smem_state *state,
469460ae2fSBjorn Andersson 				u32 mask,
479460ae2fSBjorn Andersson 				u32 value)
489460ae2fSBjorn Andersson {
499460ae2fSBjorn Andersson 	if (state->orphan)
509460ae2fSBjorn Andersson 		return -ENXIO;
519460ae2fSBjorn Andersson 
529460ae2fSBjorn Andersson 	if (!state->ops.update_bits)
539460ae2fSBjorn Andersson 		return -ENOTSUPP;
549460ae2fSBjorn Andersson 
559460ae2fSBjorn Andersson 	return state->ops.update_bits(state->priv, mask, value);
569460ae2fSBjorn Andersson }
579460ae2fSBjorn Andersson EXPORT_SYMBOL_GPL(qcom_smem_state_update_bits);
589460ae2fSBjorn Andersson 
599460ae2fSBjorn Andersson static struct qcom_smem_state *of_node_to_state(struct device_node *np)
609460ae2fSBjorn Andersson {
619460ae2fSBjorn Andersson 	struct qcom_smem_state *state;
629460ae2fSBjorn Andersson 
639460ae2fSBjorn Andersson 	mutex_lock(&list_lock);
649460ae2fSBjorn Andersson 
659460ae2fSBjorn Andersson 	list_for_each_entry(state, &smem_states, list) {
669460ae2fSBjorn Andersson 		if (state->of_node == np) {
679460ae2fSBjorn Andersson 			kref_get(&state->refcount);
689460ae2fSBjorn Andersson 			goto unlock;
699460ae2fSBjorn Andersson 		}
709460ae2fSBjorn Andersson 	}
719460ae2fSBjorn Andersson 	state = ERR_PTR(-EPROBE_DEFER);
729460ae2fSBjorn Andersson 
739460ae2fSBjorn Andersson unlock:
749460ae2fSBjorn Andersson 	mutex_unlock(&list_lock);
759460ae2fSBjorn Andersson 
769460ae2fSBjorn Andersson 	return state;
779460ae2fSBjorn Andersson }
789460ae2fSBjorn Andersson 
799460ae2fSBjorn Andersson /**
809460ae2fSBjorn Andersson  * qcom_smem_state_get() - acquire handle to a state
819460ae2fSBjorn Andersson  * @dev:	client device pointer
829460ae2fSBjorn Andersson  * @con_id:	name of the state to lookup
839460ae2fSBjorn Andersson  * @bit:	flags from the state reference, indicating which bit's affected
849460ae2fSBjorn Andersson  *
859460ae2fSBjorn Andersson  * Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() must be
869460ae2fSBjorn Andersson  * called to release the returned state handle.
879460ae2fSBjorn Andersson  */
889460ae2fSBjorn Andersson struct qcom_smem_state *qcom_smem_state_get(struct device *dev,
899460ae2fSBjorn Andersson 					    const char *con_id,
909460ae2fSBjorn Andersson 					    unsigned *bit)
919460ae2fSBjorn Andersson {
929460ae2fSBjorn Andersson 	struct qcom_smem_state *state;
939460ae2fSBjorn Andersson 	struct of_phandle_args args;
949460ae2fSBjorn Andersson 	int index = 0;
959460ae2fSBjorn Andersson 	int ret;
969460ae2fSBjorn Andersson 
979460ae2fSBjorn Andersson 	if (con_id) {
989460ae2fSBjorn Andersson 		index = of_property_match_string(dev->of_node,
993680a4a9SBjorn Andersson 						 "qcom,smem-state-names",
1009460ae2fSBjorn Andersson 						 con_id);
1019460ae2fSBjorn Andersson 		if (index < 0) {
1023680a4a9SBjorn Andersson 			dev_err(dev, "missing qcom,smem-state-names\n");
1039460ae2fSBjorn Andersson 			return ERR_PTR(index);
1049460ae2fSBjorn Andersson 		}
1059460ae2fSBjorn Andersson 	}
1069460ae2fSBjorn Andersson 
1079460ae2fSBjorn Andersson 	ret = of_parse_phandle_with_args(dev->of_node,
1083680a4a9SBjorn Andersson 					 "qcom,smem-states",
1093680a4a9SBjorn Andersson 					 "#qcom,smem-state-cells",
1109460ae2fSBjorn Andersson 					 index,
1119460ae2fSBjorn Andersson 					 &args);
1129460ae2fSBjorn Andersson 	if (ret) {
1133680a4a9SBjorn Andersson 		dev_err(dev, "failed to parse qcom,smem-states property\n");
1149460ae2fSBjorn Andersson 		return ERR_PTR(ret);
1159460ae2fSBjorn Andersson 	}
1169460ae2fSBjorn Andersson 
1179460ae2fSBjorn Andersson 	if (args.args_count != 1) {
1183680a4a9SBjorn Andersson 		dev_err(dev, "invalid #qcom,smem-state-cells\n");
1199460ae2fSBjorn Andersson 		return ERR_PTR(-EINVAL);
1209460ae2fSBjorn Andersson 	}
1219460ae2fSBjorn Andersson 
1229460ae2fSBjorn Andersson 	state = of_node_to_state(args.np);
1239460ae2fSBjorn Andersson 	if (IS_ERR(state))
1249460ae2fSBjorn Andersson 		goto put;
1259460ae2fSBjorn Andersson 
1269460ae2fSBjorn Andersson 	*bit = args.args[0];
1279460ae2fSBjorn Andersson 
1289460ae2fSBjorn Andersson put:
1299460ae2fSBjorn Andersson 	of_node_put(args.np);
1309460ae2fSBjorn Andersson 	return state;
1319460ae2fSBjorn Andersson }
1329460ae2fSBjorn Andersson EXPORT_SYMBOL_GPL(qcom_smem_state_get);
1339460ae2fSBjorn Andersson 
1349460ae2fSBjorn Andersson static void qcom_smem_state_release(struct kref *ref)
1359460ae2fSBjorn Andersson {
1369460ae2fSBjorn Andersson 	struct qcom_smem_state *state = container_of(ref, struct qcom_smem_state, refcount);
1379460ae2fSBjorn Andersson 
1389460ae2fSBjorn Andersson 	list_del(&state->list);
1399460ae2fSBjorn Andersson 	kfree(state);
1409460ae2fSBjorn Andersson }
1419460ae2fSBjorn Andersson 
1429460ae2fSBjorn Andersson /**
1439460ae2fSBjorn Andersson  * qcom_smem_state_put() - release state handle
1449460ae2fSBjorn Andersson  * @state:	state handle to be released
1459460ae2fSBjorn Andersson  */
1469460ae2fSBjorn Andersson void qcom_smem_state_put(struct qcom_smem_state *state)
1479460ae2fSBjorn Andersson {
1489460ae2fSBjorn Andersson 	mutex_lock(&list_lock);
1499460ae2fSBjorn Andersson 	kref_put(&state->refcount, qcom_smem_state_release);
1509460ae2fSBjorn Andersson 	mutex_unlock(&list_lock);
1519460ae2fSBjorn Andersson }
1529460ae2fSBjorn Andersson EXPORT_SYMBOL_GPL(qcom_smem_state_put);
1539460ae2fSBjorn Andersson 
1549460ae2fSBjorn Andersson /**
1559460ae2fSBjorn Andersson  * qcom_smem_state_register() - register a new state
1569460ae2fSBjorn Andersson  * @of_node:	of_node used for matching client lookups
1579460ae2fSBjorn Andersson  * @ops:	implementation ops
1589460ae2fSBjorn Andersson  * @priv:	implementation specific private data
1599460ae2fSBjorn Andersson  */
1609460ae2fSBjorn Andersson struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node,
1619460ae2fSBjorn Andersson 						 const struct qcom_smem_state_ops *ops,
1629460ae2fSBjorn Andersson 						 void *priv)
1639460ae2fSBjorn Andersson {
1649460ae2fSBjorn Andersson 	struct qcom_smem_state *state;
1659460ae2fSBjorn Andersson 
1669460ae2fSBjorn Andersson 	state = kzalloc(sizeof(*state), GFP_KERNEL);
1679460ae2fSBjorn Andersson 	if (!state)
1689460ae2fSBjorn Andersson 		return ERR_PTR(-ENOMEM);
1699460ae2fSBjorn Andersson 
1709460ae2fSBjorn Andersson 	kref_init(&state->refcount);
1719460ae2fSBjorn Andersson 
1729460ae2fSBjorn Andersson 	state->of_node = of_node;
1739460ae2fSBjorn Andersson 	state->ops = *ops;
1749460ae2fSBjorn Andersson 	state->priv = priv;
1759460ae2fSBjorn Andersson 
1769460ae2fSBjorn Andersson 	mutex_lock(&list_lock);
1779460ae2fSBjorn Andersson 	list_add(&state->list, &smem_states);
1789460ae2fSBjorn Andersson 	mutex_unlock(&list_lock);
1799460ae2fSBjorn Andersson 
1809460ae2fSBjorn Andersson 	return state;
1819460ae2fSBjorn Andersson }
1829460ae2fSBjorn Andersson EXPORT_SYMBOL_GPL(qcom_smem_state_register);
1839460ae2fSBjorn Andersson 
1849460ae2fSBjorn Andersson /**
1859460ae2fSBjorn Andersson  * qcom_smem_state_unregister() - unregister a registered state
1869460ae2fSBjorn Andersson  * @state:	state handle to be unregistered
1879460ae2fSBjorn Andersson  */
1889460ae2fSBjorn Andersson void qcom_smem_state_unregister(struct qcom_smem_state *state)
1899460ae2fSBjorn Andersson {
1909460ae2fSBjorn Andersson 	state->orphan = true;
1919460ae2fSBjorn Andersson 	qcom_smem_state_put(state);
1929460ae2fSBjorn Andersson }
1939460ae2fSBjorn Andersson EXPORT_SYMBOL_GPL(qcom_smem_state_unregister);
194