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