1 /* 2 * Copyright (c) 2015, Sony Mobile Communications Inc. 3 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 and 7 * only version 2 as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 #include <linux/device.h> 15 #include <linux/list.h> 16 #include <linux/module.h> 17 #include <linux/of.h> 18 #include <linux/slab.h> 19 #include <linux/soc/qcom/smem_state.h> 20 21 static LIST_HEAD(smem_states); 22 static DEFINE_MUTEX(list_lock); 23 24 /** 25 * struct qcom_smem_state - state context 26 * @refcount: refcount for the state 27 * @orphan: boolean indicator that this state has been unregistered 28 * @list: entry in smem_states list 29 * @of_node: of_node to use for matching the state in DT 30 * @priv: implementation private data 31 * @ops: ops for the state 32 */ 33 struct qcom_smem_state { 34 struct kref refcount; 35 bool orphan; 36 37 struct list_head list; 38 struct device_node *of_node; 39 40 void *priv; 41 42 struct qcom_smem_state_ops ops; 43 }; 44 45 /** 46 * qcom_smem_state_update_bits() - update the masked bits in state with value 47 * @state: state handle acquired by calling qcom_smem_state_get() 48 * @mask: bit mask for the change 49 * @value: new value for the masked bits 50 * 51 * Returns 0 on success, otherwise negative errno. 52 */ 53 int qcom_smem_state_update_bits(struct qcom_smem_state *state, 54 u32 mask, 55 u32 value) 56 { 57 if (state->orphan) 58 return -ENXIO; 59 60 if (!state->ops.update_bits) 61 return -ENOTSUPP; 62 63 return state->ops.update_bits(state->priv, mask, value); 64 } 65 EXPORT_SYMBOL_GPL(qcom_smem_state_update_bits); 66 67 static struct qcom_smem_state *of_node_to_state(struct device_node *np) 68 { 69 struct qcom_smem_state *state; 70 71 mutex_lock(&list_lock); 72 73 list_for_each_entry(state, &smem_states, list) { 74 if (state->of_node == np) { 75 kref_get(&state->refcount); 76 goto unlock; 77 } 78 } 79 state = ERR_PTR(-EPROBE_DEFER); 80 81 unlock: 82 mutex_unlock(&list_lock); 83 84 return state; 85 } 86 87 /** 88 * qcom_smem_state_get() - acquire handle to a state 89 * @dev: client device pointer 90 * @con_id: name of the state to lookup 91 * @bit: flags from the state reference, indicating which bit's affected 92 * 93 * Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() must be 94 * called to release the returned state handle. 95 */ 96 struct qcom_smem_state *qcom_smem_state_get(struct device *dev, 97 const char *con_id, 98 unsigned *bit) 99 { 100 struct qcom_smem_state *state; 101 struct of_phandle_args args; 102 int index = 0; 103 int ret; 104 105 if (con_id) { 106 index = of_property_match_string(dev->of_node, 107 "qcom,state-names", 108 con_id); 109 if (index < 0) { 110 dev_err(dev, "missing qcom,state-names\n"); 111 return ERR_PTR(index); 112 } 113 } 114 115 ret = of_parse_phandle_with_args(dev->of_node, 116 "qcom,state", 117 "#qcom,state-cells", 118 index, 119 &args); 120 if (ret) { 121 dev_err(dev, "failed to parse qcom,state property\n"); 122 return ERR_PTR(ret); 123 } 124 125 if (args.args_count != 1) { 126 dev_err(dev, "invalid #qcom,state-cells\n"); 127 return ERR_PTR(-EINVAL); 128 } 129 130 state = of_node_to_state(args.np); 131 if (IS_ERR(state)) 132 goto put; 133 134 *bit = args.args[0]; 135 136 put: 137 of_node_put(args.np); 138 return state; 139 } 140 EXPORT_SYMBOL_GPL(qcom_smem_state_get); 141 142 static void qcom_smem_state_release(struct kref *ref) 143 { 144 struct qcom_smem_state *state = container_of(ref, struct qcom_smem_state, refcount); 145 146 list_del(&state->list); 147 kfree(state); 148 } 149 150 /** 151 * qcom_smem_state_put() - release state handle 152 * @state: state handle to be released 153 */ 154 void qcom_smem_state_put(struct qcom_smem_state *state) 155 { 156 mutex_lock(&list_lock); 157 kref_put(&state->refcount, qcom_smem_state_release); 158 mutex_unlock(&list_lock); 159 } 160 EXPORT_SYMBOL_GPL(qcom_smem_state_put); 161 162 /** 163 * qcom_smem_state_register() - register a new state 164 * @of_node: of_node used for matching client lookups 165 * @ops: implementation ops 166 * @priv: implementation specific private data 167 */ 168 struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node, 169 const struct qcom_smem_state_ops *ops, 170 void *priv) 171 { 172 struct qcom_smem_state *state; 173 174 state = kzalloc(sizeof(*state), GFP_KERNEL); 175 if (!state) 176 return ERR_PTR(-ENOMEM); 177 178 kref_init(&state->refcount); 179 180 state->of_node = of_node; 181 state->ops = *ops; 182 state->priv = priv; 183 184 mutex_lock(&list_lock); 185 list_add(&state->list, &smem_states); 186 mutex_unlock(&list_lock); 187 188 return state; 189 } 190 EXPORT_SYMBOL_GPL(qcom_smem_state_register); 191 192 /** 193 * qcom_smem_state_unregister() - unregister a registered state 194 * @state: state handle to be unregistered 195 */ 196 void qcom_smem_state_unregister(struct qcom_smem_state *state) 197 { 198 state->orphan = true; 199 qcom_smem_state_put(state); 200 } 201 EXPORT_SYMBOL_GPL(qcom_smem_state_unregister); 202