cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

smem_state.c (5532B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      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 context
     18 * @refcount:	refcount for the state
     19 * @orphan:	boolean indicator that this state has been unregistered
     20 * @list:	entry in smem_states list
     21 * @of_node:	of_node to use for matching the state in DT
     22 * @priv:	implementation private data
     23 * @ops:	ops for the state
     24 */
     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 value
     39 * @state:	state handle acquired by calling qcom_smem_state_get()
     40 * @mask:	bit mask for the change
     41 * @value:	new value for the masked bits
     42 *
     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 state
     81 * @dev:	client device pointer
     82 * @con_id:	name of the state to lookup
     83 * @bit:	flags from the state reference, indicating which bit's affected
     84 *
     85 * Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() must be
     86 * 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	kfree(state);
    140}
    141
    142/**
    143 * qcom_smem_state_put() - release state handle
    144 * @state:	state handle to be released
    145 */
    146void qcom_smem_state_put(struct qcom_smem_state *state)
    147{
    148	mutex_lock(&list_lock);
    149	kref_put(&state->refcount, qcom_smem_state_release);
    150	mutex_unlock(&list_lock);
    151}
    152EXPORT_SYMBOL_GPL(qcom_smem_state_put);
    153
    154static void devm_qcom_smem_state_release(struct device *dev, void *res)
    155{
    156	qcom_smem_state_put(*(struct qcom_smem_state **)res);
    157}
    158
    159/**
    160 * devm_qcom_smem_state_get() - acquire handle to a devres managed state
    161 * @dev:	client device pointer
    162 * @con_id:	name of the state to lookup
    163 * @bit:	flags from the state reference, indicating which bit's affected
    164 *
    165 * Returns handle to the state, or ERR_PTR(). qcom_smem_state_put() is called
    166 * automatically when @dev is removed.
    167 */
    168struct qcom_smem_state *devm_qcom_smem_state_get(struct device *dev,
    169						 const char *con_id,
    170						 unsigned *bit)
    171{
    172	struct qcom_smem_state **ptr, *state;
    173
    174	ptr = devres_alloc(devm_qcom_smem_state_release, sizeof(*ptr), GFP_KERNEL);
    175	if (!ptr)
    176		return ERR_PTR(-ENOMEM);
    177
    178	state = qcom_smem_state_get(dev, con_id, bit);
    179	if (!IS_ERR(state)) {
    180		*ptr = state;
    181		devres_add(dev, ptr);
    182	} else {
    183		devres_free(ptr);
    184	}
    185
    186	return state;
    187}
    188EXPORT_SYMBOL_GPL(devm_qcom_smem_state_get);
    189
    190/**
    191 * qcom_smem_state_register() - register a new state
    192 * @of_node:	of_node used for matching client lookups
    193 * @ops:	implementation ops
    194 * @priv:	implementation specific private data
    195 */
    196struct qcom_smem_state *qcom_smem_state_register(struct device_node *of_node,
    197						 const struct qcom_smem_state_ops *ops,
    198						 void *priv)
    199{
    200	struct qcom_smem_state *state;
    201
    202	state = kzalloc(sizeof(*state), GFP_KERNEL);
    203	if (!state)
    204		return ERR_PTR(-ENOMEM);
    205
    206	kref_init(&state->refcount);
    207
    208	state->of_node = of_node;
    209	state->ops = *ops;
    210	state->priv = priv;
    211
    212	mutex_lock(&list_lock);
    213	list_add(&state->list, &smem_states);
    214	mutex_unlock(&list_lock);
    215
    216	return state;
    217}
    218EXPORT_SYMBOL_GPL(qcom_smem_state_register);
    219
    220/**
    221 * qcom_smem_state_unregister() - unregister a registered state
    222 * @state:	state handle to be unregistered
    223 */
    224void qcom_smem_state_unregister(struct qcom_smem_state *state)
    225{
    226	state->orphan = true;
    227	qcom_smem_state_put(state);
    228}
    229EXPORT_SYMBOL_GPL(qcom_smem_state_unregister);