18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * System Trace Module (STM) infrastructure
48c2ecf20Sopenharmony_ci * Copyright (c) 2014, Intel Corporation.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * STM class implements generic infrastructure for  System Trace Module devices
78c2ecf20Sopenharmony_ci * as defined in MIPI STPv2 specification.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
118c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/device.h>
158c2ecf20Sopenharmony_ci#include <linux/compat.h>
168c2ecf20Sopenharmony_ci#include <linux/kdev_t.h>
178c2ecf20Sopenharmony_ci#include <linux/srcu.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/stm.h>
208c2ecf20Sopenharmony_ci#include <linux/fs.h>
218c2ecf20Sopenharmony_ci#include <linux/mm.h>
228c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
238c2ecf20Sopenharmony_ci#include "stm.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <uapi/linux/stm.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic unsigned int stm_core_up;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * The SRCU here makes sure that STM device doesn't disappear from under a
318c2ecf20Sopenharmony_ci * stm_source_write() caller, which may want to have as little overhead as
328c2ecf20Sopenharmony_ci * possible.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistatic struct srcu_struct stm_source_srcu;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic ssize_t masters_show(struct device *dev,
378c2ecf20Sopenharmony_ci			    struct device_attribute *attr,
388c2ecf20Sopenharmony_ci			    char *buf)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct stm_device *stm = to_stm_device(dev);
418c2ecf20Sopenharmony_ci	int ret;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	ret = sprintf(buf, "%u %u\n", stm->data->sw_start, stm->data->sw_end);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	return ret;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(masters);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic ssize_t channels_show(struct device *dev,
518c2ecf20Sopenharmony_ci			     struct device_attribute *attr,
528c2ecf20Sopenharmony_ci			     char *buf)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	struct stm_device *stm = to_stm_device(dev);
558c2ecf20Sopenharmony_ci	int ret;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	ret = sprintf(buf, "%u\n", stm->data->sw_nchannels);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return ret;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(channels);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic ssize_t hw_override_show(struct device *dev,
658c2ecf20Sopenharmony_ci				struct device_attribute *attr,
668c2ecf20Sopenharmony_ci				char *buf)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct stm_device *stm = to_stm_device(dev);
698c2ecf20Sopenharmony_ci	int ret;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	ret = sprintf(buf, "%u\n", stm->data->hw_override);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return ret;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(hw_override);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic struct attribute *stm_attrs[] = {
798c2ecf20Sopenharmony_ci	&dev_attr_masters.attr,
808c2ecf20Sopenharmony_ci	&dev_attr_channels.attr,
818c2ecf20Sopenharmony_ci	&dev_attr_hw_override.attr,
828c2ecf20Sopenharmony_ci	NULL,
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(stm);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic struct class stm_class = {
888c2ecf20Sopenharmony_ci	.name		= "stm",
898c2ecf20Sopenharmony_ci	.dev_groups	= stm_groups,
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/**
938c2ecf20Sopenharmony_ci * stm_find_device() - find stm device by name
948c2ecf20Sopenharmony_ci * @buf:	character buffer containing the name
958c2ecf20Sopenharmony_ci *
968c2ecf20Sopenharmony_ci * This is called when either policy gets assigned to an stm device or an
978c2ecf20Sopenharmony_ci * stm_source device gets linked to an stm device.
988c2ecf20Sopenharmony_ci *
998c2ecf20Sopenharmony_ci * This grabs device's reference (get_device()) and module reference, both
1008c2ecf20Sopenharmony_ci * of which the calling path needs to make sure to drop with stm_put_device().
1018c2ecf20Sopenharmony_ci *
1028c2ecf20Sopenharmony_ci * Return:	stm device pointer or null if lookup failed.
1038c2ecf20Sopenharmony_ci */
1048c2ecf20Sopenharmony_cistruct stm_device *stm_find_device(const char *buf)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct stm_device *stm;
1078c2ecf20Sopenharmony_ci	struct device *dev;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (!stm_core_up)
1108c2ecf20Sopenharmony_ci		return NULL;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	dev = class_find_device_by_name(&stm_class, buf);
1138c2ecf20Sopenharmony_ci	if (!dev)
1148c2ecf20Sopenharmony_ci		return NULL;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	stm = to_stm_device(dev);
1178c2ecf20Sopenharmony_ci	if (!try_module_get(stm->owner)) {
1188c2ecf20Sopenharmony_ci		/* matches class_find_device() above */
1198c2ecf20Sopenharmony_ci		put_device(dev);
1208c2ecf20Sopenharmony_ci		return NULL;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return stm;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/**
1278c2ecf20Sopenharmony_ci * stm_put_device() - drop references on the stm device
1288c2ecf20Sopenharmony_ci * @stm:	stm device, previously acquired by stm_find_device()
1298c2ecf20Sopenharmony_ci *
1308c2ecf20Sopenharmony_ci * This drops the module reference and device reference taken by
1318c2ecf20Sopenharmony_ci * stm_find_device() or stm_char_open().
1328c2ecf20Sopenharmony_ci */
1338c2ecf20Sopenharmony_civoid stm_put_device(struct stm_device *stm)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	module_put(stm->owner);
1368c2ecf20Sopenharmony_ci	put_device(&stm->dev);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/*
1408c2ecf20Sopenharmony_ci * Internally we only care about software-writable masters here, that is the
1418c2ecf20Sopenharmony_ci * ones in the range [stm_data->sw_start..stm_data..sw_end], however we need
1428c2ecf20Sopenharmony_ci * original master numbers to be visible externally, since they are the ones
1438c2ecf20Sopenharmony_ci * that will appear in the STP stream. Thus, the internal bookkeeping uses
1448c2ecf20Sopenharmony_ci * $master - stm_data->sw_start to reference master descriptors and such.
1458c2ecf20Sopenharmony_ci */
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci#define __stm_master(_s, _m)				\
1488c2ecf20Sopenharmony_ci	((_s)->masters[(_m) - (_s)->data->sw_start])
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic inline struct stp_master *
1518c2ecf20Sopenharmony_cistm_master(struct stm_device *stm, unsigned int idx)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	if (idx < stm->data->sw_start || idx > stm->data->sw_end)
1548c2ecf20Sopenharmony_ci		return NULL;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return __stm_master(stm, idx);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int stp_master_alloc(struct stm_device *stm, unsigned int idx)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct stp_master *master;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	master = kzalloc(struct_size(master, chan_map,
1648c2ecf20Sopenharmony_ci				     BITS_TO_LONGS(stm->data->sw_nchannels)),
1658c2ecf20Sopenharmony_ci			 GFP_ATOMIC);
1668c2ecf20Sopenharmony_ci	if (!master)
1678c2ecf20Sopenharmony_ci		return -ENOMEM;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	master->nr_free = stm->data->sw_nchannels;
1708c2ecf20Sopenharmony_ci	__stm_master(stm, idx) = master;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void stp_master_free(struct stm_device *stm, unsigned int idx)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct stp_master *master = stm_master(stm, idx);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (!master)
1808c2ecf20Sopenharmony_ci		return;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	__stm_master(stm, idx) = NULL;
1838c2ecf20Sopenharmony_ci	kfree(master);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic void stm_output_claim(struct stm_device *stm, struct stm_output *output)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct stp_master *master = stm_master(stm, output->master);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	lockdep_assert_held(&stm->mc_lock);
1918c2ecf20Sopenharmony_ci	lockdep_assert_held(&output->lock);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(master->nr_free < output->nr_chans))
1948c2ecf20Sopenharmony_ci		return;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	bitmap_allocate_region(&master->chan_map[0], output->channel,
1978c2ecf20Sopenharmony_ci			       ilog2(output->nr_chans));
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	master->nr_free -= output->nr_chans;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void
2038c2ecf20Sopenharmony_cistm_output_disclaim(struct stm_device *stm, struct stm_output *output)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct stp_master *master = stm_master(stm, output->master);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	lockdep_assert_held(&stm->mc_lock);
2088c2ecf20Sopenharmony_ci	lockdep_assert_held(&output->lock);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	bitmap_release_region(&master->chan_map[0], output->channel,
2118c2ecf20Sopenharmony_ci			      ilog2(output->nr_chans));
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	master->nr_free += output->nr_chans;
2148c2ecf20Sopenharmony_ci	output->nr_chans = 0;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/*
2188c2ecf20Sopenharmony_ci * This is like bitmap_find_free_region(), except it can ignore @start bits
2198c2ecf20Sopenharmony_ci * at the beginning.
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_cistatic int find_free_channels(unsigned long *bitmap, unsigned int start,
2228c2ecf20Sopenharmony_ci			      unsigned int end, unsigned int width)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	unsigned int pos;
2258c2ecf20Sopenharmony_ci	int i;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	for (pos = start; pos < end + 1; pos = ALIGN(pos, width)) {
2288c2ecf20Sopenharmony_ci		pos = find_next_zero_bit(bitmap, end + 1, pos);
2298c2ecf20Sopenharmony_ci		if (pos + width > end + 1)
2308c2ecf20Sopenharmony_ci			break;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		if (pos & (width - 1))
2338c2ecf20Sopenharmony_ci			continue;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		for (i = 1; i < width && !test_bit(pos + i, bitmap); i++)
2368c2ecf20Sopenharmony_ci			;
2378c2ecf20Sopenharmony_ci		if (i == width)
2388c2ecf20Sopenharmony_ci			return pos;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		/* step over [pos..pos+i) to continue search */
2418c2ecf20Sopenharmony_ci		pos += i;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	return -1;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int
2488c2ecf20Sopenharmony_cistm_find_master_chan(struct stm_device *stm, unsigned int width,
2498c2ecf20Sopenharmony_ci		     unsigned int *mstart, unsigned int mend,
2508c2ecf20Sopenharmony_ci		     unsigned int *cstart, unsigned int cend)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct stp_master *master;
2538c2ecf20Sopenharmony_ci	unsigned int midx;
2548c2ecf20Sopenharmony_ci	int pos, err;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	for (midx = *mstart; midx <= mend; midx++) {
2578c2ecf20Sopenharmony_ci		if (!stm_master(stm, midx)) {
2588c2ecf20Sopenharmony_ci			err = stp_master_alloc(stm, midx);
2598c2ecf20Sopenharmony_ci			if (err)
2608c2ecf20Sopenharmony_ci				return err;
2618c2ecf20Sopenharmony_ci		}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci		master = stm_master(stm, midx);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		if (!master->nr_free)
2668c2ecf20Sopenharmony_ci			continue;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		pos = find_free_channels(master->chan_map, *cstart, cend,
2698c2ecf20Sopenharmony_ci					 width);
2708c2ecf20Sopenharmony_ci		if (pos < 0)
2718c2ecf20Sopenharmony_ci			continue;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		*mstart = midx;
2748c2ecf20Sopenharmony_ci		*cstart = pos;
2758c2ecf20Sopenharmony_ci		return 0;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return -ENOSPC;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic int stm_output_assign(struct stm_device *stm, unsigned int width,
2828c2ecf20Sopenharmony_ci			     struct stp_policy_node *policy_node,
2838c2ecf20Sopenharmony_ci			     struct stm_output *output)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	unsigned int midx, cidx, mend, cend;
2868c2ecf20Sopenharmony_ci	int ret = -EINVAL;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (width > stm->data->sw_nchannels)
2898c2ecf20Sopenharmony_ci		return -EINVAL;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* We no longer accept policy_node==NULL here */
2928c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!policy_node))
2938c2ecf20Sopenharmony_ci		return -EINVAL;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/*
2968c2ecf20Sopenharmony_ci	 * Also, the caller holds reference to policy_node, so it won't
2978c2ecf20Sopenharmony_ci	 * disappear on us.
2988c2ecf20Sopenharmony_ci	 */
2998c2ecf20Sopenharmony_ci	stp_policy_node_get_ranges(policy_node, &midx, &mend, &cidx, &cend);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	spin_lock(&stm->mc_lock);
3028c2ecf20Sopenharmony_ci	spin_lock(&output->lock);
3038c2ecf20Sopenharmony_ci	/* output is already assigned -- shouldn't happen */
3048c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(output->nr_chans))
3058c2ecf20Sopenharmony_ci		goto unlock;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	ret = stm_find_master_chan(stm, width, &midx, mend, &cidx, cend);
3088c2ecf20Sopenharmony_ci	if (ret < 0)
3098c2ecf20Sopenharmony_ci		goto unlock;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	output->master = midx;
3128c2ecf20Sopenharmony_ci	output->channel = cidx;
3138c2ecf20Sopenharmony_ci	output->nr_chans = width;
3148c2ecf20Sopenharmony_ci	if (stm->pdrv->output_open) {
3158c2ecf20Sopenharmony_ci		void *priv = stp_policy_node_priv(policy_node);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!priv))
3188c2ecf20Sopenharmony_ci			goto unlock;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		/* configfs subsys mutex is held by the caller */
3218c2ecf20Sopenharmony_ci		ret = stm->pdrv->output_open(priv, output);
3228c2ecf20Sopenharmony_ci		if (ret)
3238c2ecf20Sopenharmony_ci			goto unlock;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	stm_output_claim(stm, output);
3278c2ecf20Sopenharmony_ci	dev_dbg(&stm->dev, "assigned %u:%u (+%u)\n", midx, cidx, width);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	ret = 0;
3308c2ecf20Sopenharmony_ciunlock:
3318c2ecf20Sopenharmony_ci	if (ret)
3328c2ecf20Sopenharmony_ci		output->nr_chans = 0;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	spin_unlock(&output->lock);
3358c2ecf20Sopenharmony_ci	spin_unlock(&stm->mc_lock);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	return ret;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic void stm_output_free(struct stm_device *stm, struct stm_output *output)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	spin_lock(&stm->mc_lock);
3438c2ecf20Sopenharmony_ci	spin_lock(&output->lock);
3448c2ecf20Sopenharmony_ci	if (output->nr_chans)
3458c2ecf20Sopenharmony_ci		stm_output_disclaim(stm, output);
3468c2ecf20Sopenharmony_ci	if (stm->pdrv && stm->pdrv->output_close)
3478c2ecf20Sopenharmony_ci		stm->pdrv->output_close(output);
3488c2ecf20Sopenharmony_ci	spin_unlock(&output->lock);
3498c2ecf20Sopenharmony_ci	spin_unlock(&stm->mc_lock);
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic void stm_output_init(struct stm_output *output)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	spin_lock_init(&output->lock);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic int major_match(struct device *dev, const void *data)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	unsigned int major = *(unsigned int *)data;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	return MAJOR(dev->devt) == major;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci/*
3658c2ecf20Sopenharmony_ci * Framing protocol management
3668c2ecf20Sopenharmony_ci * Modules can implement STM protocol drivers and (un-)register them
3678c2ecf20Sopenharmony_ci * with the STM class framework.
3688c2ecf20Sopenharmony_ci */
3698c2ecf20Sopenharmony_cistatic struct list_head stm_pdrv_head;
3708c2ecf20Sopenharmony_cistatic struct mutex stm_pdrv_mutex;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistruct stm_pdrv_entry {
3738c2ecf20Sopenharmony_ci	struct list_head			entry;
3748c2ecf20Sopenharmony_ci	const struct stm_protocol_driver	*pdrv;
3758c2ecf20Sopenharmony_ci	const struct config_item_type		*node_type;
3768c2ecf20Sopenharmony_ci};
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic const struct stm_pdrv_entry *
3798c2ecf20Sopenharmony_ci__stm_lookup_protocol(const char *name)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct stm_pdrv_entry *pe;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/*
3848c2ecf20Sopenharmony_ci	 * If no name is given (NULL or ""), fall back to "p_basic".
3858c2ecf20Sopenharmony_ci	 */
3868c2ecf20Sopenharmony_ci	if (!name || !*name)
3878c2ecf20Sopenharmony_ci		name = "p_basic";
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	list_for_each_entry(pe, &stm_pdrv_head, entry) {
3908c2ecf20Sopenharmony_ci		if (!strcmp(name, pe->pdrv->name))
3918c2ecf20Sopenharmony_ci			return pe;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return NULL;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ciint stm_register_protocol(const struct stm_protocol_driver *pdrv)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct stm_pdrv_entry *pe = NULL;
4008c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	mutex_lock(&stm_pdrv_mutex);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (__stm_lookup_protocol(pdrv->name)) {
4058c2ecf20Sopenharmony_ci		ret = -EEXIST;
4068c2ecf20Sopenharmony_ci		goto unlock;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	pe = kzalloc(sizeof(*pe), GFP_KERNEL);
4108c2ecf20Sopenharmony_ci	if (!pe)
4118c2ecf20Sopenharmony_ci		goto unlock;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (pdrv->policy_attr) {
4148c2ecf20Sopenharmony_ci		pe->node_type = get_policy_node_type(pdrv->policy_attr);
4158c2ecf20Sopenharmony_ci		if (!pe->node_type)
4168c2ecf20Sopenharmony_ci			goto unlock;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	list_add_tail(&pe->entry, &stm_pdrv_head);
4208c2ecf20Sopenharmony_ci	pe->pdrv = pdrv;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	ret = 0;
4238c2ecf20Sopenharmony_ciunlock:
4248c2ecf20Sopenharmony_ci	mutex_unlock(&stm_pdrv_mutex);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (ret)
4278c2ecf20Sopenharmony_ci		kfree(pe);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return ret;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stm_register_protocol);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_civoid stm_unregister_protocol(const struct stm_protocol_driver *pdrv)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	struct stm_pdrv_entry *pe, *iter;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	mutex_lock(&stm_pdrv_mutex);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pe, iter, &stm_pdrv_head, entry) {
4408c2ecf20Sopenharmony_ci		if (pe->pdrv == pdrv) {
4418c2ecf20Sopenharmony_ci			list_del(&pe->entry);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci			if (pe->node_type) {
4448c2ecf20Sopenharmony_ci				kfree(pe->node_type->ct_attrs);
4458c2ecf20Sopenharmony_ci				kfree(pe->node_type);
4468c2ecf20Sopenharmony_ci			}
4478c2ecf20Sopenharmony_ci			kfree(pe);
4488c2ecf20Sopenharmony_ci			break;
4498c2ecf20Sopenharmony_ci		}
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	mutex_unlock(&stm_pdrv_mutex);
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stm_unregister_protocol);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic bool stm_get_protocol(const struct stm_protocol_driver *pdrv)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	return try_module_get(pdrv->owner);
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_civoid stm_put_protocol(const struct stm_protocol_driver *pdrv)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	module_put(pdrv->owner);
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ciint stm_lookup_protocol(const char *name,
4678c2ecf20Sopenharmony_ci			const struct stm_protocol_driver **pdrv,
4688c2ecf20Sopenharmony_ci			const struct config_item_type **node_type)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	const struct stm_pdrv_entry *pe;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	mutex_lock(&stm_pdrv_mutex);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	pe = __stm_lookup_protocol(name);
4758c2ecf20Sopenharmony_ci	if (pe && pe->pdrv && stm_get_protocol(pe->pdrv)) {
4768c2ecf20Sopenharmony_ci		*pdrv = pe->pdrv;
4778c2ecf20Sopenharmony_ci		*node_type = pe->node_type;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	mutex_unlock(&stm_pdrv_mutex);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	return pe ? 0 : -ENOENT;
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic int stm_char_open(struct inode *inode, struct file *file)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	struct stm_file *stmf;
4888c2ecf20Sopenharmony_ci	struct device *dev;
4898c2ecf20Sopenharmony_ci	unsigned int major = imajor(inode);
4908c2ecf20Sopenharmony_ci	int err = -ENOMEM;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	dev = class_find_device(&stm_class, NULL, &major, major_match);
4938c2ecf20Sopenharmony_ci	if (!dev)
4948c2ecf20Sopenharmony_ci		return -ENODEV;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	stmf = kzalloc(sizeof(*stmf), GFP_KERNEL);
4978c2ecf20Sopenharmony_ci	if (!stmf)
4988c2ecf20Sopenharmony_ci		goto err_put_device;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	err = -ENODEV;
5018c2ecf20Sopenharmony_ci	stm_output_init(&stmf->output);
5028c2ecf20Sopenharmony_ci	stmf->stm = to_stm_device(dev);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if (!try_module_get(stmf->stm->owner))
5058c2ecf20Sopenharmony_ci		goto err_free;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	file->private_data = stmf;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	return nonseekable_open(inode, file);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cierr_free:
5128c2ecf20Sopenharmony_ci	kfree(stmf);
5138c2ecf20Sopenharmony_cierr_put_device:
5148c2ecf20Sopenharmony_ci	/* matches class_find_device() above */
5158c2ecf20Sopenharmony_ci	put_device(dev);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	return err;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic int stm_char_release(struct inode *inode, struct file *file)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	struct stm_file *stmf = file->private_data;
5238c2ecf20Sopenharmony_ci	struct stm_device *stm = stmf->stm;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (stm->data->unlink)
5268c2ecf20Sopenharmony_ci		stm->data->unlink(stm->data, stmf->output.master,
5278c2ecf20Sopenharmony_ci				  stmf->output.channel);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	stm_output_free(stm, &stmf->output);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/*
5328c2ecf20Sopenharmony_ci	 * matches the stm_char_open()'s
5338c2ecf20Sopenharmony_ci	 * class_find_device() + try_module_get()
5348c2ecf20Sopenharmony_ci	 */
5358c2ecf20Sopenharmony_ci	stm_put_device(stm);
5368c2ecf20Sopenharmony_ci	kfree(stmf);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	return 0;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic int
5428c2ecf20Sopenharmony_cistm_assign_first_policy(struct stm_device *stm, struct stm_output *output,
5438c2ecf20Sopenharmony_ci			char **ids, unsigned int width)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct stp_policy_node *pn;
5468c2ecf20Sopenharmony_ci	int err, n;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/*
5498c2ecf20Sopenharmony_ci	 * On success, stp_policy_node_lookup() will return holding the
5508c2ecf20Sopenharmony_ci	 * configfs subsystem mutex, which is then released in
5518c2ecf20Sopenharmony_ci	 * stp_policy_node_put(). This allows the pdrv->output_open() in
5528c2ecf20Sopenharmony_ci	 * stm_output_assign() to serialize against the attribute accessors.
5538c2ecf20Sopenharmony_ci	 */
5548c2ecf20Sopenharmony_ci	for (n = 0, pn = NULL; ids[n] && !pn; n++)
5558c2ecf20Sopenharmony_ci		pn = stp_policy_node_lookup(stm, ids[n]);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	if (!pn)
5588c2ecf20Sopenharmony_ci		return -EINVAL;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	err = stm_output_assign(stm, width, pn, output);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	stp_policy_node_put(pn);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	return err;
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci/**
5688c2ecf20Sopenharmony_ci * stm_data_write() - send the given payload as data packets
5698c2ecf20Sopenharmony_ci * @data:	stm driver's data
5708c2ecf20Sopenharmony_ci * @m:		STP master
5718c2ecf20Sopenharmony_ci * @c:		STP channel
5728c2ecf20Sopenharmony_ci * @ts_first:	timestamp the first packet
5738c2ecf20Sopenharmony_ci * @buf:	data payload buffer
5748c2ecf20Sopenharmony_ci * @count:	data payload size
5758c2ecf20Sopenharmony_ci */
5768c2ecf20Sopenharmony_cissize_t notrace stm_data_write(struct stm_data *data, unsigned int m,
5778c2ecf20Sopenharmony_ci			       unsigned int c, bool ts_first, const void *buf,
5788c2ecf20Sopenharmony_ci			       size_t count)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	unsigned int flags = ts_first ? STP_PACKET_TIMESTAMPED : 0;
5818c2ecf20Sopenharmony_ci	ssize_t sz;
5828c2ecf20Sopenharmony_ci	size_t pos;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	for (pos = 0, sz = 0; pos < count; pos += sz) {
5858c2ecf20Sopenharmony_ci		sz = min_t(unsigned int, count - pos, 8);
5868c2ecf20Sopenharmony_ci		sz = data->packet(data, m, c, STP_PACKET_DATA, flags, sz,
5878c2ecf20Sopenharmony_ci				  &((u8 *)buf)[pos]);
5888c2ecf20Sopenharmony_ci		if (sz <= 0)
5898c2ecf20Sopenharmony_ci			break;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		if (ts_first) {
5928c2ecf20Sopenharmony_ci			flags = 0;
5938c2ecf20Sopenharmony_ci			ts_first = false;
5948c2ecf20Sopenharmony_ci		}
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	return sz < 0 ? sz : pos;
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stm_data_write);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cistatic ssize_t notrace
6028c2ecf20Sopenharmony_cistm_write(struct stm_device *stm, struct stm_output *output,
6038c2ecf20Sopenharmony_ci	  unsigned int chan, const char *buf, size_t count)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	int err;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	/* stm->pdrv is serialized against policy_mutex */
6088c2ecf20Sopenharmony_ci	if (!stm->pdrv)
6098c2ecf20Sopenharmony_ci		return -ENODEV;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	err = stm->pdrv->write(stm->data, output, chan, buf, count);
6128c2ecf20Sopenharmony_ci	if (err < 0)
6138c2ecf20Sopenharmony_ci		return err;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	return err;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_cistatic ssize_t stm_char_write(struct file *file, const char __user *buf,
6198c2ecf20Sopenharmony_ci			      size_t count, loff_t *ppos)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	struct stm_file *stmf = file->private_data;
6228c2ecf20Sopenharmony_ci	struct stm_device *stm = stmf->stm;
6238c2ecf20Sopenharmony_ci	char *kbuf;
6248c2ecf20Sopenharmony_ci	int err;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	if (count + 1 > PAGE_SIZE)
6278c2ecf20Sopenharmony_ci		count = PAGE_SIZE - 1;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	/*
6308c2ecf20Sopenharmony_ci	 * If no m/c have been assigned to this writer up to this
6318c2ecf20Sopenharmony_ci	 * point, try to use the task name and "default" policy entries.
6328c2ecf20Sopenharmony_ci	 */
6338c2ecf20Sopenharmony_ci	if (!stmf->output.nr_chans) {
6348c2ecf20Sopenharmony_ci		char comm[sizeof(current->comm)];
6358c2ecf20Sopenharmony_ci		char *ids[] = { comm, "default", NULL };
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci		get_task_comm(comm, current);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		err = stm_assign_first_policy(stmf->stm, &stmf->output, ids, 1);
6408c2ecf20Sopenharmony_ci		/*
6418c2ecf20Sopenharmony_ci		 * EBUSY means that somebody else just assigned this
6428c2ecf20Sopenharmony_ci		 * output, which is just fine for write()
6438c2ecf20Sopenharmony_ci		 */
6448c2ecf20Sopenharmony_ci		if (err)
6458c2ecf20Sopenharmony_ci			return err;
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	kbuf = kmalloc(count + 1, GFP_KERNEL);
6498c2ecf20Sopenharmony_ci	if (!kbuf)
6508c2ecf20Sopenharmony_ci		return -ENOMEM;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	err = copy_from_user(kbuf, buf, count);
6538c2ecf20Sopenharmony_ci	if (err) {
6548c2ecf20Sopenharmony_ci		kfree(kbuf);
6558c2ecf20Sopenharmony_ci		return -EFAULT;
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	pm_runtime_get_sync(&stm->dev);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	count = stm_write(stm, &stmf->output, 0, kbuf, count);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(&stm->dev);
6638c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(&stm->dev);
6648c2ecf20Sopenharmony_ci	kfree(kbuf);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	return count;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic void stm_mmap_open(struct vm_area_struct *vma)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct stm_file *stmf = vma->vm_file->private_data;
6728c2ecf20Sopenharmony_ci	struct stm_device *stm = stmf->stm;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	pm_runtime_get(&stm->dev);
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic void stm_mmap_close(struct vm_area_struct *vma)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	struct stm_file *stmf = vma->vm_file->private_data;
6808c2ecf20Sopenharmony_ci	struct stm_device *stm = stmf->stm;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(&stm->dev);
6838c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(&stm->dev);
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic const struct vm_operations_struct stm_mmap_vmops = {
6878c2ecf20Sopenharmony_ci	.open	= stm_mmap_open,
6888c2ecf20Sopenharmony_ci	.close	= stm_mmap_close,
6898c2ecf20Sopenharmony_ci};
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic int stm_char_mmap(struct file *file, struct vm_area_struct *vma)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	struct stm_file *stmf = file->private_data;
6948c2ecf20Sopenharmony_ci	struct stm_device *stm = stmf->stm;
6958c2ecf20Sopenharmony_ci	unsigned long size, phys;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	if (!stm->data->mmio_addr)
6988c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	if (vma->vm_pgoff)
7018c2ecf20Sopenharmony_ci		return -EINVAL;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	size = vma->vm_end - vma->vm_start;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (stmf->output.nr_chans * stm->data->sw_mmiosz != size)
7068c2ecf20Sopenharmony_ci		return -EINVAL;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	phys = stm->data->mmio_addr(stm->data, stmf->output.master,
7098c2ecf20Sopenharmony_ci				    stmf->output.channel,
7108c2ecf20Sopenharmony_ci				    stmf->output.nr_chans);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	if (!phys)
7138c2ecf20Sopenharmony_ci		return -EINVAL;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	pm_runtime_get_sync(&stm->dev);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
7188c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
7198c2ecf20Sopenharmony_ci	vma->vm_ops = &stm_mmap_vmops;
7208c2ecf20Sopenharmony_ci	vm_iomap_memory(vma, phys, size);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	return 0;
7238c2ecf20Sopenharmony_ci}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_cistatic int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg)
7268c2ecf20Sopenharmony_ci{
7278c2ecf20Sopenharmony_ci	struct stm_device *stm = stmf->stm;
7288c2ecf20Sopenharmony_ci	struct stp_policy_id *id;
7298c2ecf20Sopenharmony_ci	char *ids[] = { NULL, NULL };
7308c2ecf20Sopenharmony_ci	int ret = -EINVAL, wlimit = 1;
7318c2ecf20Sopenharmony_ci	u32 size;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	if (stmf->output.nr_chans)
7348c2ecf20Sopenharmony_ci		return -EBUSY;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	if (copy_from_user(&size, arg, sizeof(size)))
7378c2ecf20Sopenharmony_ci		return -EFAULT;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	if (size < sizeof(*id) || size >= PATH_MAX + sizeof(*id))
7408c2ecf20Sopenharmony_ci		return -EINVAL;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	/*
7438c2ecf20Sopenharmony_ci	 * size + 1 to make sure the .id string at the bottom is terminated,
7448c2ecf20Sopenharmony_ci	 * which is also why memdup_user() is not useful here
7458c2ecf20Sopenharmony_ci	 */
7468c2ecf20Sopenharmony_ci	id = kzalloc(size + 1, GFP_KERNEL);
7478c2ecf20Sopenharmony_ci	if (!id)
7488c2ecf20Sopenharmony_ci		return -ENOMEM;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (copy_from_user(id, arg, size)) {
7518c2ecf20Sopenharmony_ci		ret = -EFAULT;
7528c2ecf20Sopenharmony_ci		goto err_free;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (id->__reserved_0 || id->__reserved_1)
7568c2ecf20Sopenharmony_ci		goto err_free;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	if (stm->data->sw_mmiosz)
7598c2ecf20Sopenharmony_ci		wlimit = PAGE_SIZE / stm->data->sw_mmiosz;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	if (id->width < 1 || id->width > wlimit)
7628c2ecf20Sopenharmony_ci		goto err_free;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	ids[0] = id->id;
7658c2ecf20Sopenharmony_ci	ret = stm_assign_first_policy(stmf->stm, &stmf->output, ids,
7668c2ecf20Sopenharmony_ci				      id->width);
7678c2ecf20Sopenharmony_ci	if (ret)
7688c2ecf20Sopenharmony_ci		goto err_free;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	if (stm->data->link)
7718c2ecf20Sopenharmony_ci		ret = stm->data->link(stm->data, stmf->output.master,
7728c2ecf20Sopenharmony_ci				      stmf->output.channel);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	if (ret)
7758c2ecf20Sopenharmony_ci		stm_output_free(stmf->stm, &stmf->output);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_cierr_free:
7788c2ecf20Sopenharmony_ci	kfree(id);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	return ret;
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_cistatic int stm_char_policy_get_ioctl(struct stm_file *stmf, void __user *arg)
7848c2ecf20Sopenharmony_ci{
7858c2ecf20Sopenharmony_ci	struct stp_policy_id id = {
7868c2ecf20Sopenharmony_ci		.size		= sizeof(id),
7878c2ecf20Sopenharmony_ci		.master		= stmf->output.master,
7888c2ecf20Sopenharmony_ci		.channel	= stmf->output.channel,
7898c2ecf20Sopenharmony_ci		.width		= stmf->output.nr_chans,
7908c2ecf20Sopenharmony_ci		.__reserved_0	= 0,
7918c2ecf20Sopenharmony_ci		.__reserved_1	= 0,
7928c2ecf20Sopenharmony_ci	};
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	return copy_to_user(arg, &id, id.size) ? -EFAULT : 0;
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistatic long
7988c2ecf20Sopenharmony_cistm_char_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	struct stm_file *stmf = file->private_data;
8018c2ecf20Sopenharmony_ci	struct stm_data *stm_data = stmf->stm->data;
8028c2ecf20Sopenharmony_ci	int err = -ENOTTY;
8038c2ecf20Sopenharmony_ci	u64 options;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	switch (cmd) {
8068c2ecf20Sopenharmony_ci	case STP_POLICY_ID_SET:
8078c2ecf20Sopenharmony_ci		err = stm_char_policy_set_ioctl(stmf, (void __user *)arg);
8088c2ecf20Sopenharmony_ci		if (err)
8098c2ecf20Sopenharmony_ci			return err;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci		return stm_char_policy_get_ioctl(stmf, (void __user *)arg);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	case STP_POLICY_ID_GET:
8148c2ecf20Sopenharmony_ci		return stm_char_policy_get_ioctl(stmf, (void __user *)arg);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	case STP_SET_OPTIONS:
8178c2ecf20Sopenharmony_ci		if (copy_from_user(&options, (u64 __user *)arg, sizeof(u64)))
8188c2ecf20Sopenharmony_ci			return -EFAULT;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci		if (stm_data->set_options)
8218c2ecf20Sopenharmony_ci			err = stm_data->set_options(stm_data,
8228c2ecf20Sopenharmony_ci						    stmf->output.master,
8238c2ecf20Sopenharmony_ci						    stmf->output.channel,
8248c2ecf20Sopenharmony_ci						    stmf->output.nr_chans,
8258c2ecf20Sopenharmony_ci						    options);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		break;
8288c2ecf20Sopenharmony_ci	default:
8298c2ecf20Sopenharmony_ci		break;
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	return err;
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic const struct file_operations stm_fops = {
8368c2ecf20Sopenharmony_ci	.open		= stm_char_open,
8378c2ecf20Sopenharmony_ci	.release	= stm_char_release,
8388c2ecf20Sopenharmony_ci	.write		= stm_char_write,
8398c2ecf20Sopenharmony_ci	.mmap		= stm_char_mmap,
8408c2ecf20Sopenharmony_ci	.unlocked_ioctl	= stm_char_ioctl,
8418c2ecf20Sopenharmony_ci	.compat_ioctl	= compat_ptr_ioctl,
8428c2ecf20Sopenharmony_ci	.llseek		= no_llseek,
8438c2ecf20Sopenharmony_ci};
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_cistatic void stm_device_release(struct device *dev)
8468c2ecf20Sopenharmony_ci{
8478c2ecf20Sopenharmony_ci	struct stm_device *stm = to_stm_device(dev);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	vfree(stm);
8508c2ecf20Sopenharmony_ci}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ciint stm_register_device(struct device *parent, struct stm_data *stm_data,
8538c2ecf20Sopenharmony_ci			struct module *owner)
8548c2ecf20Sopenharmony_ci{
8558c2ecf20Sopenharmony_ci	struct stm_device *stm;
8568c2ecf20Sopenharmony_ci	unsigned int nmasters;
8578c2ecf20Sopenharmony_ci	int err = -ENOMEM;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	if (!stm_core_up)
8608c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	if (!stm_data->packet || !stm_data->sw_nchannels)
8638c2ecf20Sopenharmony_ci		return -EINVAL;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	nmasters = stm_data->sw_end - stm_data->sw_start + 1;
8668c2ecf20Sopenharmony_ci	stm = vzalloc(sizeof(*stm) + nmasters * sizeof(void *));
8678c2ecf20Sopenharmony_ci	if (!stm)
8688c2ecf20Sopenharmony_ci		return -ENOMEM;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	stm->major = register_chrdev(0, stm_data->name, &stm_fops);
8718c2ecf20Sopenharmony_ci	if (stm->major < 0)
8728c2ecf20Sopenharmony_ci		goto err_free;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	device_initialize(&stm->dev);
8758c2ecf20Sopenharmony_ci	stm->dev.devt = MKDEV(stm->major, 0);
8768c2ecf20Sopenharmony_ci	stm->dev.class = &stm_class;
8778c2ecf20Sopenharmony_ci	stm->dev.parent = parent;
8788c2ecf20Sopenharmony_ci	stm->dev.release = stm_device_release;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	mutex_init(&stm->link_mutex);
8818c2ecf20Sopenharmony_ci	spin_lock_init(&stm->link_lock);
8828c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&stm->link_list);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	/* initialize the object before it is accessible via sysfs */
8858c2ecf20Sopenharmony_ci	spin_lock_init(&stm->mc_lock);
8868c2ecf20Sopenharmony_ci	mutex_init(&stm->policy_mutex);
8878c2ecf20Sopenharmony_ci	stm->sw_nmasters = nmasters;
8888c2ecf20Sopenharmony_ci	stm->owner = owner;
8898c2ecf20Sopenharmony_ci	stm->data = stm_data;
8908c2ecf20Sopenharmony_ci	stm_data->stm = stm;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	err = kobject_set_name(&stm->dev.kobj, "%s", stm_data->name);
8938c2ecf20Sopenharmony_ci	if (err)
8948c2ecf20Sopenharmony_ci		goto err_device;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	err = device_add(&stm->dev);
8978c2ecf20Sopenharmony_ci	if (err)
8988c2ecf20Sopenharmony_ci		goto err_device;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	/*
9018c2ecf20Sopenharmony_ci	 * Use delayed autosuspend to avoid bouncing back and forth
9028c2ecf20Sopenharmony_ci	 * on recurring character device writes, with the initial
9038c2ecf20Sopenharmony_ci	 * delay time of 2 seconds.
9048c2ecf20Sopenharmony_ci	 */
9058c2ecf20Sopenharmony_ci	pm_runtime_no_callbacks(&stm->dev);
9068c2ecf20Sopenharmony_ci	pm_runtime_use_autosuspend(&stm->dev);
9078c2ecf20Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&stm->dev, 2000);
9088c2ecf20Sopenharmony_ci	pm_runtime_set_suspended(&stm->dev);
9098c2ecf20Sopenharmony_ci	pm_runtime_enable(&stm->dev);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	return 0;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_cierr_device:
9148c2ecf20Sopenharmony_ci	unregister_chrdev(stm->major, stm_data->name);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	/* matches device_initialize() above */
9178c2ecf20Sopenharmony_ci	put_device(&stm->dev);
9188c2ecf20Sopenharmony_cierr_free:
9198c2ecf20Sopenharmony_ci	vfree(stm);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	return err;
9228c2ecf20Sopenharmony_ci}
9238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stm_register_device);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_cistatic int __stm_source_link_drop(struct stm_source_device *src,
9268c2ecf20Sopenharmony_ci				  struct stm_device *stm);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_civoid stm_unregister_device(struct stm_data *stm_data)
9298c2ecf20Sopenharmony_ci{
9308c2ecf20Sopenharmony_ci	struct stm_device *stm = stm_data->stm;
9318c2ecf20Sopenharmony_ci	struct stm_source_device *src, *iter;
9328c2ecf20Sopenharmony_ci	int i, ret;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	pm_runtime_dont_use_autosuspend(&stm->dev);
9358c2ecf20Sopenharmony_ci	pm_runtime_disable(&stm->dev);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	mutex_lock(&stm->link_mutex);
9388c2ecf20Sopenharmony_ci	list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) {
9398c2ecf20Sopenharmony_ci		ret = __stm_source_link_drop(src, stm);
9408c2ecf20Sopenharmony_ci		/*
9418c2ecf20Sopenharmony_ci		 * src <-> stm link must not change under the same
9428c2ecf20Sopenharmony_ci		 * stm::link_mutex, so complain loudly if it has;
9438c2ecf20Sopenharmony_ci		 * also in this situation ret!=0 means this src is
9448c2ecf20Sopenharmony_ci		 * not connected to this stm and it should be otherwise
9458c2ecf20Sopenharmony_ci		 * safe to proceed with the tear-down of stm.
9468c2ecf20Sopenharmony_ci		 */
9478c2ecf20Sopenharmony_ci		WARN_ON_ONCE(ret);
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci	mutex_unlock(&stm->link_mutex);
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	synchronize_srcu(&stm_source_srcu);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	unregister_chrdev(stm->major, stm_data->name);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	mutex_lock(&stm->policy_mutex);
9568c2ecf20Sopenharmony_ci	if (stm->policy)
9578c2ecf20Sopenharmony_ci		stp_policy_unbind(stm->policy);
9588c2ecf20Sopenharmony_ci	mutex_unlock(&stm->policy_mutex);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	for (i = stm->data->sw_start; i <= stm->data->sw_end; i++)
9618c2ecf20Sopenharmony_ci		stp_master_free(stm, i);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	device_unregister(&stm->dev);
9648c2ecf20Sopenharmony_ci	stm_data->stm = NULL;
9658c2ecf20Sopenharmony_ci}
9668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stm_unregister_device);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci/*
9698c2ecf20Sopenharmony_ci * stm::link_list access serialization uses a spinlock and a mutex; holding
9708c2ecf20Sopenharmony_ci * either of them guarantees that the list is stable; modification requires
9718c2ecf20Sopenharmony_ci * holding both of them.
9728c2ecf20Sopenharmony_ci *
9738c2ecf20Sopenharmony_ci * Lock ordering is as follows:
9748c2ecf20Sopenharmony_ci *   stm::link_mutex
9758c2ecf20Sopenharmony_ci *     stm::link_lock
9768c2ecf20Sopenharmony_ci *       src::link_lock
9778c2ecf20Sopenharmony_ci */
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci/**
9808c2ecf20Sopenharmony_ci * stm_source_link_add() - connect an stm_source device to an stm device
9818c2ecf20Sopenharmony_ci * @src:	stm_source device
9828c2ecf20Sopenharmony_ci * @stm:	stm device
9838c2ecf20Sopenharmony_ci *
9848c2ecf20Sopenharmony_ci * This function establishes a link from stm_source to an stm device so that
9858c2ecf20Sopenharmony_ci * the former can send out trace data to the latter.
9868c2ecf20Sopenharmony_ci *
9878c2ecf20Sopenharmony_ci * Return:	0 on success, -errno otherwise.
9888c2ecf20Sopenharmony_ci */
9898c2ecf20Sopenharmony_cistatic int stm_source_link_add(struct stm_source_device *src,
9908c2ecf20Sopenharmony_ci			       struct stm_device *stm)
9918c2ecf20Sopenharmony_ci{
9928c2ecf20Sopenharmony_ci	char *ids[] = { NULL, "default", NULL };
9938c2ecf20Sopenharmony_ci	int err = -ENOMEM;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	mutex_lock(&stm->link_mutex);
9968c2ecf20Sopenharmony_ci	spin_lock(&stm->link_lock);
9978c2ecf20Sopenharmony_ci	spin_lock(&src->link_lock);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	/* src->link is dereferenced under stm_source_srcu but not the list */
10008c2ecf20Sopenharmony_ci	rcu_assign_pointer(src->link, stm);
10018c2ecf20Sopenharmony_ci	list_add_tail(&src->link_entry, &stm->link_list);
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	spin_unlock(&src->link_lock);
10048c2ecf20Sopenharmony_ci	spin_unlock(&stm->link_lock);
10058c2ecf20Sopenharmony_ci	mutex_unlock(&stm->link_mutex);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	ids[0] = kstrdup(src->data->name, GFP_KERNEL);
10088c2ecf20Sopenharmony_ci	if (!ids[0])
10098c2ecf20Sopenharmony_ci		goto fail_detach;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	err = stm_assign_first_policy(stm, &src->output, ids,
10128c2ecf20Sopenharmony_ci				      src->data->nr_chans);
10138c2ecf20Sopenharmony_ci	kfree(ids[0]);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	if (err)
10168c2ecf20Sopenharmony_ci		goto fail_detach;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	/* this is to notify the STM device that a new link has been made */
10198c2ecf20Sopenharmony_ci	if (stm->data->link)
10208c2ecf20Sopenharmony_ci		err = stm->data->link(stm->data, src->output.master,
10218c2ecf20Sopenharmony_ci				      src->output.channel);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	if (err)
10248c2ecf20Sopenharmony_ci		goto fail_free_output;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	/* this is to let the source carry out all necessary preparations */
10278c2ecf20Sopenharmony_ci	if (src->data->link)
10288c2ecf20Sopenharmony_ci		src->data->link(src->data);
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	return 0;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_cifail_free_output:
10338c2ecf20Sopenharmony_ci	stm_output_free(stm, &src->output);
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_cifail_detach:
10368c2ecf20Sopenharmony_ci	mutex_lock(&stm->link_mutex);
10378c2ecf20Sopenharmony_ci	spin_lock(&stm->link_lock);
10388c2ecf20Sopenharmony_ci	spin_lock(&src->link_lock);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	rcu_assign_pointer(src->link, NULL);
10418c2ecf20Sopenharmony_ci	list_del_init(&src->link_entry);
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	spin_unlock(&src->link_lock);
10448c2ecf20Sopenharmony_ci	spin_unlock(&stm->link_lock);
10458c2ecf20Sopenharmony_ci	mutex_unlock(&stm->link_mutex);
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	return err;
10488c2ecf20Sopenharmony_ci}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci/**
10518c2ecf20Sopenharmony_ci * __stm_source_link_drop() - detach stm_source from an stm device
10528c2ecf20Sopenharmony_ci * @src:	stm_source device
10538c2ecf20Sopenharmony_ci * @stm:	stm device
10548c2ecf20Sopenharmony_ci *
10558c2ecf20Sopenharmony_ci * If @stm is @src::link, disconnect them from one another and put the
10568c2ecf20Sopenharmony_ci * reference on the @stm device.
10578c2ecf20Sopenharmony_ci *
10588c2ecf20Sopenharmony_ci * Caller must hold stm::link_mutex.
10598c2ecf20Sopenharmony_ci */
10608c2ecf20Sopenharmony_cistatic int __stm_source_link_drop(struct stm_source_device *src,
10618c2ecf20Sopenharmony_ci				  struct stm_device *stm)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	struct stm_device *link;
10648c2ecf20Sopenharmony_ci	int ret = 0;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	lockdep_assert_held(&stm->link_mutex);
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	/* for stm::link_list modification, we hold both mutex and spinlock */
10698c2ecf20Sopenharmony_ci	spin_lock(&stm->link_lock);
10708c2ecf20Sopenharmony_ci	spin_lock(&src->link_lock);
10718c2ecf20Sopenharmony_ci	link = srcu_dereference_check(src->link, &stm_source_srcu, 1);
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	/*
10748c2ecf20Sopenharmony_ci	 * The linked device may have changed since we last looked, because
10758c2ecf20Sopenharmony_ci	 * we weren't holding the src::link_lock back then; if this is the
10768c2ecf20Sopenharmony_ci	 * case, tell the caller to retry.
10778c2ecf20Sopenharmony_ci	 */
10788c2ecf20Sopenharmony_ci	if (link != stm) {
10798c2ecf20Sopenharmony_ci		ret = -EAGAIN;
10808c2ecf20Sopenharmony_ci		goto unlock;
10818c2ecf20Sopenharmony_ci	}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	stm_output_free(link, &src->output);
10848c2ecf20Sopenharmony_ci	list_del_init(&src->link_entry);
10858c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(&link->dev);
10868c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(&link->dev);
10878c2ecf20Sopenharmony_ci	/* matches stm_find_device() from stm_source_link_store() */
10888c2ecf20Sopenharmony_ci	stm_put_device(link);
10898c2ecf20Sopenharmony_ci	rcu_assign_pointer(src->link, NULL);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ciunlock:
10928c2ecf20Sopenharmony_ci	spin_unlock(&src->link_lock);
10938c2ecf20Sopenharmony_ci	spin_unlock(&stm->link_lock);
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	/*
10968c2ecf20Sopenharmony_ci	 * Call the unlink callbacks for both source and stm, when we know
10978c2ecf20Sopenharmony_ci	 * that we have actually performed the unlinking.
10988c2ecf20Sopenharmony_ci	 */
10998c2ecf20Sopenharmony_ci	if (!ret) {
11008c2ecf20Sopenharmony_ci		if (src->data->unlink)
11018c2ecf20Sopenharmony_ci			src->data->unlink(src->data);
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci		if (stm->data->unlink)
11048c2ecf20Sopenharmony_ci			stm->data->unlink(stm->data, src->output.master,
11058c2ecf20Sopenharmony_ci					  src->output.channel);
11068c2ecf20Sopenharmony_ci	}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	return ret;
11098c2ecf20Sopenharmony_ci}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci/**
11128c2ecf20Sopenharmony_ci * stm_source_link_drop() - detach stm_source from its stm device
11138c2ecf20Sopenharmony_ci * @src:	stm_source device
11148c2ecf20Sopenharmony_ci *
11158c2ecf20Sopenharmony_ci * Unlinking means disconnecting from source's STM device; after this
11168c2ecf20Sopenharmony_ci * writes will be unsuccessful until it is linked to a new STM device.
11178c2ecf20Sopenharmony_ci *
11188c2ecf20Sopenharmony_ci * This will happen on "stm_source_link" sysfs attribute write to undo
11198c2ecf20Sopenharmony_ci * the existing link (if any), or on linked STM device's de-registration.
11208c2ecf20Sopenharmony_ci */
11218c2ecf20Sopenharmony_cistatic void stm_source_link_drop(struct stm_source_device *src)
11228c2ecf20Sopenharmony_ci{
11238c2ecf20Sopenharmony_ci	struct stm_device *stm;
11248c2ecf20Sopenharmony_ci	int idx, ret;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ciretry:
11278c2ecf20Sopenharmony_ci	idx = srcu_read_lock(&stm_source_srcu);
11288c2ecf20Sopenharmony_ci	/*
11298c2ecf20Sopenharmony_ci	 * The stm device will be valid for the duration of this
11308c2ecf20Sopenharmony_ci	 * read section, but the link may change before we grab
11318c2ecf20Sopenharmony_ci	 * the src::link_lock in __stm_source_link_drop().
11328c2ecf20Sopenharmony_ci	 */
11338c2ecf20Sopenharmony_ci	stm = srcu_dereference(src->link, &stm_source_srcu);
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	ret = 0;
11368c2ecf20Sopenharmony_ci	if (stm) {
11378c2ecf20Sopenharmony_ci		mutex_lock(&stm->link_mutex);
11388c2ecf20Sopenharmony_ci		ret = __stm_source_link_drop(src, stm);
11398c2ecf20Sopenharmony_ci		mutex_unlock(&stm->link_mutex);
11408c2ecf20Sopenharmony_ci	}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	srcu_read_unlock(&stm_source_srcu, idx);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	/* if it did change, retry */
11458c2ecf20Sopenharmony_ci	if (ret == -EAGAIN)
11468c2ecf20Sopenharmony_ci		goto retry;
11478c2ecf20Sopenharmony_ci}
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_cistatic ssize_t stm_source_link_show(struct device *dev,
11508c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
11518c2ecf20Sopenharmony_ci				    char *buf)
11528c2ecf20Sopenharmony_ci{
11538c2ecf20Sopenharmony_ci	struct stm_source_device *src = to_stm_source_device(dev);
11548c2ecf20Sopenharmony_ci	struct stm_device *stm;
11558c2ecf20Sopenharmony_ci	int idx, ret;
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	idx = srcu_read_lock(&stm_source_srcu);
11588c2ecf20Sopenharmony_ci	stm = srcu_dereference(src->link, &stm_source_srcu);
11598c2ecf20Sopenharmony_ci	ret = sprintf(buf, "%s\n",
11608c2ecf20Sopenharmony_ci		      stm ? dev_name(&stm->dev) : "<none>");
11618c2ecf20Sopenharmony_ci	srcu_read_unlock(&stm_source_srcu, idx);
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	return ret;
11648c2ecf20Sopenharmony_ci}
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_cistatic ssize_t stm_source_link_store(struct device *dev,
11678c2ecf20Sopenharmony_ci				     struct device_attribute *attr,
11688c2ecf20Sopenharmony_ci				     const char *buf, size_t count)
11698c2ecf20Sopenharmony_ci{
11708c2ecf20Sopenharmony_ci	struct stm_source_device *src = to_stm_source_device(dev);
11718c2ecf20Sopenharmony_ci	struct stm_device *link;
11728c2ecf20Sopenharmony_ci	int err;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	stm_source_link_drop(src);
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	link = stm_find_device(buf);
11778c2ecf20Sopenharmony_ci	if (!link)
11788c2ecf20Sopenharmony_ci		return -EINVAL;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	pm_runtime_get(&link->dev);
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	err = stm_source_link_add(src, link);
11838c2ecf20Sopenharmony_ci	if (err) {
11848c2ecf20Sopenharmony_ci		pm_runtime_put_autosuspend(&link->dev);
11858c2ecf20Sopenharmony_ci		/* matches the stm_find_device() above */
11868c2ecf20Sopenharmony_ci		stm_put_device(link);
11878c2ecf20Sopenharmony_ci	}
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	return err ? : count;
11908c2ecf20Sopenharmony_ci}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(stm_source_link);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_cistatic struct attribute *stm_source_attrs[] = {
11958c2ecf20Sopenharmony_ci	&dev_attr_stm_source_link.attr,
11968c2ecf20Sopenharmony_ci	NULL,
11978c2ecf20Sopenharmony_ci};
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(stm_source);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_cistatic struct class stm_source_class = {
12028c2ecf20Sopenharmony_ci	.name		= "stm_source",
12038c2ecf20Sopenharmony_ci	.dev_groups	= stm_source_groups,
12048c2ecf20Sopenharmony_ci};
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_cistatic void stm_source_device_release(struct device *dev)
12078c2ecf20Sopenharmony_ci{
12088c2ecf20Sopenharmony_ci	struct stm_source_device *src = to_stm_source_device(dev);
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	kfree(src);
12118c2ecf20Sopenharmony_ci}
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci/**
12148c2ecf20Sopenharmony_ci * stm_source_register_device() - register an stm_source device
12158c2ecf20Sopenharmony_ci * @parent:	parent device
12168c2ecf20Sopenharmony_ci * @data:	device description structure
12178c2ecf20Sopenharmony_ci *
12188c2ecf20Sopenharmony_ci * This will create a device of stm_source class that can write
12198c2ecf20Sopenharmony_ci * data to an stm device once linked.
12208c2ecf20Sopenharmony_ci *
12218c2ecf20Sopenharmony_ci * Return:	0 on success, -errno otherwise.
12228c2ecf20Sopenharmony_ci */
12238c2ecf20Sopenharmony_ciint stm_source_register_device(struct device *parent,
12248c2ecf20Sopenharmony_ci			       struct stm_source_data *data)
12258c2ecf20Sopenharmony_ci{
12268c2ecf20Sopenharmony_ci	struct stm_source_device *src;
12278c2ecf20Sopenharmony_ci	int err;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	if (!stm_core_up)
12308c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	src = kzalloc(sizeof(*src), GFP_KERNEL);
12338c2ecf20Sopenharmony_ci	if (!src)
12348c2ecf20Sopenharmony_ci		return -ENOMEM;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	device_initialize(&src->dev);
12378c2ecf20Sopenharmony_ci	src->dev.class = &stm_source_class;
12388c2ecf20Sopenharmony_ci	src->dev.parent = parent;
12398c2ecf20Sopenharmony_ci	src->dev.release = stm_source_device_release;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	err = kobject_set_name(&src->dev.kobj, "%s", data->name);
12428c2ecf20Sopenharmony_ci	if (err)
12438c2ecf20Sopenharmony_ci		goto err;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	pm_runtime_no_callbacks(&src->dev);
12468c2ecf20Sopenharmony_ci	pm_runtime_forbid(&src->dev);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	err = device_add(&src->dev);
12498c2ecf20Sopenharmony_ci	if (err)
12508c2ecf20Sopenharmony_ci		goto err;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	stm_output_init(&src->output);
12538c2ecf20Sopenharmony_ci	spin_lock_init(&src->link_lock);
12548c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&src->link_entry);
12558c2ecf20Sopenharmony_ci	src->data = data;
12568c2ecf20Sopenharmony_ci	data->src = src;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	return 0;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_cierr:
12618c2ecf20Sopenharmony_ci	put_device(&src->dev);
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	return err;
12648c2ecf20Sopenharmony_ci}
12658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stm_source_register_device);
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci/**
12688c2ecf20Sopenharmony_ci * stm_source_unregister_device() - unregister an stm_source device
12698c2ecf20Sopenharmony_ci * @data:	device description that was used to register the device
12708c2ecf20Sopenharmony_ci *
12718c2ecf20Sopenharmony_ci * This will remove a previously created stm_source device from the system.
12728c2ecf20Sopenharmony_ci */
12738c2ecf20Sopenharmony_civoid stm_source_unregister_device(struct stm_source_data *data)
12748c2ecf20Sopenharmony_ci{
12758c2ecf20Sopenharmony_ci	struct stm_source_device *src = data->src;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	stm_source_link_drop(src);
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	device_unregister(&src->dev);
12808c2ecf20Sopenharmony_ci}
12818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stm_source_unregister_device);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ciint notrace stm_source_write(struct stm_source_data *data,
12848c2ecf20Sopenharmony_ci			     unsigned int chan,
12858c2ecf20Sopenharmony_ci			     const char *buf, size_t count)
12868c2ecf20Sopenharmony_ci{
12878c2ecf20Sopenharmony_ci	struct stm_source_device *src = data->src;
12888c2ecf20Sopenharmony_ci	struct stm_device *stm;
12898c2ecf20Sopenharmony_ci	int idx;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	if (!src->output.nr_chans)
12928c2ecf20Sopenharmony_ci		return -ENODEV;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	if (chan >= src->output.nr_chans)
12958c2ecf20Sopenharmony_ci		return -EINVAL;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	idx = srcu_read_lock(&stm_source_srcu);
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	stm = srcu_dereference(src->link, &stm_source_srcu);
13008c2ecf20Sopenharmony_ci	if (stm)
13018c2ecf20Sopenharmony_ci		count = stm_write(stm, &src->output, chan, buf, count);
13028c2ecf20Sopenharmony_ci	else
13038c2ecf20Sopenharmony_ci		count = -ENODEV;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	srcu_read_unlock(&stm_source_srcu, idx);
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	return count;
13088c2ecf20Sopenharmony_ci}
13098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stm_source_write);
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_cistatic int __init stm_core_init(void)
13128c2ecf20Sopenharmony_ci{
13138c2ecf20Sopenharmony_ci	int err;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	err = class_register(&stm_class);
13168c2ecf20Sopenharmony_ci	if (err)
13178c2ecf20Sopenharmony_ci		return err;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	err = class_register(&stm_source_class);
13208c2ecf20Sopenharmony_ci	if (err)
13218c2ecf20Sopenharmony_ci		goto err_stm;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	err = stp_configfs_init();
13248c2ecf20Sopenharmony_ci	if (err)
13258c2ecf20Sopenharmony_ci		goto err_src;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	init_srcu_struct(&stm_source_srcu);
13288c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&stm_pdrv_head);
13298c2ecf20Sopenharmony_ci	mutex_init(&stm_pdrv_mutex);
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	/*
13328c2ecf20Sopenharmony_ci	 * So as to not confuse existing users with a requirement
13338c2ecf20Sopenharmony_ci	 * to load yet another module, do it here.
13348c2ecf20Sopenharmony_ci	 */
13358c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_STM_PROTO_BASIC))
13368c2ecf20Sopenharmony_ci		(void)request_module_nowait("stm_p_basic");
13378c2ecf20Sopenharmony_ci	stm_core_up++;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	return 0;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_cierr_src:
13428c2ecf20Sopenharmony_ci	class_unregister(&stm_source_class);
13438c2ecf20Sopenharmony_cierr_stm:
13448c2ecf20Sopenharmony_ci	class_unregister(&stm_class);
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	return err;
13478c2ecf20Sopenharmony_ci}
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_cimodule_init(stm_core_init);
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_cistatic void __exit stm_core_exit(void)
13528c2ecf20Sopenharmony_ci{
13538c2ecf20Sopenharmony_ci	cleanup_srcu_struct(&stm_source_srcu);
13548c2ecf20Sopenharmony_ci	class_unregister(&stm_source_class);
13558c2ecf20Sopenharmony_ci	class_unregister(&stm_class);
13568c2ecf20Sopenharmony_ci	stp_configfs_exit();
13578c2ecf20Sopenharmony_ci}
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_cimodule_exit(stm_core_exit);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
13628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("System Trace Module device class");
13638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
1364