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