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