19ea393d8SAlexander Shishkin // SPDX-License-Identifier: GPL-2.0 27bd1d409SAlexander Shishkin /* 37bd1d409SAlexander Shishkin * System Trace Module (STM) infrastructure 47bd1d409SAlexander Shishkin * Copyright (c) 2014, Intel Corporation. 57bd1d409SAlexander Shishkin * 67bd1d409SAlexander Shishkin * STM class implements generic infrastructure for System Trace Module devices 77bd1d409SAlexander Shishkin * as defined in MIPI STPv2 specification. 87bd1d409SAlexander Shishkin */ 97bd1d409SAlexander Shishkin 108e0469a4SAlexander Shishkin #include <linux/pm_runtime.h> 117bd1d409SAlexander Shishkin #include <linux/uaccess.h> 127bd1d409SAlexander Shishkin #include <linux/kernel.h> 137bd1d409SAlexander Shishkin #include <linux/module.h> 147bd1d409SAlexander Shishkin #include <linux/device.h> 157bd1d409SAlexander Shishkin #include <linux/compat.h> 167bd1d409SAlexander Shishkin #include <linux/kdev_t.h> 177bd1d409SAlexander Shishkin #include <linux/srcu.h> 187bd1d409SAlexander Shishkin #include <linux/slab.h> 197bd1d409SAlexander Shishkin #include <linux/stm.h> 207bd1d409SAlexander Shishkin #include <linux/fs.h> 217bd1d409SAlexander Shishkin #include <linux/mm.h> 22806e3087SGreg Kroah-Hartman #include <linux/vmalloc.h> 237bd1d409SAlexander Shishkin #include "stm.h" 247bd1d409SAlexander Shishkin 257bd1d409SAlexander Shishkin #include <uapi/linux/stm.h> 267bd1d409SAlexander Shishkin 277bd1d409SAlexander Shishkin static unsigned int stm_core_up; 287bd1d409SAlexander Shishkin 297bd1d409SAlexander Shishkin /* 307bd1d409SAlexander Shishkin * The SRCU here makes sure that STM device doesn't disappear from under a 317bd1d409SAlexander Shishkin * stm_source_write() caller, which may want to have as little overhead as 327bd1d409SAlexander Shishkin * possible. 337bd1d409SAlexander Shishkin */ 347bd1d409SAlexander Shishkin static struct srcu_struct stm_source_srcu; 357bd1d409SAlexander Shishkin 367bd1d409SAlexander Shishkin static ssize_t masters_show(struct device *dev, 377bd1d409SAlexander Shishkin struct device_attribute *attr, 387bd1d409SAlexander Shishkin char *buf) 397bd1d409SAlexander Shishkin { 407bd1d409SAlexander Shishkin struct stm_device *stm = to_stm_device(dev); 417bd1d409SAlexander Shishkin int ret; 427bd1d409SAlexander Shishkin 437bd1d409SAlexander Shishkin ret = sprintf(buf, "%u %u\n", stm->data->sw_start, stm->data->sw_end); 447bd1d409SAlexander Shishkin 457bd1d409SAlexander Shishkin return ret; 467bd1d409SAlexander Shishkin } 477bd1d409SAlexander Shishkin 487bd1d409SAlexander Shishkin static DEVICE_ATTR_RO(masters); 497bd1d409SAlexander Shishkin 507bd1d409SAlexander Shishkin static ssize_t channels_show(struct device *dev, 517bd1d409SAlexander Shishkin struct device_attribute *attr, 527bd1d409SAlexander Shishkin char *buf) 537bd1d409SAlexander Shishkin { 547bd1d409SAlexander Shishkin struct stm_device *stm = to_stm_device(dev); 557bd1d409SAlexander Shishkin int ret; 567bd1d409SAlexander Shishkin 577bd1d409SAlexander Shishkin ret = sprintf(buf, "%u\n", stm->data->sw_nchannels); 587bd1d409SAlexander Shishkin 597bd1d409SAlexander Shishkin return ret; 607bd1d409SAlexander Shishkin } 617bd1d409SAlexander Shishkin 627bd1d409SAlexander Shishkin static DEVICE_ATTR_RO(channels); 637bd1d409SAlexander Shishkin 648e996a28SAlexander Shishkin static ssize_t hw_override_show(struct device *dev, 658e996a28SAlexander Shishkin struct device_attribute *attr, 668e996a28SAlexander Shishkin char *buf) 678e996a28SAlexander Shishkin { 688e996a28SAlexander Shishkin struct stm_device *stm = to_stm_device(dev); 698e996a28SAlexander Shishkin int ret; 708e996a28SAlexander Shishkin 718e996a28SAlexander Shishkin ret = sprintf(buf, "%u\n", stm->data->hw_override); 728e996a28SAlexander Shishkin 738e996a28SAlexander Shishkin return ret; 748e996a28SAlexander Shishkin } 758e996a28SAlexander Shishkin 768e996a28SAlexander Shishkin static DEVICE_ATTR_RO(hw_override); 778e996a28SAlexander Shishkin 787bd1d409SAlexander Shishkin static struct attribute *stm_attrs[] = { 797bd1d409SAlexander Shishkin &dev_attr_masters.attr, 807bd1d409SAlexander Shishkin &dev_attr_channels.attr, 818e996a28SAlexander Shishkin &dev_attr_hw_override.attr, 827bd1d409SAlexander Shishkin NULL, 837bd1d409SAlexander Shishkin }; 847bd1d409SAlexander Shishkin 857bd1d409SAlexander Shishkin ATTRIBUTE_GROUPS(stm); 867bd1d409SAlexander Shishkin 877bd1d409SAlexander Shishkin static struct class stm_class = { 887bd1d409SAlexander Shishkin .name = "stm", 897bd1d409SAlexander Shishkin .dev_groups = stm_groups, 907bd1d409SAlexander Shishkin }; 917bd1d409SAlexander Shishkin 927bd1d409SAlexander Shishkin static int stm_dev_match(struct device *dev, const void *data) 937bd1d409SAlexander Shishkin { 947bd1d409SAlexander Shishkin const char *name = data; 957bd1d409SAlexander Shishkin 967bd1d409SAlexander Shishkin return sysfs_streq(name, dev_name(dev)); 977bd1d409SAlexander Shishkin } 987bd1d409SAlexander Shishkin 997bd1d409SAlexander Shishkin /** 1007bd1d409SAlexander Shishkin * stm_find_device() - find stm device by name 1017bd1d409SAlexander Shishkin * @buf: character buffer containing the name 1027bd1d409SAlexander Shishkin * 1037bd1d409SAlexander Shishkin * This is called when either policy gets assigned to an stm device or an 1047bd1d409SAlexander Shishkin * stm_source device gets linked to an stm device. 1057bd1d409SAlexander Shishkin * 1067bd1d409SAlexander Shishkin * This grabs device's reference (get_device()) and module reference, both 1077bd1d409SAlexander Shishkin * of which the calling path needs to make sure to drop with stm_put_device(). 1087bd1d409SAlexander Shishkin * 1097bd1d409SAlexander Shishkin * Return: stm device pointer or null if lookup failed. 1107bd1d409SAlexander Shishkin */ 1117bd1d409SAlexander Shishkin struct stm_device *stm_find_device(const char *buf) 1127bd1d409SAlexander Shishkin { 1137bd1d409SAlexander Shishkin struct stm_device *stm; 1147bd1d409SAlexander Shishkin struct device *dev; 1157bd1d409SAlexander Shishkin 1167bd1d409SAlexander Shishkin if (!stm_core_up) 1177bd1d409SAlexander Shishkin return NULL; 1187bd1d409SAlexander Shishkin 1197bd1d409SAlexander Shishkin dev = class_find_device(&stm_class, NULL, buf, stm_dev_match); 1207bd1d409SAlexander Shishkin if (!dev) 1217bd1d409SAlexander Shishkin return NULL; 1227bd1d409SAlexander Shishkin 1237bd1d409SAlexander Shishkin stm = to_stm_device(dev); 1247bd1d409SAlexander Shishkin if (!try_module_get(stm->owner)) { 125f7c81c71SAlexander Shishkin /* matches class_find_device() above */ 1267bd1d409SAlexander Shishkin put_device(dev); 1277bd1d409SAlexander Shishkin return NULL; 1287bd1d409SAlexander Shishkin } 1297bd1d409SAlexander Shishkin 1307bd1d409SAlexander Shishkin return stm; 1317bd1d409SAlexander Shishkin } 1327bd1d409SAlexander Shishkin 1337bd1d409SAlexander Shishkin /** 1347bd1d409SAlexander Shishkin * stm_put_device() - drop references on the stm device 1357bd1d409SAlexander Shishkin * @stm: stm device, previously acquired by stm_find_device() 1367bd1d409SAlexander Shishkin * 1377bd1d409SAlexander Shishkin * This drops the module reference and device reference taken by 138f7c81c71SAlexander Shishkin * stm_find_device() or stm_char_open(). 1397bd1d409SAlexander Shishkin */ 1407bd1d409SAlexander Shishkin void stm_put_device(struct stm_device *stm) 1417bd1d409SAlexander Shishkin { 1427bd1d409SAlexander Shishkin module_put(stm->owner); 1437bd1d409SAlexander Shishkin put_device(&stm->dev); 1447bd1d409SAlexander Shishkin } 1457bd1d409SAlexander Shishkin 1467bd1d409SAlexander Shishkin /* 1477bd1d409SAlexander Shishkin * Internally we only care about software-writable masters here, that is the 1487bd1d409SAlexander Shishkin * ones in the range [stm_data->sw_start..stm_data..sw_end], however we need 1497bd1d409SAlexander Shishkin * original master numbers to be visible externally, since they are the ones 1507bd1d409SAlexander Shishkin * that will appear in the STP stream. Thus, the internal bookkeeping uses 1517bd1d409SAlexander Shishkin * $master - stm_data->sw_start to reference master descriptors and such. 1527bd1d409SAlexander Shishkin */ 1537bd1d409SAlexander Shishkin 1547bd1d409SAlexander Shishkin #define __stm_master(_s, _m) \ 1557bd1d409SAlexander Shishkin ((_s)->masters[(_m) - (_s)->data->sw_start]) 1567bd1d409SAlexander Shishkin 1577bd1d409SAlexander Shishkin static inline struct stp_master * 1587bd1d409SAlexander Shishkin stm_master(struct stm_device *stm, unsigned int idx) 1597bd1d409SAlexander Shishkin { 1607bd1d409SAlexander Shishkin if (idx < stm->data->sw_start || idx > stm->data->sw_end) 1617bd1d409SAlexander Shishkin return NULL; 1627bd1d409SAlexander Shishkin 1637bd1d409SAlexander Shishkin return __stm_master(stm, idx); 1647bd1d409SAlexander Shishkin } 1657bd1d409SAlexander Shishkin 1667bd1d409SAlexander Shishkin static int stp_master_alloc(struct stm_device *stm, unsigned int idx) 1677bd1d409SAlexander Shishkin { 1687bd1d409SAlexander Shishkin struct stp_master *master; 1697bd1d409SAlexander Shishkin 17051e0f227SAlexander Shishkin master = kzalloc(struct_size(master, chan_map, 17151e0f227SAlexander Shishkin BITS_TO_LONGS(stm->data->sw_nchannels)), 17251e0f227SAlexander Shishkin GFP_ATOMIC); 1737bd1d409SAlexander Shishkin if (!master) 1747bd1d409SAlexander Shishkin return -ENOMEM; 1757bd1d409SAlexander Shishkin 1767bd1d409SAlexander Shishkin master->nr_free = stm->data->sw_nchannels; 1777bd1d409SAlexander Shishkin __stm_master(stm, idx) = master; 1787bd1d409SAlexander Shishkin 1797bd1d409SAlexander Shishkin return 0; 1807bd1d409SAlexander Shishkin } 1817bd1d409SAlexander Shishkin 1827bd1d409SAlexander Shishkin static void stp_master_free(struct stm_device *stm, unsigned int idx) 1837bd1d409SAlexander Shishkin { 1847bd1d409SAlexander Shishkin struct stp_master *master = stm_master(stm, idx); 1857bd1d409SAlexander Shishkin 1867bd1d409SAlexander Shishkin if (!master) 1877bd1d409SAlexander Shishkin return; 1887bd1d409SAlexander Shishkin 1897bd1d409SAlexander Shishkin __stm_master(stm, idx) = NULL; 1907bd1d409SAlexander Shishkin kfree(master); 1917bd1d409SAlexander Shishkin } 1927bd1d409SAlexander Shishkin 1937bd1d409SAlexander Shishkin static void stm_output_claim(struct stm_device *stm, struct stm_output *output) 1947bd1d409SAlexander Shishkin { 1957bd1d409SAlexander Shishkin struct stp_master *master = stm_master(stm, output->master); 1967bd1d409SAlexander Shishkin 197cde4ad83SAlexander Shishkin lockdep_assert_held(&stm->mc_lock); 198cde4ad83SAlexander Shishkin lockdep_assert_held(&output->lock); 199cde4ad83SAlexander Shishkin 2007bd1d409SAlexander Shishkin if (WARN_ON_ONCE(master->nr_free < output->nr_chans)) 2017bd1d409SAlexander Shishkin return; 2027bd1d409SAlexander Shishkin 2037bd1d409SAlexander Shishkin bitmap_allocate_region(&master->chan_map[0], output->channel, 2047bd1d409SAlexander Shishkin ilog2(output->nr_chans)); 2057bd1d409SAlexander Shishkin 2067bd1d409SAlexander Shishkin master->nr_free -= output->nr_chans; 2077bd1d409SAlexander Shishkin } 2087bd1d409SAlexander Shishkin 2097bd1d409SAlexander Shishkin static void 2107bd1d409SAlexander Shishkin stm_output_disclaim(struct stm_device *stm, struct stm_output *output) 2117bd1d409SAlexander Shishkin { 2127bd1d409SAlexander Shishkin struct stp_master *master = stm_master(stm, output->master); 2137bd1d409SAlexander Shishkin 214cde4ad83SAlexander Shishkin lockdep_assert_held(&stm->mc_lock); 215cde4ad83SAlexander Shishkin lockdep_assert_held(&output->lock); 216cde4ad83SAlexander Shishkin 2177bd1d409SAlexander Shishkin bitmap_release_region(&master->chan_map[0], output->channel, 2187bd1d409SAlexander Shishkin ilog2(output->nr_chans)); 2197bd1d409SAlexander Shishkin 2207bd1d409SAlexander Shishkin master->nr_free += output->nr_chans; 221ee496da4STingwei Zhang output->nr_chans = 0; 2227bd1d409SAlexander Shishkin } 2237bd1d409SAlexander Shishkin 2247bd1d409SAlexander Shishkin /* 2257bd1d409SAlexander Shishkin * This is like bitmap_find_free_region(), except it can ignore @start bits 2267bd1d409SAlexander Shishkin * at the beginning. 2277bd1d409SAlexander Shishkin */ 2287bd1d409SAlexander Shishkin static int find_free_channels(unsigned long *bitmap, unsigned int start, 2297bd1d409SAlexander Shishkin unsigned int end, unsigned int width) 2307bd1d409SAlexander Shishkin { 2317bd1d409SAlexander Shishkin unsigned int pos; 2327bd1d409SAlexander Shishkin int i; 2337bd1d409SAlexander Shishkin 2347bd1d409SAlexander Shishkin for (pos = start; pos < end + 1; pos = ALIGN(pos, width)) { 2357bd1d409SAlexander Shishkin pos = find_next_zero_bit(bitmap, end + 1, pos); 2367bd1d409SAlexander Shishkin if (pos + width > end + 1) 2377bd1d409SAlexander Shishkin break; 2387bd1d409SAlexander Shishkin 2397bd1d409SAlexander Shishkin if (pos & (width - 1)) 2407bd1d409SAlexander Shishkin continue; 2417bd1d409SAlexander Shishkin 2427bd1d409SAlexander Shishkin for (i = 1; i < width && !test_bit(pos + i, bitmap); i++) 2437bd1d409SAlexander Shishkin ; 2447bd1d409SAlexander Shishkin if (i == width) 2457bd1d409SAlexander Shishkin return pos; 246a1d75dadSZhi Jin 247a1d75dadSZhi Jin /* step over [pos..pos+i) to continue search */ 248a1d75dadSZhi Jin pos += i; 2497bd1d409SAlexander Shishkin } 2507bd1d409SAlexander Shishkin 2517bd1d409SAlexander Shishkin return -1; 2527bd1d409SAlexander Shishkin } 2537bd1d409SAlexander Shishkin 254f45f40adSLucas Tanure static int 2557bd1d409SAlexander Shishkin stm_find_master_chan(struct stm_device *stm, unsigned int width, 2567bd1d409SAlexander Shishkin unsigned int *mstart, unsigned int mend, 2577bd1d409SAlexander Shishkin unsigned int *cstart, unsigned int cend) 2587bd1d409SAlexander Shishkin { 2597bd1d409SAlexander Shishkin struct stp_master *master; 2607bd1d409SAlexander Shishkin unsigned int midx; 2617bd1d409SAlexander Shishkin int pos, err; 2627bd1d409SAlexander Shishkin 2637bd1d409SAlexander Shishkin for (midx = *mstart; midx <= mend; midx++) { 2647bd1d409SAlexander Shishkin if (!stm_master(stm, midx)) { 2657bd1d409SAlexander Shishkin err = stp_master_alloc(stm, midx); 2667bd1d409SAlexander Shishkin if (err) 2677bd1d409SAlexander Shishkin return err; 2687bd1d409SAlexander Shishkin } 2697bd1d409SAlexander Shishkin 2707bd1d409SAlexander Shishkin master = stm_master(stm, midx); 2717bd1d409SAlexander Shishkin 2727bd1d409SAlexander Shishkin if (!master->nr_free) 2737bd1d409SAlexander Shishkin continue; 2747bd1d409SAlexander Shishkin 2757bd1d409SAlexander Shishkin pos = find_free_channels(master->chan_map, *cstart, cend, 2767bd1d409SAlexander Shishkin width); 2777bd1d409SAlexander Shishkin if (pos < 0) 2787bd1d409SAlexander Shishkin continue; 2797bd1d409SAlexander Shishkin 2807bd1d409SAlexander Shishkin *mstart = midx; 2817bd1d409SAlexander Shishkin *cstart = pos; 2827bd1d409SAlexander Shishkin return 0; 2837bd1d409SAlexander Shishkin } 2847bd1d409SAlexander Shishkin 2857bd1d409SAlexander Shishkin return -ENOSPC; 2867bd1d409SAlexander Shishkin } 2877bd1d409SAlexander Shishkin 2887bd1d409SAlexander Shishkin static int stm_output_assign(struct stm_device *stm, unsigned int width, 2897bd1d409SAlexander Shishkin struct stp_policy_node *policy_node, 2907bd1d409SAlexander Shishkin struct stm_output *output) 2917bd1d409SAlexander Shishkin { 2927bd1d409SAlexander Shishkin unsigned int midx, cidx, mend, cend; 2937bd1d409SAlexander Shishkin int ret = -EINVAL; 2947bd1d409SAlexander Shishkin 2957bd1d409SAlexander Shishkin if (width > stm->data->sw_nchannels) 2967bd1d409SAlexander Shishkin return -EINVAL; 2977bd1d409SAlexander Shishkin 298cb6102bdSAlexander Shishkin /* We no longer accept policy_node==NULL here */ 299cb6102bdSAlexander Shishkin if (WARN_ON_ONCE(!policy_node)) 300cb6102bdSAlexander Shishkin return -EINVAL; 301cb6102bdSAlexander Shishkin 302cb6102bdSAlexander Shishkin /* 303cb6102bdSAlexander Shishkin * Also, the caller holds reference to policy_node, so it won't 304cb6102bdSAlexander Shishkin * disappear on us. 305cb6102bdSAlexander Shishkin */ 306cb6102bdSAlexander Shishkin stp_policy_node_get_ranges(policy_node, &midx, &mend, &cidx, &cend); 3077bd1d409SAlexander Shishkin 3087bd1d409SAlexander Shishkin spin_lock(&stm->mc_lock); 309cde4ad83SAlexander Shishkin spin_lock(&output->lock); 3107bd1d409SAlexander Shishkin /* output is already assigned -- shouldn't happen */ 3117bd1d409SAlexander Shishkin if (WARN_ON_ONCE(output->nr_chans)) 3127bd1d409SAlexander Shishkin goto unlock; 3137bd1d409SAlexander Shishkin 3147bd1d409SAlexander Shishkin ret = stm_find_master_chan(stm, width, &midx, mend, &cidx, cend); 315f45f40adSLucas Tanure if (ret < 0) 3167bd1d409SAlexander Shishkin goto unlock; 3177bd1d409SAlexander Shishkin 3187bd1d409SAlexander Shishkin output->master = midx; 3197bd1d409SAlexander Shishkin output->channel = cidx; 3207bd1d409SAlexander Shishkin output->nr_chans = width; 321c7fd62bcSAlexander Shishkin if (stm->pdrv->output_open) { 322c7fd62bcSAlexander Shishkin void *priv = stp_policy_node_priv(policy_node); 323c7fd62bcSAlexander Shishkin 324c7fd62bcSAlexander Shishkin if (WARN_ON_ONCE(!priv)) 325c7fd62bcSAlexander Shishkin goto unlock; 326c7fd62bcSAlexander Shishkin 327c7fd62bcSAlexander Shishkin /* configfs subsys mutex is held by the caller */ 328c7fd62bcSAlexander Shishkin ret = stm->pdrv->output_open(priv, output); 329c7fd62bcSAlexander Shishkin if (ret) 330c7fd62bcSAlexander Shishkin goto unlock; 331c7fd62bcSAlexander Shishkin } 332c7fd62bcSAlexander Shishkin 3337bd1d409SAlexander Shishkin stm_output_claim(stm, output); 3347bd1d409SAlexander Shishkin dev_dbg(&stm->dev, "assigned %u:%u (+%u)\n", midx, cidx, width); 3357bd1d409SAlexander Shishkin 3367bd1d409SAlexander Shishkin ret = 0; 3377bd1d409SAlexander Shishkin unlock: 338c7fd62bcSAlexander Shishkin if (ret) 339c7fd62bcSAlexander Shishkin output->nr_chans = 0; 340c7fd62bcSAlexander Shishkin 341cde4ad83SAlexander Shishkin spin_unlock(&output->lock); 3427bd1d409SAlexander Shishkin spin_unlock(&stm->mc_lock); 3437bd1d409SAlexander Shishkin 3447bd1d409SAlexander Shishkin return ret; 3457bd1d409SAlexander Shishkin } 3467bd1d409SAlexander Shishkin 3477bd1d409SAlexander Shishkin static void stm_output_free(struct stm_device *stm, struct stm_output *output) 3487bd1d409SAlexander Shishkin { 3497bd1d409SAlexander Shishkin spin_lock(&stm->mc_lock); 350cde4ad83SAlexander Shishkin spin_lock(&output->lock); 3517bd1d409SAlexander Shishkin if (output->nr_chans) 3527bd1d409SAlexander Shishkin stm_output_disclaim(stm, output); 353c7fd62bcSAlexander Shishkin if (stm->pdrv && stm->pdrv->output_close) 354c7fd62bcSAlexander Shishkin stm->pdrv->output_close(output); 355cde4ad83SAlexander Shishkin spin_unlock(&output->lock); 3567bd1d409SAlexander Shishkin spin_unlock(&stm->mc_lock); 3577bd1d409SAlexander Shishkin } 3587bd1d409SAlexander Shishkin 359cde4ad83SAlexander Shishkin static void stm_output_init(struct stm_output *output) 360cde4ad83SAlexander Shishkin { 361cde4ad83SAlexander Shishkin spin_lock_init(&output->lock); 362cde4ad83SAlexander Shishkin } 363cde4ad83SAlexander Shishkin 3647bd1d409SAlexander Shishkin static int major_match(struct device *dev, const void *data) 3657bd1d409SAlexander Shishkin { 3667bd1d409SAlexander Shishkin unsigned int major = *(unsigned int *)data; 3677bd1d409SAlexander Shishkin 3687bd1d409SAlexander Shishkin return MAJOR(dev->devt) == major; 3697bd1d409SAlexander Shishkin } 3707bd1d409SAlexander Shishkin 371c7fd62bcSAlexander Shishkin /* 372c7fd62bcSAlexander Shishkin * Framing protocol management 373c7fd62bcSAlexander Shishkin * Modules can implement STM protocol drivers and (un-)register them 374c7fd62bcSAlexander Shishkin * with the STM class framework. 375c7fd62bcSAlexander Shishkin */ 376c7fd62bcSAlexander Shishkin static struct list_head stm_pdrv_head; 377c7fd62bcSAlexander Shishkin static struct mutex stm_pdrv_mutex; 378c7fd62bcSAlexander Shishkin 379c7fd62bcSAlexander Shishkin struct stm_pdrv_entry { 380c7fd62bcSAlexander Shishkin struct list_head entry; 381c7fd62bcSAlexander Shishkin const struct stm_protocol_driver *pdrv; 382c7fd62bcSAlexander Shishkin const struct config_item_type *node_type; 383c7fd62bcSAlexander Shishkin }; 384c7fd62bcSAlexander Shishkin 385c7fd62bcSAlexander Shishkin static const struct stm_pdrv_entry * 386c7fd62bcSAlexander Shishkin __stm_lookup_protocol(const char *name) 387c7fd62bcSAlexander Shishkin { 388c7fd62bcSAlexander Shishkin struct stm_pdrv_entry *pe; 389c7fd62bcSAlexander Shishkin 390c7fd62bcSAlexander Shishkin /* 391c7fd62bcSAlexander Shishkin * If no name is given (NULL or ""), fall back to "p_basic". 392c7fd62bcSAlexander Shishkin */ 393c7fd62bcSAlexander Shishkin if (!name || !*name) 394c7fd62bcSAlexander Shishkin name = "p_basic"; 395c7fd62bcSAlexander Shishkin 396c7fd62bcSAlexander Shishkin list_for_each_entry(pe, &stm_pdrv_head, entry) { 397c7fd62bcSAlexander Shishkin if (!strcmp(name, pe->pdrv->name)) 398c7fd62bcSAlexander Shishkin return pe; 399c7fd62bcSAlexander Shishkin } 400c7fd62bcSAlexander Shishkin 401c7fd62bcSAlexander Shishkin return NULL; 402c7fd62bcSAlexander Shishkin } 403c7fd62bcSAlexander Shishkin 404c7fd62bcSAlexander Shishkin int stm_register_protocol(const struct stm_protocol_driver *pdrv) 405c7fd62bcSAlexander Shishkin { 406c7fd62bcSAlexander Shishkin struct stm_pdrv_entry *pe = NULL; 407c7fd62bcSAlexander Shishkin int ret = -ENOMEM; 408c7fd62bcSAlexander Shishkin 409c7fd62bcSAlexander Shishkin mutex_lock(&stm_pdrv_mutex); 410c7fd62bcSAlexander Shishkin 411c7fd62bcSAlexander Shishkin if (__stm_lookup_protocol(pdrv->name)) { 412c7fd62bcSAlexander Shishkin ret = -EEXIST; 413c7fd62bcSAlexander Shishkin goto unlock; 414c7fd62bcSAlexander Shishkin } 415c7fd62bcSAlexander Shishkin 416c7fd62bcSAlexander Shishkin pe = kzalloc(sizeof(*pe), GFP_KERNEL); 417c7fd62bcSAlexander Shishkin if (!pe) 418c7fd62bcSAlexander Shishkin goto unlock; 419c7fd62bcSAlexander Shishkin 420c7fd62bcSAlexander Shishkin if (pdrv->policy_attr) { 421c7fd62bcSAlexander Shishkin pe->node_type = get_policy_node_type(pdrv->policy_attr); 422c7fd62bcSAlexander Shishkin if (!pe->node_type) 423c7fd62bcSAlexander Shishkin goto unlock; 424c7fd62bcSAlexander Shishkin } 425c7fd62bcSAlexander Shishkin 426c7fd62bcSAlexander Shishkin list_add_tail(&pe->entry, &stm_pdrv_head); 427c7fd62bcSAlexander Shishkin pe->pdrv = pdrv; 428c7fd62bcSAlexander Shishkin 429c7fd62bcSAlexander Shishkin ret = 0; 430c7fd62bcSAlexander Shishkin unlock: 431c7fd62bcSAlexander Shishkin mutex_unlock(&stm_pdrv_mutex); 432c7fd62bcSAlexander Shishkin 433c7fd62bcSAlexander Shishkin if (ret) 434c7fd62bcSAlexander Shishkin kfree(pe); 435c7fd62bcSAlexander Shishkin 436c7fd62bcSAlexander Shishkin return ret; 437c7fd62bcSAlexander Shishkin } 438c7fd62bcSAlexander Shishkin EXPORT_SYMBOL_GPL(stm_register_protocol); 439c7fd62bcSAlexander Shishkin 440c7fd62bcSAlexander Shishkin void stm_unregister_protocol(const struct stm_protocol_driver *pdrv) 441c7fd62bcSAlexander Shishkin { 442c7fd62bcSAlexander Shishkin struct stm_pdrv_entry *pe, *iter; 443c7fd62bcSAlexander Shishkin 444c7fd62bcSAlexander Shishkin mutex_lock(&stm_pdrv_mutex); 445c7fd62bcSAlexander Shishkin 446c7fd62bcSAlexander Shishkin list_for_each_entry_safe(pe, iter, &stm_pdrv_head, entry) { 447c7fd62bcSAlexander Shishkin if (pe->pdrv == pdrv) { 448c7fd62bcSAlexander Shishkin list_del(&pe->entry); 449c7fd62bcSAlexander Shishkin 450c7fd62bcSAlexander Shishkin if (pe->node_type) { 451c7fd62bcSAlexander Shishkin kfree(pe->node_type->ct_attrs); 452c7fd62bcSAlexander Shishkin kfree(pe->node_type); 453c7fd62bcSAlexander Shishkin } 454c7fd62bcSAlexander Shishkin kfree(pe); 455c7fd62bcSAlexander Shishkin break; 456c7fd62bcSAlexander Shishkin } 457c7fd62bcSAlexander Shishkin } 458c7fd62bcSAlexander Shishkin 459c7fd62bcSAlexander Shishkin mutex_unlock(&stm_pdrv_mutex); 460c7fd62bcSAlexander Shishkin } 461c7fd62bcSAlexander Shishkin EXPORT_SYMBOL_GPL(stm_unregister_protocol); 462c7fd62bcSAlexander Shishkin 463c7fd62bcSAlexander Shishkin static bool stm_get_protocol(const struct stm_protocol_driver *pdrv) 464c7fd62bcSAlexander Shishkin { 465c7fd62bcSAlexander Shishkin return try_module_get(pdrv->owner); 466c7fd62bcSAlexander Shishkin } 467c7fd62bcSAlexander Shishkin 468c7fd62bcSAlexander Shishkin void stm_put_protocol(const struct stm_protocol_driver *pdrv) 469c7fd62bcSAlexander Shishkin { 470c7fd62bcSAlexander Shishkin module_put(pdrv->owner); 471c7fd62bcSAlexander Shishkin } 472c7fd62bcSAlexander Shishkin 473c7fd62bcSAlexander Shishkin int stm_lookup_protocol(const char *name, 474c7fd62bcSAlexander Shishkin const struct stm_protocol_driver **pdrv, 475c7fd62bcSAlexander Shishkin const struct config_item_type **node_type) 476c7fd62bcSAlexander Shishkin { 477c7fd62bcSAlexander Shishkin const struct stm_pdrv_entry *pe; 478c7fd62bcSAlexander Shishkin 479c7fd62bcSAlexander Shishkin mutex_lock(&stm_pdrv_mutex); 480c7fd62bcSAlexander Shishkin 481c7fd62bcSAlexander Shishkin pe = __stm_lookup_protocol(name); 482c7fd62bcSAlexander Shishkin if (pe && pe->pdrv && stm_get_protocol(pe->pdrv)) { 483c7fd62bcSAlexander Shishkin *pdrv = pe->pdrv; 484c7fd62bcSAlexander Shishkin *node_type = pe->node_type; 485c7fd62bcSAlexander Shishkin } 486c7fd62bcSAlexander Shishkin 487c7fd62bcSAlexander Shishkin mutex_unlock(&stm_pdrv_mutex); 488c7fd62bcSAlexander Shishkin 489c7fd62bcSAlexander Shishkin return pe ? 0 : -ENOENT; 490c7fd62bcSAlexander Shishkin } 491c7fd62bcSAlexander Shishkin 4927bd1d409SAlexander Shishkin static int stm_char_open(struct inode *inode, struct file *file) 4937bd1d409SAlexander Shishkin { 4947bd1d409SAlexander Shishkin struct stm_file *stmf; 4957bd1d409SAlexander Shishkin struct device *dev; 4967bd1d409SAlexander Shishkin unsigned int major = imajor(inode); 497a0ebf519SJohan Hovold int err = -ENOMEM; 4987bd1d409SAlexander Shishkin 4997bd1d409SAlexander Shishkin dev = class_find_device(&stm_class, NULL, &major, major_match); 5007bd1d409SAlexander Shishkin if (!dev) 5017bd1d409SAlexander Shishkin return -ENODEV; 5027bd1d409SAlexander Shishkin 5037bd1d409SAlexander Shishkin stmf = kzalloc(sizeof(*stmf), GFP_KERNEL); 5047bd1d409SAlexander Shishkin if (!stmf) 505a0ebf519SJohan Hovold goto err_put_device; 5067bd1d409SAlexander Shishkin 507a0ebf519SJohan Hovold err = -ENODEV; 508cde4ad83SAlexander Shishkin stm_output_init(&stmf->output); 5097bd1d409SAlexander Shishkin stmf->stm = to_stm_device(dev); 5107bd1d409SAlexander Shishkin 5117bd1d409SAlexander Shishkin if (!try_module_get(stmf->stm->owner)) 5127bd1d409SAlexander Shishkin goto err_free; 5137bd1d409SAlexander Shishkin 5147bd1d409SAlexander Shishkin file->private_data = stmf; 5157bd1d409SAlexander Shishkin 5167bd1d409SAlexander Shishkin return nonseekable_open(inode, file); 5177bd1d409SAlexander Shishkin 5187bd1d409SAlexander Shishkin err_free: 519a0ebf519SJohan Hovold kfree(stmf); 520a0ebf519SJohan Hovold err_put_device: 521f7c81c71SAlexander Shishkin /* matches class_find_device() above */ 522f7c81c71SAlexander Shishkin put_device(dev); 5237bd1d409SAlexander Shishkin 5247bd1d409SAlexander Shishkin return err; 5257bd1d409SAlexander Shishkin } 5267bd1d409SAlexander Shishkin 5277bd1d409SAlexander Shishkin static int stm_char_release(struct inode *inode, struct file *file) 5287bd1d409SAlexander Shishkin { 5297bd1d409SAlexander Shishkin struct stm_file *stmf = file->private_data; 530cc842407SAlexander Shishkin struct stm_device *stm = stmf->stm; 5317bd1d409SAlexander Shishkin 532cc842407SAlexander Shishkin if (stm->data->unlink) 533cc842407SAlexander Shishkin stm->data->unlink(stm->data, stmf->output.master, 534cc842407SAlexander Shishkin stmf->output.channel); 535cc842407SAlexander Shishkin 536cc842407SAlexander Shishkin stm_output_free(stm, &stmf->output); 537f7c81c71SAlexander Shishkin 538f7c81c71SAlexander Shishkin /* 539f7c81c71SAlexander Shishkin * matches the stm_char_open()'s 540f7c81c71SAlexander Shishkin * class_find_device() + try_module_get() 541f7c81c71SAlexander Shishkin */ 542cc842407SAlexander Shishkin stm_put_device(stm); 5437bd1d409SAlexander Shishkin kfree(stmf); 5447bd1d409SAlexander Shishkin 5457bd1d409SAlexander Shishkin return 0; 5467bd1d409SAlexander Shishkin } 5477bd1d409SAlexander Shishkin 548cb6102bdSAlexander Shishkin static int 549cb6102bdSAlexander Shishkin stm_assign_first_policy(struct stm_device *stm, struct stm_output *output, 550cb6102bdSAlexander Shishkin char **ids, unsigned int width) 5517bd1d409SAlexander Shishkin { 552cb6102bdSAlexander Shishkin struct stp_policy_node *pn; 553cb6102bdSAlexander Shishkin int err, n; 5547bd1d409SAlexander Shishkin 555cb6102bdSAlexander Shishkin /* 556cb6102bdSAlexander Shishkin * On success, stp_policy_node_lookup() will return holding the 557cb6102bdSAlexander Shishkin * configfs subsystem mutex, which is then released in 558cb6102bdSAlexander Shishkin * stp_policy_node_put(). This allows the pdrv->output_open() in 559cb6102bdSAlexander Shishkin * stm_output_assign() to serialize against the attribute accessors. 560cb6102bdSAlexander Shishkin */ 561cb6102bdSAlexander Shishkin for (n = 0, pn = NULL; ids[n] && !pn; n++) 562cb6102bdSAlexander Shishkin pn = stp_policy_node_lookup(stm, ids[n]); 5637bd1d409SAlexander Shishkin 564cb6102bdSAlexander Shishkin if (!pn) 565cb6102bdSAlexander Shishkin return -EINVAL; 5667bd1d409SAlexander Shishkin 567cb6102bdSAlexander Shishkin err = stm_output_assign(stm, width, pn, output); 5687bd1d409SAlexander Shishkin 569cb6102bdSAlexander Shishkin stp_policy_node_put(pn); 570cb6102bdSAlexander Shishkin 571cb6102bdSAlexander Shishkin return err; 5727bd1d409SAlexander Shishkin } 5737bd1d409SAlexander Shishkin 574d279a380SAlexander Shishkin /** 575d279a380SAlexander Shishkin * stm_data_write() - send the given payload as data packets 576d279a380SAlexander Shishkin * @data: stm driver's data 577d279a380SAlexander Shishkin * @m: STP master 578d279a380SAlexander Shishkin * @c: STP channel 579d279a380SAlexander Shishkin * @ts_first: timestamp the first packet 580d279a380SAlexander Shishkin * @buf: data payload buffer 581d279a380SAlexander Shishkin * @count: data payload size 582d279a380SAlexander Shishkin */ 583d279a380SAlexander Shishkin ssize_t notrace stm_data_write(struct stm_data *data, unsigned int m, 584d279a380SAlexander Shishkin unsigned int c, bool ts_first, const void *buf, 585d279a380SAlexander Shishkin size_t count) 586d279a380SAlexander Shishkin { 587d279a380SAlexander Shishkin unsigned int flags = ts_first ? STP_PACKET_TIMESTAMPED : 0; 588d279a380SAlexander Shishkin ssize_t sz; 589d279a380SAlexander Shishkin size_t pos; 590d279a380SAlexander Shishkin 591d279a380SAlexander Shishkin for (pos = 0, sz = 0; pos < count; pos += sz) { 592d279a380SAlexander Shishkin sz = min_t(unsigned int, count - pos, 8); 593d279a380SAlexander Shishkin sz = data->packet(data, m, c, STP_PACKET_DATA, flags, sz, 594d279a380SAlexander Shishkin &((u8 *)buf)[pos]); 595d279a380SAlexander Shishkin if (sz <= 0) 596d279a380SAlexander Shishkin break; 597d279a380SAlexander Shishkin 598d279a380SAlexander Shishkin if (ts_first) { 599d279a380SAlexander Shishkin flags = 0; 600d279a380SAlexander Shishkin ts_first = false; 601d279a380SAlexander Shishkin } 602d279a380SAlexander Shishkin } 603d279a380SAlexander Shishkin 604d279a380SAlexander Shishkin return sz < 0 ? sz : pos; 605d279a380SAlexander Shishkin } 606d279a380SAlexander Shishkin EXPORT_SYMBOL_GPL(stm_data_write); 607d279a380SAlexander Shishkin 60824c7bcb6SAlexander Shishkin static ssize_t notrace 60924c7bcb6SAlexander Shishkin stm_write(struct stm_device *stm, struct stm_output *output, 61024c7bcb6SAlexander Shishkin unsigned int chan, const char *buf, size_t count) 6117bd1d409SAlexander Shishkin { 61224c7bcb6SAlexander Shishkin int err; 6137bd1d409SAlexander Shishkin 61424c7bcb6SAlexander Shishkin /* stm->pdrv is serialized against policy_mutex */ 61524c7bcb6SAlexander Shishkin if (!stm->pdrv) 61624c7bcb6SAlexander Shishkin return -ENODEV; 617f8560a9bSAlexander Shishkin 61824c7bcb6SAlexander Shishkin err = stm->pdrv->write(stm->data, output, chan, buf, count); 61924c7bcb6SAlexander Shishkin if (err < 0) 62024c7bcb6SAlexander Shishkin return err; 62124c7bcb6SAlexander Shishkin 62224c7bcb6SAlexander Shishkin return err; 6237bd1d409SAlexander Shishkin } 6247bd1d409SAlexander Shishkin 6257bd1d409SAlexander Shishkin static ssize_t stm_char_write(struct file *file, const char __user *buf, 6267bd1d409SAlexander Shishkin size_t count, loff_t *ppos) 6277bd1d409SAlexander Shishkin { 6287bd1d409SAlexander Shishkin struct stm_file *stmf = file->private_data; 6297bd1d409SAlexander Shishkin struct stm_device *stm = stmf->stm; 6307bd1d409SAlexander Shishkin char *kbuf; 6317bd1d409SAlexander Shishkin int err; 6327bd1d409SAlexander Shishkin 633f08b1826SAlexander Shishkin if (count + 1 > PAGE_SIZE) 634f08b1826SAlexander Shishkin count = PAGE_SIZE - 1; 635f08b1826SAlexander Shishkin 6367bd1d409SAlexander Shishkin /* 637cb6102bdSAlexander Shishkin * If no m/c have been assigned to this writer up to this 638cb6102bdSAlexander Shishkin * point, try to use the task name and "default" policy entries. 6397bd1d409SAlexander Shishkin */ 6407bd1d409SAlexander Shishkin if (!stmf->output.nr_chans) { 641cb6102bdSAlexander Shishkin char comm[sizeof(current->comm)]; 642cb6102bdSAlexander Shishkin char *ids[] = { comm, "default", NULL }; 643cb6102bdSAlexander Shishkin 644cb6102bdSAlexander Shishkin get_task_comm(comm, current); 645cb6102bdSAlexander Shishkin 646cb6102bdSAlexander Shishkin err = stm_assign_first_policy(stmf->stm, &stmf->output, ids, 1); 6477bd1d409SAlexander Shishkin /* 6487bd1d409SAlexander Shishkin * EBUSY means that somebody else just assigned this 6497bd1d409SAlexander Shishkin * output, which is just fine for write() 6507bd1d409SAlexander Shishkin */ 651cb6102bdSAlexander Shishkin if (err) 6527bd1d409SAlexander Shishkin return err; 6537bd1d409SAlexander Shishkin } 6547bd1d409SAlexander Shishkin 6557bd1d409SAlexander Shishkin kbuf = kmalloc(count + 1, GFP_KERNEL); 6567bd1d409SAlexander Shishkin if (!kbuf) 6577bd1d409SAlexander Shishkin return -ENOMEM; 6587bd1d409SAlexander Shishkin 6597bd1d409SAlexander Shishkin err = copy_from_user(kbuf, buf, count); 6607bd1d409SAlexander Shishkin if (err) { 6617bd1d409SAlexander Shishkin kfree(kbuf); 6627bd1d409SAlexander Shishkin return -EFAULT; 6637bd1d409SAlexander Shishkin } 6647bd1d409SAlexander Shishkin 6658e0469a4SAlexander Shishkin pm_runtime_get_sync(&stm->dev); 6668e0469a4SAlexander Shishkin 66724c7bcb6SAlexander Shishkin count = stm_write(stm, &stmf->output, 0, kbuf, count); 6687bd1d409SAlexander Shishkin 6698e0469a4SAlexander Shishkin pm_runtime_mark_last_busy(&stm->dev); 6708e0469a4SAlexander Shishkin pm_runtime_put_autosuspend(&stm->dev); 6717bd1d409SAlexander Shishkin kfree(kbuf); 6727bd1d409SAlexander Shishkin 6737bd1d409SAlexander Shishkin return count; 6747bd1d409SAlexander Shishkin } 6757bd1d409SAlexander Shishkin 6768e0469a4SAlexander Shishkin static void stm_mmap_open(struct vm_area_struct *vma) 6778e0469a4SAlexander Shishkin { 6788e0469a4SAlexander Shishkin struct stm_file *stmf = vma->vm_file->private_data; 6798e0469a4SAlexander Shishkin struct stm_device *stm = stmf->stm; 6808e0469a4SAlexander Shishkin 6818e0469a4SAlexander Shishkin pm_runtime_get(&stm->dev); 6828e0469a4SAlexander Shishkin } 6838e0469a4SAlexander Shishkin 6848e0469a4SAlexander Shishkin static void stm_mmap_close(struct vm_area_struct *vma) 6858e0469a4SAlexander Shishkin { 6868e0469a4SAlexander Shishkin struct stm_file *stmf = vma->vm_file->private_data; 6878e0469a4SAlexander Shishkin struct stm_device *stm = stmf->stm; 6888e0469a4SAlexander Shishkin 6898e0469a4SAlexander Shishkin pm_runtime_mark_last_busy(&stm->dev); 6908e0469a4SAlexander Shishkin pm_runtime_put_autosuspend(&stm->dev); 6918e0469a4SAlexander Shishkin } 6928e0469a4SAlexander Shishkin 6938e0469a4SAlexander Shishkin static const struct vm_operations_struct stm_mmap_vmops = { 6948e0469a4SAlexander Shishkin .open = stm_mmap_open, 6958e0469a4SAlexander Shishkin .close = stm_mmap_close, 6968e0469a4SAlexander Shishkin }; 6978e0469a4SAlexander Shishkin 6987bd1d409SAlexander Shishkin static int stm_char_mmap(struct file *file, struct vm_area_struct *vma) 6997bd1d409SAlexander Shishkin { 7007bd1d409SAlexander Shishkin struct stm_file *stmf = file->private_data; 7017bd1d409SAlexander Shishkin struct stm_device *stm = stmf->stm; 7027bd1d409SAlexander Shishkin unsigned long size, phys; 7037bd1d409SAlexander Shishkin 7047bd1d409SAlexander Shishkin if (!stm->data->mmio_addr) 7057bd1d409SAlexander Shishkin return -EOPNOTSUPP; 7067bd1d409SAlexander Shishkin 7077bd1d409SAlexander Shishkin if (vma->vm_pgoff) 7087bd1d409SAlexander Shishkin return -EINVAL; 7097bd1d409SAlexander Shishkin 7107bd1d409SAlexander Shishkin size = vma->vm_end - vma->vm_start; 7117bd1d409SAlexander Shishkin 7127bd1d409SAlexander Shishkin if (stmf->output.nr_chans * stm->data->sw_mmiosz != size) 7137bd1d409SAlexander Shishkin return -EINVAL; 7147bd1d409SAlexander Shishkin 7157bd1d409SAlexander Shishkin phys = stm->data->mmio_addr(stm->data, stmf->output.master, 7167bd1d409SAlexander Shishkin stmf->output.channel, 7177bd1d409SAlexander Shishkin stmf->output.nr_chans); 7187bd1d409SAlexander Shishkin 7197bd1d409SAlexander Shishkin if (!phys) 7207bd1d409SAlexander Shishkin return -EINVAL; 7217bd1d409SAlexander Shishkin 7228e0469a4SAlexander Shishkin pm_runtime_get_sync(&stm->dev); 7238e0469a4SAlexander Shishkin 7247bd1d409SAlexander Shishkin vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 7257bd1d409SAlexander Shishkin vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; 7268e0469a4SAlexander Shishkin vma->vm_ops = &stm_mmap_vmops; 7277bd1d409SAlexander Shishkin vm_iomap_memory(vma, phys, size); 7287bd1d409SAlexander Shishkin 7297bd1d409SAlexander Shishkin return 0; 7307bd1d409SAlexander Shishkin } 7317bd1d409SAlexander Shishkin 7327bd1d409SAlexander Shishkin static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) 7337bd1d409SAlexander Shishkin { 7347bd1d409SAlexander Shishkin struct stm_device *stm = stmf->stm; 7357bd1d409SAlexander Shishkin struct stp_policy_id *id; 736cb6102bdSAlexander Shishkin char *ids[] = { NULL, NULL }; 737bf7cbaaeSAlexander Shishkin int ret = -EINVAL, wlimit = 1; 7387bd1d409SAlexander Shishkin u32 size; 7397bd1d409SAlexander Shishkin 7407bd1d409SAlexander Shishkin if (stmf->output.nr_chans) 7417bd1d409SAlexander Shishkin return -EBUSY; 7427bd1d409SAlexander Shishkin 7437bd1d409SAlexander Shishkin if (copy_from_user(&size, arg, sizeof(size))) 7447bd1d409SAlexander Shishkin return -EFAULT; 7457bd1d409SAlexander Shishkin 74671c488f3SDan Carpenter if (size < sizeof(*id) || size >= PATH_MAX + sizeof(*id)) 7477bd1d409SAlexander Shishkin return -EINVAL; 7487bd1d409SAlexander Shishkin 7497bd1d409SAlexander Shishkin /* 7507bd1d409SAlexander Shishkin * size + 1 to make sure the .id string at the bottom is terminated, 7517bd1d409SAlexander Shishkin * which is also why memdup_user() is not useful here 7527bd1d409SAlexander Shishkin */ 7537bd1d409SAlexander Shishkin id = kzalloc(size + 1, GFP_KERNEL); 7547bd1d409SAlexander Shishkin if (!id) 7557bd1d409SAlexander Shishkin return -ENOMEM; 7567bd1d409SAlexander Shishkin 7577bd1d409SAlexander Shishkin if (copy_from_user(id, arg, size)) { 7587bd1d409SAlexander Shishkin ret = -EFAULT; 7597bd1d409SAlexander Shishkin goto err_free; 7607bd1d409SAlexander Shishkin } 7617bd1d409SAlexander Shishkin 7627bd1d409SAlexander Shishkin if (id->__reserved_0 || id->__reserved_1) 7637bd1d409SAlexander Shishkin goto err_free; 7647bd1d409SAlexander Shishkin 765bf7cbaaeSAlexander Shishkin if (stm->data->sw_mmiosz) 766bf7cbaaeSAlexander Shishkin wlimit = PAGE_SIZE / stm->data->sw_mmiosz; 767bf7cbaaeSAlexander Shishkin 768bf7cbaaeSAlexander Shishkin if (id->width < 1 || id->width > wlimit) 7697bd1d409SAlexander Shishkin goto err_free; 7707bd1d409SAlexander Shishkin 771cb6102bdSAlexander Shishkin ids[0] = id->id; 772cb6102bdSAlexander Shishkin ret = stm_assign_first_policy(stmf->stm, &stmf->output, ids, 773cb6102bdSAlexander Shishkin id->width); 7747bd1d409SAlexander Shishkin if (ret) 7757bd1d409SAlexander Shishkin goto err_free; 7767bd1d409SAlexander Shishkin 7777bd1d409SAlexander Shishkin if (stm->data->link) 7787bd1d409SAlexander Shishkin ret = stm->data->link(stm->data, stmf->output.master, 7797bd1d409SAlexander Shishkin stmf->output.channel); 7807bd1d409SAlexander Shishkin 781f7c81c71SAlexander Shishkin if (ret) 7827bd1d409SAlexander Shishkin stm_output_free(stmf->stm, &stmf->output); 7837bd1d409SAlexander Shishkin 7847bd1d409SAlexander Shishkin err_free: 7857bd1d409SAlexander Shishkin kfree(id); 7867bd1d409SAlexander Shishkin 7877bd1d409SAlexander Shishkin return ret; 7887bd1d409SAlexander Shishkin } 7897bd1d409SAlexander Shishkin 7907bd1d409SAlexander Shishkin static int stm_char_policy_get_ioctl(struct stm_file *stmf, void __user *arg) 7917bd1d409SAlexander Shishkin { 7927bd1d409SAlexander Shishkin struct stp_policy_id id = { 7937bd1d409SAlexander Shishkin .size = sizeof(id), 7947bd1d409SAlexander Shishkin .master = stmf->output.master, 7957bd1d409SAlexander Shishkin .channel = stmf->output.channel, 7967bd1d409SAlexander Shishkin .width = stmf->output.nr_chans, 7977bd1d409SAlexander Shishkin .__reserved_0 = 0, 7987bd1d409SAlexander Shishkin .__reserved_1 = 0, 7997bd1d409SAlexander Shishkin }; 8007bd1d409SAlexander Shishkin 8017bd1d409SAlexander Shishkin return copy_to_user(arg, &id, id.size) ? -EFAULT : 0; 8027bd1d409SAlexander Shishkin } 8037bd1d409SAlexander Shishkin 8047bd1d409SAlexander Shishkin static long 8057bd1d409SAlexander Shishkin stm_char_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 8067bd1d409SAlexander Shishkin { 8077bd1d409SAlexander Shishkin struct stm_file *stmf = file->private_data; 8087bd1d409SAlexander Shishkin struct stm_data *stm_data = stmf->stm->data; 8097bd1d409SAlexander Shishkin int err = -ENOTTY; 8107bd1d409SAlexander Shishkin u64 options; 8117bd1d409SAlexander Shishkin 8127bd1d409SAlexander Shishkin switch (cmd) { 8137bd1d409SAlexander Shishkin case STP_POLICY_ID_SET: 8147bd1d409SAlexander Shishkin err = stm_char_policy_set_ioctl(stmf, (void __user *)arg); 8157bd1d409SAlexander Shishkin if (err) 8167bd1d409SAlexander Shishkin return err; 8177bd1d409SAlexander Shishkin 8187bd1d409SAlexander Shishkin return stm_char_policy_get_ioctl(stmf, (void __user *)arg); 8197bd1d409SAlexander Shishkin 8207bd1d409SAlexander Shishkin case STP_POLICY_ID_GET: 8217bd1d409SAlexander Shishkin return stm_char_policy_get_ioctl(stmf, (void __user *)arg); 8227bd1d409SAlexander Shishkin 8237bd1d409SAlexander Shishkin case STP_SET_OPTIONS: 8247bd1d409SAlexander Shishkin if (copy_from_user(&options, (u64 __user *)arg, sizeof(u64))) 8257bd1d409SAlexander Shishkin return -EFAULT; 8267bd1d409SAlexander Shishkin 8277bd1d409SAlexander Shishkin if (stm_data->set_options) 8287bd1d409SAlexander Shishkin err = stm_data->set_options(stm_data, 8297bd1d409SAlexander Shishkin stmf->output.master, 8307bd1d409SAlexander Shishkin stmf->output.channel, 8317bd1d409SAlexander Shishkin stmf->output.nr_chans, 8327bd1d409SAlexander Shishkin options); 8337bd1d409SAlexander Shishkin 8347bd1d409SAlexander Shishkin break; 8357bd1d409SAlexander Shishkin default: 8367bd1d409SAlexander Shishkin break; 8377bd1d409SAlexander Shishkin } 8387bd1d409SAlexander Shishkin 8397bd1d409SAlexander Shishkin return err; 8407bd1d409SAlexander Shishkin } 8417bd1d409SAlexander Shishkin 8427bd1d409SAlexander Shishkin #ifdef CONFIG_COMPAT 8437bd1d409SAlexander Shishkin static long 8447bd1d409SAlexander Shishkin stm_char_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 8457bd1d409SAlexander Shishkin { 8467bd1d409SAlexander Shishkin return stm_char_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); 8477bd1d409SAlexander Shishkin } 8487bd1d409SAlexander Shishkin #else 8497bd1d409SAlexander Shishkin #define stm_char_compat_ioctl NULL 8507bd1d409SAlexander Shishkin #endif 8517bd1d409SAlexander Shishkin 8527bd1d409SAlexander Shishkin static const struct file_operations stm_fops = { 8537bd1d409SAlexander Shishkin .open = stm_char_open, 8547bd1d409SAlexander Shishkin .release = stm_char_release, 8557bd1d409SAlexander Shishkin .write = stm_char_write, 8567bd1d409SAlexander Shishkin .mmap = stm_char_mmap, 8577bd1d409SAlexander Shishkin .unlocked_ioctl = stm_char_ioctl, 8587bd1d409SAlexander Shishkin .compat_ioctl = stm_char_compat_ioctl, 8597bd1d409SAlexander Shishkin .llseek = no_llseek, 8607bd1d409SAlexander Shishkin }; 8617bd1d409SAlexander Shishkin 8627bd1d409SAlexander Shishkin static void stm_device_release(struct device *dev) 8637bd1d409SAlexander Shishkin { 8647bd1d409SAlexander Shishkin struct stm_device *stm = to_stm_device(dev); 8657bd1d409SAlexander Shishkin 866b5e2ced9SAlexander Shishkin vfree(stm); 8677bd1d409SAlexander Shishkin } 8687bd1d409SAlexander Shishkin 8697bd1d409SAlexander Shishkin int stm_register_device(struct device *parent, struct stm_data *stm_data, 8707bd1d409SAlexander Shishkin struct module *owner) 8717bd1d409SAlexander Shishkin { 8727bd1d409SAlexander Shishkin struct stm_device *stm; 8737bd1d409SAlexander Shishkin unsigned int nmasters; 8747bd1d409SAlexander Shishkin int err = -ENOMEM; 8757bd1d409SAlexander Shishkin 8767bd1d409SAlexander Shishkin if (!stm_core_up) 8777bd1d409SAlexander Shishkin return -EPROBE_DEFER; 8787bd1d409SAlexander Shishkin 8797bd1d409SAlexander Shishkin if (!stm_data->packet || !stm_data->sw_nchannels) 8807bd1d409SAlexander Shishkin return -EINVAL; 8817bd1d409SAlexander Shishkin 8827b3bb0e7SChunyan Zhang nmasters = stm_data->sw_end - stm_data->sw_start + 1; 883b5e2ced9SAlexander Shishkin stm = vzalloc(sizeof(*stm) + nmasters * sizeof(void *)); 8847bd1d409SAlexander Shishkin if (!stm) 8857bd1d409SAlexander Shishkin return -ENOMEM; 8867bd1d409SAlexander Shishkin 8877bd1d409SAlexander Shishkin stm->major = register_chrdev(0, stm_data->name, &stm_fops); 8887bd1d409SAlexander Shishkin if (stm->major < 0) 8897bd1d409SAlexander Shishkin goto err_free; 8907bd1d409SAlexander Shishkin 8917bd1d409SAlexander Shishkin device_initialize(&stm->dev); 8927bd1d409SAlexander Shishkin stm->dev.devt = MKDEV(stm->major, 0); 8937bd1d409SAlexander Shishkin stm->dev.class = &stm_class; 8947bd1d409SAlexander Shishkin stm->dev.parent = parent; 8957bd1d409SAlexander Shishkin stm->dev.release = stm_device_release; 8967bd1d409SAlexander Shishkin 897389b6699SAlexander Shishkin mutex_init(&stm->link_mutex); 898389b6699SAlexander Shishkin spin_lock_init(&stm->link_lock); 899389b6699SAlexander Shishkin INIT_LIST_HEAD(&stm->link_list); 900389b6699SAlexander Shishkin 901389b6699SAlexander Shishkin /* initialize the object before it is accessible via sysfs */ 902389b6699SAlexander Shishkin spin_lock_init(&stm->mc_lock); 903389b6699SAlexander Shishkin mutex_init(&stm->policy_mutex); 904389b6699SAlexander Shishkin stm->sw_nmasters = nmasters; 905389b6699SAlexander Shishkin stm->owner = owner; 906389b6699SAlexander Shishkin stm->data = stm_data; 907389b6699SAlexander Shishkin stm_data->stm = stm; 908389b6699SAlexander Shishkin 9097bd1d409SAlexander Shishkin err = kobject_set_name(&stm->dev.kobj, "%s", stm_data->name); 9107bd1d409SAlexander Shishkin if (err) 9117bd1d409SAlexander Shishkin goto err_device; 9127bd1d409SAlexander Shishkin 9137bd1d409SAlexander Shishkin err = device_add(&stm->dev); 9147bd1d409SAlexander Shishkin if (err) 9157bd1d409SAlexander Shishkin goto err_device; 9167bd1d409SAlexander Shishkin 9178e0469a4SAlexander Shishkin /* 9188e0469a4SAlexander Shishkin * Use delayed autosuspend to avoid bouncing back and forth 9198e0469a4SAlexander Shishkin * on recurring character device writes, with the initial 9208e0469a4SAlexander Shishkin * delay time of 2 seconds. 9218e0469a4SAlexander Shishkin */ 9228e0469a4SAlexander Shishkin pm_runtime_no_callbacks(&stm->dev); 9238e0469a4SAlexander Shishkin pm_runtime_use_autosuspend(&stm->dev); 9248e0469a4SAlexander Shishkin pm_runtime_set_autosuspend_delay(&stm->dev, 2000); 9258e0469a4SAlexander Shishkin pm_runtime_set_suspended(&stm->dev); 9268e0469a4SAlexander Shishkin pm_runtime_enable(&stm->dev); 9278e0469a4SAlexander Shishkin 9287bd1d409SAlexander Shishkin return 0; 9297bd1d409SAlexander Shishkin 9307bd1d409SAlexander Shishkin err_device: 931cbe4a61dSAlexander Shishkin unregister_chrdev(stm->major, stm_data->name); 932cbe4a61dSAlexander Shishkin 933f7c81c71SAlexander Shishkin /* matches device_initialize() above */ 9347bd1d409SAlexander Shishkin put_device(&stm->dev); 9357bd1d409SAlexander Shishkin err_free: 936b5e2ced9SAlexander Shishkin vfree(stm); 9377bd1d409SAlexander Shishkin 9387bd1d409SAlexander Shishkin return err; 9397bd1d409SAlexander Shishkin } 9407bd1d409SAlexander Shishkin EXPORT_SYMBOL_GPL(stm_register_device); 9417bd1d409SAlexander Shishkin 942b4ca34aaSAlexander Shishkin static int __stm_source_link_drop(struct stm_source_device *src, 9437bd1d409SAlexander Shishkin struct stm_device *stm); 9447bd1d409SAlexander Shishkin 9457bd1d409SAlexander Shishkin void stm_unregister_device(struct stm_data *stm_data) 9467bd1d409SAlexander Shishkin { 9477bd1d409SAlexander Shishkin struct stm_device *stm = stm_data->stm; 9487bd1d409SAlexander Shishkin struct stm_source_device *src, *iter; 949b4ca34aaSAlexander Shishkin int i, ret; 9507bd1d409SAlexander Shishkin 9518e0469a4SAlexander Shishkin pm_runtime_dont_use_autosuspend(&stm->dev); 9528e0469a4SAlexander Shishkin pm_runtime_disable(&stm->dev); 9538e0469a4SAlexander Shishkin 954c74f7e82SAlexander Shishkin mutex_lock(&stm->link_mutex); 9557bd1d409SAlexander Shishkin list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) { 956b4ca34aaSAlexander Shishkin ret = __stm_source_link_drop(src, stm); 957b4ca34aaSAlexander Shishkin /* 958b4ca34aaSAlexander Shishkin * src <-> stm link must not change under the same 959b4ca34aaSAlexander Shishkin * stm::link_mutex, so complain loudly if it has; 960b4ca34aaSAlexander Shishkin * also in this situation ret!=0 means this src is 961b4ca34aaSAlexander Shishkin * not connected to this stm and it should be otherwise 962b4ca34aaSAlexander Shishkin * safe to proceed with the tear-down of stm. 963b4ca34aaSAlexander Shishkin */ 964b4ca34aaSAlexander Shishkin WARN_ON_ONCE(ret); 9657bd1d409SAlexander Shishkin } 966c74f7e82SAlexander Shishkin mutex_unlock(&stm->link_mutex); 9677bd1d409SAlexander Shishkin 9687bd1d409SAlexander Shishkin synchronize_srcu(&stm_source_srcu); 9697bd1d409SAlexander Shishkin 9707bd1d409SAlexander Shishkin unregister_chrdev(stm->major, stm_data->name); 9717bd1d409SAlexander Shishkin 9727bd1d409SAlexander Shishkin mutex_lock(&stm->policy_mutex); 9737bd1d409SAlexander Shishkin if (stm->policy) 9747bd1d409SAlexander Shishkin stp_policy_unbind(stm->policy); 9757bd1d409SAlexander Shishkin mutex_unlock(&stm->policy_mutex); 9767bd1d409SAlexander Shishkin 97773a3ed19SChunyan Zhang for (i = stm->data->sw_start; i <= stm->data->sw_end; i++) 9787bd1d409SAlexander Shishkin stp_master_free(stm, i); 9797bd1d409SAlexander Shishkin 9807bd1d409SAlexander Shishkin device_unregister(&stm->dev); 9817bd1d409SAlexander Shishkin stm_data->stm = NULL; 9827bd1d409SAlexander Shishkin } 9837bd1d409SAlexander Shishkin EXPORT_SYMBOL_GPL(stm_unregister_device); 9847bd1d409SAlexander Shishkin 985c74f7e82SAlexander Shishkin /* 986c74f7e82SAlexander Shishkin * stm::link_list access serialization uses a spinlock and a mutex; holding 987c74f7e82SAlexander Shishkin * either of them guarantees that the list is stable; modification requires 988c74f7e82SAlexander Shishkin * holding both of them. 989c74f7e82SAlexander Shishkin * 990c74f7e82SAlexander Shishkin * Lock ordering is as follows: 991c74f7e82SAlexander Shishkin * stm::link_mutex 992c74f7e82SAlexander Shishkin * stm::link_lock 993c74f7e82SAlexander Shishkin * src::link_lock 994c74f7e82SAlexander Shishkin */ 995c74f7e82SAlexander Shishkin 9967bd1d409SAlexander Shishkin /** 9977bd1d409SAlexander Shishkin * stm_source_link_add() - connect an stm_source device to an stm device 9987bd1d409SAlexander Shishkin * @src: stm_source device 9997bd1d409SAlexander Shishkin * @stm: stm device 10007bd1d409SAlexander Shishkin * 10017bd1d409SAlexander Shishkin * This function establishes a link from stm_source to an stm device so that 10027bd1d409SAlexander Shishkin * the former can send out trace data to the latter. 10037bd1d409SAlexander Shishkin * 10047bd1d409SAlexander Shishkin * Return: 0 on success, -errno otherwise. 10057bd1d409SAlexander Shishkin */ 10067bd1d409SAlexander Shishkin static int stm_source_link_add(struct stm_source_device *src, 10077bd1d409SAlexander Shishkin struct stm_device *stm) 10087bd1d409SAlexander Shishkin { 1009cb6102bdSAlexander Shishkin char *ids[] = { NULL, "default", NULL }; 1010cb6102bdSAlexander Shishkin int err = -ENOMEM; 10117bd1d409SAlexander Shishkin 1012c74f7e82SAlexander Shishkin mutex_lock(&stm->link_mutex); 10137bd1d409SAlexander Shishkin spin_lock(&stm->link_lock); 10147bd1d409SAlexander Shishkin spin_lock(&src->link_lock); 10157bd1d409SAlexander Shishkin 10167bd1d409SAlexander Shishkin /* src->link is dereferenced under stm_source_srcu but not the list */ 10177bd1d409SAlexander Shishkin rcu_assign_pointer(src->link, stm); 10187bd1d409SAlexander Shishkin list_add_tail(&src->link_entry, &stm->link_list); 10197bd1d409SAlexander Shishkin 10207bd1d409SAlexander Shishkin spin_unlock(&src->link_lock); 10217bd1d409SAlexander Shishkin spin_unlock(&stm->link_lock); 1022c74f7e82SAlexander Shishkin mutex_unlock(&stm->link_mutex); 10237bd1d409SAlexander Shishkin 1024cb6102bdSAlexander Shishkin ids[0] = kstrdup(src->data->name, GFP_KERNEL); 1025cb6102bdSAlexander Shishkin if (!ids[0]) 1026cb6102bdSAlexander Shishkin goto fail_detach; 10277bd1d409SAlexander Shishkin 1028cb6102bdSAlexander Shishkin err = stm_assign_first_policy(stm, &src->output, ids, 1029cb6102bdSAlexander Shishkin src->data->nr_chans); 1030cb6102bdSAlexander Shishkin kfree(ids[0]); 10317bd1d409SAlexander Shishkin 10327bd1d409SAlexander Shishkin if (err) 10337bd1d409SAlexander Shishkin goto fail_detach; 10347bd1d409SAlexander Shishkin 10357bd1d409SAlexander Shishkin /* this is to notify the STM device that a new link has been made */ 10367bd1d409SAlexander Shishkin if (stm->data->link) 10377bd1d409SAlexander Shishkin err = stm->data->link(stm->data, src->output.master, 10387bd1d409SAlexander Shishkin src->output.channel); 10397bd1d409SAlexander Shishkin 10407bd1d409SAlexander Shishkin if (err) 10417bd1d409SAlexander Shishkin goto fail_free_output; 10427bd1d409SAlexander Shishkin 10437bd1d409SAlexander Shishkin /* this is to let the source carry out all necessary preparations */ 10447bd1d409SAlexander Shishkin if (src->data->link) 10457bd1d409SAlexander Shishkin src->data->link(src->data); 10467bd1d409SAlexander Shishkin 10477bd1d409SAlexander Shishkin return 0; 10487bd1d409SAlexander Shishkin 10497bd1d409SAlexander Shishkin fail_free_output: 10507bd1d409SAlexander Shishkin stm_output_free(stm, &src->output); 10517bd1d409SAlexander Shishkin 10527bd1d409SAlexander Shishkin fail_detach: 1053c74f7e82SAlexander Shishkin mutex_lock(&stm->link_mutex); 10547bd1d409SAlexander Shishkin spin_lock(&stm->link_lock); 10557bd1d409SAlexander Shishkin spin_lock(&src->link_lock); 10567bd1d409SAlexander Shishkin 10577bd1d409SAlexander Shishkin rcu_assign_pointer(src->link, NULL); 10587bd1d409SAlexander Shishkin list_del_init(&src->link_entry); 10597bd1d409SAlexander Shishkin 10607bd1d409SAlexander Shishkin spin_unlock(&src->link_lock); 10617bd1d409SAlexander Shishkin spin_unlock(&stm->link_lock); 1062c74f7e82SAlexander Shishkin mutex_unlock(&stm->link_mutex); 10637bd1d409SAlexander Shishkin 10647bd1d409SAlexander Shishkin return err; 10657bd1d409SAlexander Shishkin } 10667bd1d409SAlexander Shishkin 10677bd1d409SAlexander Shishkin /** 10687bd1d409SAlexander Shishkin * __stm_source_link_drop() - detach stm_source from an stm device 10697bd1d409SAlexander Shishkin * @src: stm_source device 10707bd1d409SAlexander Shishkin * @stm: stm device 10717bd1d409SAlexander Shishkin * 10727bd1d409SAlexander Shishkin * If @stm is @src::link, disconnect them from one another and put the 10737bd1d409SAlexander Shishkin * reference on the @stm device. 10747bd1d409SAlexander Shishkin * 1075c74f7e82SAlexander Shishkin * Caller must hold stm::link_mutex. 10767bd1d409SAlexander Shishkin */ 1077b4ca34aaSAlexander Shishkin static int __stm_source_link_drop(struct stm_source_device *src, 10787bd1d409SAlexander Shishkin struct stm_device *stm) 10797bd1d409SAlexander Shishkin { 10800df771deSAlexander Shishkin struct stm_device *link; 1081b4ca34aaSAlexander Shishkin int ret = 0; 10820df771deSAlexander Shishkin 1083c74f7e82SAlexander Shishkin lockdep_assert_held(&stm->link_mutex); 1084c74f7e82SAlexander Shishkin 1085c74f7e82SAlexander Shishkin /* for stm::link_list modification, we hold both mutex and spinlock */ 1086c74f7e82SAlexander Shishkin spin_lock(&stm->link_lock); 10877bd1d409SAlexander Shishkin spin_lock(&src->link_lock); 10880df771deSAlexander Shishkin link = srcu_dereference_check(src->link, &stm_source_srcu, 1); 1089b4ca34aaSAlexander Shishkin 1090b4ca34aaSAlexander Shishkin /* 1091b4ca34aaSAlexander Shishkin * The linked device may have changed since we last looked, because 1092b4ca34aaSAlexander Shishkin * we weren't holding the src::link_lock back then; if this is the 1093b4ca34aaSAlexander Shishkin * case, tell the caller to retry. 1094b4ca34aaSAlexander Shishkin */ 1095b4ca34aaSAlexander Shishkin if (link != stm) { 1096b4ca34aaSAlexander Shishkin ret = -EAGAIN; 10971810f2c4SAlexander Shishkin goto unlock; 1098b4ca34aaSAlexander Shishkin } 10997bd1d409SAlexander Shishkin 11000df771deSAlexander Shishkin stm_output_free(link, &src->output); 11017bd1d409SAlexander Shishkin list_del_init(&src->link_entry); 11028e0469a4SAlexander Shishkin pm_runtime_mark_last_busy(&link->dev); 11038e0469a4SAlexander Shishkin pm_runtime_put_autosuspend(&link->dev); 11047bd1d409SAlexander Shishkin /* matches stm_find_device() from stm_source_link_store() */ 11050df771deSAlexander Shishkin stm_put_device(link); 11067bd1d409SAlexander Shishkin rcu_assign_pointer(src->link, NULL); 11077bd1d409SAlexander Shishkin 11081810f2c4SAlexander Shishkin unlock: 11097bd1d409SAlexander Shishkin spin_unlock(&src->link_lock); 1110c74f7e82SAlexander Shishkin spin_unlock(&stm->link_lock); 1111b4ca34aaSAlexander Shishkin 1112cc842407SAlexander Shishkin /* 1113cc842407SAlexander Shishkin * Call the unlink callbacks for both source and stm, when we know 1114cc842407SAlexander Shishkin * that we have actually performed the unlinking. 1115cc842407SAlexander Shishkin */ 1116cc842407SAlexander Shishkin if (!ret) { 1117cc842407SAlexander Shishkin if (src->data->unlink) 1118b4ca34aaSAlexander Shishkin src->data->unlink(src->data); 1119b4ca34aaSAlexander Shishkin 1120cc842407SAlexander Shishkin if (stm->data->unlink) 1121cc842407SAlexander Shishkin stm->data->unlink(stm->data, src->output.master, 1122cc842407SAlexander Shishkin src->output.channel); 1123cc842407SAlexander Shishkin } 1124cc842407SAlexander Shishkin 1125b4ca34aaSAlexander Shishkin return ret; 11267bd1d409SAlexander Shishkin } 11277bd1d409SAlexander Shishkin 11287bd1d409SAlexander Shishkin /** 11297bd1d409SAlexander Shishkin * stm_source_link_drop() - detach stm_source from its stm device 11307bd1d409SAlexander Shishkin * @src: stm_source device 11317bd1d409SAlexander Shishkin * 11327bd1d409SAlexander Shishkin * Unlinking means disconnecting from source's STM device; after this 11337bd1d409SAlexander Shishkin * writes will be unsuccessful until it is linked to a new STM device. 11347bd1d409SAlexander Shishkin * 11357bd1d409SAlexander Shishkin * This will happen on "stm_source_link" sysfs attribute write to undo 11367bd1d409SAlexander Shishkin * the existing link (if any), or on linked STM device's de-registration. 11377bd1d409SAlexander Shishkin */ 11387bd1d409SAlexander Shishkin static void stm_source_link_drop(struct stm_source_device *src) 11397bd1d409SAlexander Shishkin { 11407bd1d409SAlexander Shishkin struct stm_device *stm; 1141b4ca34aaSAlexander Shishkin int idx, ret; 11427bd1d409SAlexander Shishkin 1143b4ca34aaSAlexander Shishkin retry: 11447bd1d409SAlexander Shishkin idx = srcu_read_lock(&stm_source_srcu); 1145b4ca34aaSAlexander Shishkin /* 1146b4ca34aaSAlexander Shishkin * The stm device will be valid for the duration of this 1147b4ca34aaSAlexander Shishkin * read section, but the link may change before we grab 1148b4ca34aaSAlexander Shishkin * the src::link_lock in __stm_source_link_drop(). 1149b4ca34aaSAlexander Shishkin */ 11507bd1d409SAlexander Shishkin stm = srcu_dereference(src->link, &stm_source_srcu); 11517bd1d409SAlexander Shishkin 1152b4ca34aaSAlexander Shishkin ret = 0; 11537bd1d409SAlexander Shishkin if (stm) { 1154c74f7e82SAlexander Shishkin mutex_lock(&stm->link_mutex); 1155b4ca34aaSAlexander Shishkin ret = __stm_source_link_drop(src, stm); 1156c74f7e82SAlexander Shishkin mutex_unlock(&stm->link_mutex); 11577bd1d409SAlexander Shishkin } 11587bd1d409SAlexander Shishkin 11597bd1d409SAlexander Shishkin srcu_read_unlock(&stm_source_srcu, idx); 1160b4ca34aaSAlexander Shishkin 1161b4ca34aaSAlexander Shishkin /* if it did change, retry */ 1162b4ca34aaSAlexander Shishkin if (ret == -EAGAIN) 1163b4ca34aaSAlexander Shishkin goto retry; 11647bd1d409SAlexander Shishkin } 11657bd1d409SAlexander Shishkin 11667bd1d409SAlexander Shishkin static ssize_t stm_source_link_show(struct device *dev, 11677bd1d409SAlexander Shishkin struct device_attribute *attr, 11687bd1d409SAlexander Shishkin char *buf) 11697bd1d409SAlexander Shishkin { 11707bd1d409SAlexander Shishkin struct stm_source_device *src = to_stm_source_device(dev); 11717bd1d409SAlexander Shishkin struct stm_device *stm; 11727bd1d409SAlexander Shishkin int idx, ret; 11737bd1d409SAlexander Shishkin 11747bd1d409SAlexander Shishkin idx = srcu_read_lock(&stm_source_srcu); 11757bd1d409SAlexander Shishkin stm = srcu_dereference(src->link, &stm_source_srcu); 11767bd1d409SAlexander Shishkin ret = sprintf(buf, "%s\n", 11777bd1d409SAlexander Shishkin stm ? dev_name(&stm->dev) : "<none>"); 11787bd1d409SAlexander Shishkin srcu_read_unlock(&stm_source_srcu, idx); 11797bd1d409SAlexander Shishkin 11807bd1d409SAlexander Shishkin return ret; 11817bd1d409SAlexander Shishkin } 11827bd1d409SAlexander Shishkin 11837bd1d409SAlexander Shishkin static ssize_t stm_source_link_store(struct device *dev, 11847bd1d409SAlexander Shishkin struct device_attribute *attr, 11857bd1d409SAlexander Shishkin const char *buf, size_t count) 11867bd1d409SAlexander Shishkin { 11877bd1d409SAlexander Shishkin struct stm_source_device *src = to_stm_source_device(dev); 11887bd1d409SAlexander Shishkin struct stm_device *link; 11897bd1d409SAlexander Shishkin int err; 11907bd1d409SAlexander Shishkin 11917bd1d409SAlexander Shishkin stm_source_link_drop(src); 11927bd1d409SAlexander Shishkin 11937bd1d409SAlexander Shishkin link = stm_find_device(buf); 11947bd1d409SAlexander Shishkin if (!link) 11957bd1d409SAlexander Shishkin return -EINVAL; 11967bd1d409SAlexander Shishkin 11978e0469a4SAlexander Shishkin pm_runtime_get(&link->dev); 11988e0469a4SAlexander Shishkin 11997bd1d409SAlexander Shishkin err = stm_source_link_add(src, link); 1200f7c81c71SAlexander Shishkin if (err) { 12018e0469a4SAlexander Shishkin pm_runtime_put_autosuspend(&link->dev); 1202f7c81c71SAlexander Shishkin /* matches the stm_find_device() above */ 12037bd1d409SAlexander Shishkin stm_put_device(link); 1204f7c81c71SAlexander Shishkin } 12057bd1d409SAlexander Shishkin 12067bd1d409SAlexander Shishkin return err ? : count; 12077bd1d409SAlexander Shishkin } 12087bd1d409SAlexander Shishkin 12097bd1d409SAlexander Shishkin static DEVICE_ATTR_RW(stm_source_link); 12107bd1d409SAlexander Shishkin 12117bd1d409SAlexander Shishkin static struct attribute *stm_source_attrs[] = { 12127bd1d409SAlexander Shishkin &dev_attr_stm_source_link.attr, 12137bd1d409SAlexander Shishkin NULL, 12147bd1d409SAlexander Shishkin }; 12157bd1d409SAlexander Shishkin 12167bd1d409SAlexander Shishkin ATTRIBUTE_GROUPS(stm_source); 12177bd1d409SAlexander Shishkin 12187bd1d409SAlexander Shishkin static struct class stm_source_class = { 12197bd1d409SAlexander Shishkin .name = "stm_source", 12207bd1d409SAlexander Shishkin .dev_groups = stm_source_groups, 12217bd1d409SAlexander Shishkin }; 12227bd1d409SAlexander Shishkin 12237bd1d409SAlexander Shishkin static void stm_source_device_release(struct device *dev) 12247bd1d409SAlexander Shishkin { 12257bd1d409SAlexander Shishkin struct stm_source_device *src = to_stm_source_device(dev); 12267bd1d409SAlexander Shishkin 12277bd1d409SAlexander Shishkin kfree(src); 12287bd1d409SAlexander Shishkin } 12297bd1d409SAlexander Shishkin 12307bd1d409SAlexander Shishkin /** 12317bd1d409SAlexander Shishkin * stm_source_register_device() - register an stm_source device 12327bd1d409SAlexander Shishkin * @parent: parent device 12337bd1d409SAlexander Shishkin * @data: device description structure 12347bd1d409SAlexander Shishkin * 12357bd1d409SAlexander Shishkin * This will create a device of stm_source class that can write 12367bd1d409SAlexander Shishkin * data to an stm device once linked. 12377bd1d409SAlexander Shishkin * 12387bd1d409SAlexander Shishkin * Return: 0 on success, -errno otherwise. 12397bd1d409SAlexander Shishkin */ 12407bd1d409SAlexander Shishkin int stm_source_register_device(struct device *parent, 12417bd1d409SAlexander Shishkin struct stm_source_data *data) 12427bd1d409SAlexander Shishkin { 12437bd1d409SAlexander Shishkin struct stm_source_device *src; 12447bd1d409SAlexander Shishkin int err; 12457bd1d409SAlexander Shishkin 12467bd1d409SAlexander Shishkin if (!stm_core_up) 12477bd1d409SAlexander Shishkin return -EPROBE_DEFER; 12487bd1d409SAlexander Shishkin 12497bd1d409SAlexander Shishkin src = kzalloc(sizeof(*src), GFP_KERNEL); 12507bd1d409SAlexander Shishkin if (!src) 12517bd1d409SAlexander Shishkin return -ENOMEM; 12527bd1d409SAlexander Shishkin 12537bd1d409SAlexander Shishkin device_initialize(&src->dev); 12547bd1d409SAlexander Shishkin src->dev.class = &stm_source_class; 12557bd1d409SAlexander Shishkin src->dev.parent = parent; 12567bd1d409SAlexander Shishkin src->dev.release = stm_source_device_release; 12577bd1d409SAlexander Shishkin 12587bd1d409SAlexander Shishkin err = kobject_set_name(&src->dev.kobj, "%s", data->name); 12597bd1d409SAlexander Shishkin if (err) 12607bd1d409SAlexander Shishkin goto err; 12617bd1d409SAlexander Shishkin 12628e0469a4SAlexander Shishkin pm_runtime_no_callbacks(&src->dev); 12638e0469a4SAlexander Shishkin pm_runtime_forbid(&src->dev); 12648e0469a4SAlexander Shishkin 12657bd1d409SAlexander Shishkin err = device_add(&src->dev); 12667bd1d409SAlexander Shishkin if (err) 12677bd1d409SAlexander Shishkin goto err; 12687bd1d409SAlexander Shishkin 1269cde4ad83SAlexander Shishkin stm_output_init(&src->output); 12707bd1d409SAlexander Shishkin spin_lock_init(&src->link_lock); 12717bd1d409SAlexander Shishkin INIT_LIST_HEAD(&src->link_entry); 12727bd1d409SAlexander Shishkin src->data = data; 12737bd1d409SAlexander Shishkin data->src = src; 12747bd1d409SAlexander Shishkin 12757bd1d409SAlexander Shishkin return 0; 12767bd1d409SAlexander Shishkin 12777bd1d409SAlexander Shishkin err: 12787bd1d409SAlexander Shishkin put_device(&src->dev); 12797bd1d409SAlexander Shishkin kfree(src); 12807bd1d409SAlexander Shishkin 12817bd1d409SAlexander Shishkin return err; 12827bd1d409SAlexander Shishkin } 12837bd1d409SAlexander Shishkin EXPORT_SYMBOL_GPL(stm_source_register_device); 12847bd1d409SAlexander Shishkin 12857bd1d409SAlexander Shishkin /** 12867bd1d409SAlexander Shishkin * stm_source_unregister_device() - unregister an stm_source device 12877bd1d409SAlexander Shishkin * @data: device description that was used to register the device 12887bd1d409SAlexander Shishkin * 12897bd1d409SAlexander Shishkin * This will remove a previously created stm_source device from the system. 12907bd1d409SAlexander Shishkin */ 12917bd1d409SAlexander Shishkin void stm_source_unregister_device(struct stm_source_data *data) 12927bd1d409SAlexander Shishkin { 12937bd1d409SAlexander Shishkin struct stm_source_device *src = data->src; 12947bd1d409SAlexander Shishkin 12957bd1d409SAlexander Shishkin stm_source_link_drop(src); 12967bd1d409SAlexander Shishkin 1297fd085bb1SAlexander Shishkin device_unregister(&src->dev); 12987bd1d409SAlexander Shishkin } 12997bd1d409SAlexander Shishkin EXPORT_SYMBOL_GPL(stm_source_unregister_device); 13007bd1d409SAlexander Shishkin 13019dfed80dSChunyan Zhang int notrace stm_source_write(struct stm_source_data *data, 13029dfed80dSChunyan Zhang unsigned int chan, 13037bd1d409SAlexander Shishkin const char *buf, size_t count) 13047bd1d409SAlexander Shishkin { 13057bd1d409SAlexander Shishkin struct stm_source_device *src = data->src; 13067bd1d409SAlexander Shishkin struct stm_device *stm; 13077bd1d409SAlexander Shishkin int idx; 13087bd1d409SAlexander Shishkin 13097bd1d409SAlexander Shishkin if (!src->output.nr_chans) 13107bd1d409SAlexander Shishkin return -ENODEV; 13117bd1d409SAlexander Shishkin 13127bd1d409SAlexander Shishkin if (chan >= src->output.nr_chans) 13137bd1d409SAlexander Shishkin return -EINVAL; 13147bd1d409SAlexander Shishkin 13157bd1d409SAlexander Shishkin idx = srcu_read_lock(&stm_source_srcu); 13167bd1d409SAlexander Shishkin 13177bd1d409SAlexander Shishkin stm = srcu_dereference(src->link, &stm_source_srcu); 13187bd1d409SAlexander Shishkin if (stm) 131924c7bcb6SAlexander Shishkin count = stm_write(stm, &src->output, chan, buf, count); 13207bd1d409SAlexander Shishkin else 13217bd1d409SAlexander Shishkin count = -ENODEV; 13227bd1d409SAlexander Shishkin 13237bd1d409SAlexander Shishkin srcu_read_unlock(&stm_source_srcu, idx); 13247bd1d409SAlexander Shishkin 13257bd1d409SAlexander Shishkin return count; 13267bd1d409SAlexander Shishkin } 13277bd1d409SAlexander Shishkin EXPORT_SYMBOL_GPL(stm_source_write); 13287bd1d409SAlexander Shishkin 13297bd1d409SAlexander Shishkin static int __init stm_core_init(void) 13307bd1d409SAlexander Shishkin { 13317bd1d409SAlexander Shishkin int err; 13327bd1d409SAlexander Shishkin 13337bd1d409SAlexander Shishkin err = class_register(&stm_class); 13347bd1d409SAlexander Shishkin if (err) 13357bd1d409SAlexander Shishkin return err; 13367bd1d409SAlexander Shishkin 13377bd1d409SAlexander Shishkin err = class_register(&stm_source_class); 13387bd1d409SAlexander Shishkin if (err) 13397bd1d409SAlexander Shishkin goto err_stm; 13407bd1d409SAlexander Shishkin 13417bd1d409SAlexander Shishkin err = stp_configfs_init(); 13427bd1d409SAlexander Shishkin if (err) 13437bd1d409SAlexander Shishkin goto err_src; 13447bd1d409SAlexander Shishkin 13457bd1d409SAlexander Shishkin init_srcu_struct(&stm_source_srcu); 1346c7fd62bcSAlexander Shishkin INIT_LIST_HEAD(&stm_pdrv_head); 1347c7fd62bcSAlexander Shishkin mutex_init(&stm_pdrv_mutex); 13487bd1d409SAlexander Shishkin 134924c7bcb6SAlexander Shishkin /* 135024c7bcb6SAlexander Shishkin * So as to not confuse existing users with a requirement 135124c7bcb6SAlexander Shishkin * to load yet another module, do it here. 135224c7bcb6SAlexander Shishkin */ 135324c7bcb6SAlexander Shishkin if (IS_ENABLED(CONFIG_STM_PROTO_BASIC)) 135424c7bcb6SAlexander Shishkin (void)request_module_nowait("stm_p_basic"); 13557bd1d409SAlexander Shishkin stm_core_up++; 13567bd1d409SAlexander Shishkin 13577bd1d409SAlexander Shishkin return 0; 13587bd1d409SAlexander Shishkin 13597bd1d409SAlexander Shishkin err_src: 13607bd1d409SAlexander Shishkin class_unregister(&stm_source_class); 13617bd1d409SAlexander Shishkin err_stm: 13627bd1d409SAlexander Shishkin class_unregister(&stm_class); 13637bd1d409SAlexander Shishkin 13647bd1d409SAlexander Shishkin return err; 13657bd1d409SAlexander Shishkin } 13667bd1d409SAlexander Shishkin 13677bd1d409SAlexander Shishkin module_init(stm_core_init); 13687bd1d409SAlexander Shishkin 13697bd1d409SAlexander Shishkin static void __exit stm_core_exit(void) 13707bd1d409SAlexander Shishkin { 13717bd1d409SAlexander Shishkin cleanup_srcu_struct(&stm_source_srcu); 13727bd1d409SAlexander Shishkin class_unregister(&stm_source_class); 13737bd1d409SAlexander Shishkin class_unregister(&stm_class); 13747bd1d409SAlexander Shishkin stp_configfs_exit(); 13757bd1d409SAlexander Shishkin } 13767bd1d409SAlexander Shishkin 13777bd1d409SAlexander Shishkin module_exit(stm_core_exit); 13787bd1d409SAlexander Shishkin 13797bd1d409SAlexander Shishkin MODULE_LICENSE("GPL v2"); 13807bd1d409SAlexander Shishkin MODULE_DESCRIPTION("System Trace Module device class"); 13817bd1d409SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); 1382