17bd1d409SAlexander Shishkin /* 27bd1d409SAlexander Shishkin * System Trace Module (STM) infrastructure 37bd1d409SAlexander Shishkin * Copyright (c) 2014, Intel Corporation. 47bd1d409SAlexander Shishkin * 57bd1d409SAlexander Shishkin * This program is free software; you can redistribute it and/or modify it 67bd1d409SAlexander Shishkin * under the terms and conditions of the GNU General Public License, 77bd1d409SAlexander Shishkin * version 2, as published by the Free Software Foundation. 87bd1d409SAlexander Shishkin * 97bd1d409SAlexander Shishkin * This program is distributed in the hope it will be useful, but WITHOUT 107bd1d409SAlexander Shishkin * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 117bd1d409SAlexander Shishkin * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 127bd1d409SAlexander Shishkin * more details. 137bd1d409SAlexander Shishkin * 147bd1d409SAlexander Shishkin * STM class implements generic infrastructure for System Trace Module devices 157bd1d409SAlexander Shishkin * as defined in MIPI STPv2 specification. 167bd1d409SAlexander Shishkin */ 177bd1d409SAlexander Shishkin 188e0469a4SAlexander Shishkin #include <linux/pm_runtime.h> 197bd1d409SAlexander Shishkin #include <linux/uaccess.h> 207bd1d409SAlexander Shishkin #include <linux/kernel.h> 217bd1d409SAlexander Shishkin #include <linux/module.h> 227bd1d409SAlexander Shishkin #include <linux/device.h> 237bd1d409SAlexander Shishkin #include <linux/compat.h> 247bd1d409SAlexander Shishkin #include <linux/kdev_t.h> 257bd1d409SAlexander Shishkin #include <linux/srcu.h> 267bd1d409SAlexander Shishkin #include <linux/slab.h> 277bd1d409SAlexander Shishkin #include <linux/stm.h> 287bd1d409SAlexander Shishkin #include <linux/fs.h> 297bd1d409SAlexander Shishkin #include <linux/mm.h> 307bd1d409SAlexander Shishkin #include "stm.h" 317bd1d409SAlexander Shishkin 327bd1d409SAlexander Shishkin #include <uapi/linux/stm.h> 337bd1d409SAlexander Shishkin 347bd1d409SAlexander Shishkin static unsigned int stm_core_up; 357bd1d409SAlexander Shishkin 367bd1d409SAlexander Shishkin /* 377bd1d409SAlexander Shishkin * The SRCU here makes sure that STM device doesn't disappear from under a 387bd1d409SAlexander Shishkin * stm_source_write() caller, which may want to have as little overhead as 397bd1d409SAlexander Shishkin * possible. 407bd1d409SAlexander Shishkin */ 417bd1d409SAlexander Shishkin static struct srcu_struct stm_source_srcu; 427bd1d409SAlexander Shishkin 437bd1d409SAlexander Shishkin static ssize_t masters_show(struct device *dev, 447bd1d409SAlexander Shishkin struct device_attribute *attr, 457bd1d409SAlexander Shishkin char *buf) 467bd1d409SAlexander Shishkin { 477bd1d409SAlexander Shishkin struct stm_device *stm = to_stm_device(dev); 487bd1d409SAlexander Shishkin int ret; 497bd1d409SAlexander Shishkin 507bd1d409SAlexander Shishkin ret = sprintf(buf, "%u %u\n", stm->data->sw_start, stm->data->sw_end); 517bd1d409SAlexander Shishkin 527bd1d409SAlexander Shishkin return ret; 537bd1d409SAlexander Shishkin } 547bd1d409SAlexander Shishkin 557bd1d409SAlexander Shishkin static DEVICE_ATTR_RO(masters); 567bd1d409SAlexander Shishkin 577bd1d409SAlexander Shishkin static ssize_t channels_show(struct device *dev, 587bd1d409SAlexander Shishkin struct device_attribute *attr, 597bd1d409SAlexander Shishkin char *buf) 607bd1d409SAlexander Shishkin { 617bd1d409SAlexander Shishkin struct stm_device *stm = to_stm_device(dev); 627bd1d409SAlexander Shishkin int ret; 637bd1d409SAlexander Shishkin 647bd1d409SAlexander Shishkin ret = sprintf(buf, "%u\n", stm->data->sw_nchannels); 657bd1d409SAlexander Shishkin 667bd1d409SAlexander Shishkin return ret; 677bd1d409SAlexander Shishkin } 687bd1d409SAlexander Shishkin 697bd1d409SAlexander Shishkin static DEVICE_ATTR_RO(channels); 707bd1d409SAlexander Shishkin 718e996a28SAlexander Shishkin static ssize_t hw_override_show(struct device *dev, 728e996a28SAlexander Shishkin struct device_attribute *attr, 738e996a28SAlexander Shishkin char *buf) 748e996a28SAlexander Shishkin { 758e996a28SAlexander Shishkin struct stm_device *stm = to_stm_device(dev); 768e996a28SAlexander Shishkin int ret; 778e996a28SAlexander Shishkin 788e996a28SAlexander Shishkin ret = sprintf(buf, "%u\n", stm->data->hw_override); 798e996a28SAlexander Shishkin 808e996a28SAlexander Shishkin return ret; 818e996a28SAlexander Shishkin } 828e996a28SAlexander Shishkin 838e996a28SAlexander Shishkin static DEVICE_ATTR_RO(hw_override); 848e996a28SAlexander Shishkin 857bd1d409SAlexander Shishkin static struct attribute *stm_attrs[] = { 867bd1d409SAlexander Shishkin &dev_attr_masters.attr, 877bd1d409SAlexander Shishkin &dev_attr_channels.attr, 888e996a28SAlexander Shishkin &dev_attr_hw_override.attr, 897bd1d409SAlexander Shishkin NULL, 907bd1d409SAlexander Shishkin }; 917bd1d409SAlexander Shishkin 927bd1d409SAlexander Shishkin ATTRIBUTE_GROUPS(stm); 937bd1d409SAlexander Shishkin 947bd1d409SAlexander Shishkin static struct class stm_class = { 957bd1d409SAlexander Shishkin .name = "stm", 967bd1d409SAlexander Shishkin .dev_groups = stm_groups, 977bd1d409SAlexander Shishkin }; 987bd1d409SAlexander Shishkin 997bd1d409SAlexander Shishkin static int stm_dev_match(struct device *dev, const void *data) 1007bd1d409SAlexander Shishkin { 1017bd1d409SAlexander Shishkin const char *name = data; 1027bd1d409SAlexander Shishkin 1037bd1d409SAlexander Shishkin return sysfs_streq(name, dev_name(dev)); 1047bd1d409SAlexander Shishkin } 1057bd1d409SAlexander Shishkin 1067bd1d409SAlexander Shishkin /** 1077bd1d409SAlexander Shishkin * stm_find_device() - find stm device by name 1087bd1d409SAlexander Shishkin * @buf: character buffer containing the name 1097bd1d409SAlexander Shishkin * 1107bd1d409SAlexander Shishkin * This is called when either policy gets assigned to an stm device or an 1117bd1d409SAlexander Shishkin * stm_source device gets linked to an stm device. 1127bd1d409SAlexander Shishkin * 1137bd1d409SAlexander Shishkin * This grabs device's reference (get_device()) and module reference, both 1147bd1d409SAlexander Shishkin * of which the calling path needs to make sure to drop with stm_put_device(). 1157bd1d409SAlexander Shishkin * 1167bd1d409SAlexander Shishkin * Return: stm device pointer or null if lookup failed. 1177bd1d409SAlexander Shishkin */ 1187bd1d409SAlexander Shishkin struct stm_device *stm_find_device(const char *buf) 1197bd1d409SAlexander Shishkin { 1207bd1d409SAlexander Shishkin struct stm_device *stm; 1217bd1d409SAlexander Shishkin struct device *dev; 1227bd1d409SAlexander Shishkin 1237bd1d409SAlexander Shishkin if (!stm_core_up) 1247bd1d409SAlexander Shishkin return NULL; 1257bd1d409SAlexander Shishkin 1267bd1d409SAlexander Shishkin dev = class_find_device(&stm_class, NULL, buf, stm_dev_match); 1277bd1d409SAlexander Shishkin if (!dev) 1287bd1d409SAlexander Shishkin return NULL; 1297bd1d409SAlexander Shishkin 1307bd1d409SAlexander Shishkin stm = to_stm_device(dev); 1317bd1d409SAlexander Shishkin if (!try_module_get(stm->owner)) { 132f7c81c71SAlexander Shishkin /* matches class_find_device() above */ 1337bd1d409SAlexander Shishkin put_device(dev); 1347bd1d409SAlexander Shishkin return NULL; 1357bd1d409SAlexander Shishkin } 1367bd1d409SAlexander Shishkin 1377bd1d409SAlexander Shishkin return stm; 1387bd1d409SAlexander Shishkin } 1397bd1d409SAlexander Shishkin 1407bd1d409SAlexander Shishkin /** 1417bd1d409SAlexander Shishkin * stm_put_device() - drop references on the stm device 1427bd1d409SAlexander Shishkin * @stm: stm device, previously acquired by stm_find_device() 1437bd1d409SAlexander Shishkin * 1447bd1d409SAlexander Shishkin * This drops the module reference and device reference taken by 145f7c81c71SAlexander Shishkin * stm_find_device() or stm_char_open(). 1467bd1d409SAlexander Shishkin */ 1477bd1d409SAlexander Shishkin void stm_put_device(struct stm_device *stm) 1487bd1d409SAlexander Shishkin { 1497bd1d409SAlexander Shishkin module_put(stm->owner); 1507bd1d409SAlexander Shishkin put_device(&stm->dev); 1517bd1d409SAlexander Shishkin } 1527bd1d409SAlexander Shishkin 1537bd1d409SAlexander Shishkin /* 1547bd1d409SAlexander Shishkin * Internally we only care about software-writable masters here, that is the 1557bd1d409SAlexander Shishkin * ones in the range [stm_data->sw_start..stm_data..sw_end], however we need 1567bd1d409SAlexander Shishkin * original master numbers to be visible externally, since they are the ones 1577bd1d409SAlexander Shishkin * that will appear in the STP stream. Thus, the internal bookkeeping uses 1587bd1d409SAlexander Shishkin * $master - stm_data->sw_start to reference master descriptors and such. 1597bd1d409SAlexander Shishkin */ 1607bd1d409SAlexander Shishkin 1617bd1d409SAlexander Shishkin #define __stm_master(_s, _m) \ 1627bd1d409SAlexander Shishkin ((_s)->masters[(_m) - (_s)->data->sw_start]) 1637bd1d409SAlexander Shishkin 1647bd1d409SAlexander Shishkin static inline struct stp_master * 1657bd1d409SAlexander Shishkin stm_master(struct stm_device *stm, unsigned int idx) 1667bd1d409SAlexander Shishkin { 1677bd1d409SAlexander Shishkin if (idx < stm->data->sw_start || idx > stm->data->sw_end) 1687bd1d409SAlexander Shishkin return NULL; 1697bd1d409SAlexander Shishkin 1707bd1d409SAlexander Shishkin return __stm_master(stm, idx); 1717bd1d409SAlexander Shishkin } 1727bd1d409SAlexander Shishkin 1737bd1d409SAlexander Shishkin static int stp_master_alloc(struct stm_device *stm, unsigned int idx) 1747bd1d409SAlexander Shishkin { 1757bd1d409SAlexander Shishkin struct stp_master *master; 1767bd1d409SAlexander Shishkin size_t size; 1777bd1d409SAlexander Shishkin 1787bd1d409SAlexander Shishkin size = ALIGN(stm->data->sw_nchannels, 8) / 8; 1797bd1d409SAlexander Shishkin size += sizeof(struct stp_master); 1807bd1d409SAlexander Shishkin master = kzalloc(size, GFP_ATOMIC); 1817bd1d409SAlexander Shishkin if (!master) 1827bd1d409SAlexander Shishkin return -ENOMEM; 1837bd1d409SAlexander Shishkin 1847bd1d409SAlexander Shishkin master->nr_free = stm->data->sw_nchannels; 1857bd1d409SAlexander Shishkin __stm_master(stm, idx) = master; 1867bd1d409SAlexander Shishkin 1877bd1d409SAlexander Shishkin return 0; 1887bd1d409SAlexander Shishkin } 1897bd1d409SAlexander Shishkin 1907bd1d409SAlexander Shishkin static void stp_master_free(struct stm_device *stm, unsigned int idx) 1917bd1d409SAlexander Shishkin { 1927bd1d409SAlexander Shishkin struct stp_master *master = stm_master(stm, idx); 1937bd1d409SAlexander Shishkin 1947bd1d409SAlexander Shishkin if (!master) 1957bd1d409SAlexander Shishkin return; 1967bd1d409SAlexander Shishkin 1977bd1d409SAlexander Shishkin __stm_master(stm, idx) = NULL; 1987bd1d409SAlexander Shishkin kfree(master); 1997bd1d409SAlexander Shishkin } 2007bd1d409SAlexander Shishkin 2017bd1d409SAlexander Shishkin static void stm_output_claim(struct stm_device *stm, struct stm_output *output) 2027bd1d409SAlexander Shishkin { 2037bd1d409SAlexander Shishkin struct stp_master *master = stm_master(stm, output->master); 2047bd1d409SAlexander Shishkin 205cde4ad83SAlexander Shishkin lockdep_assert_held(&stm->mc_lock); 206cde4ad83SAlexander Shishkin lockdep_assert_held(&output->lock); 207cde4ad83SAlexander Shishkin 2087bd1d409SAlexander Shishkin if (WARN_ON_ONCE(master->nr_free < output->nr_chans)) 2097bd1d409SAlexander Shishkin return; 2107bd1d409SAlexander Shishkin 2117bd1d409SAlexander Shishkin bitmap_allocate_region(&master->chan_map[0], output->channel, 2127bd1d409SAlexander Shishkin ilog2(output->nr_chans)); 2137bd1d409SAlexander Shishkin 2147bd1d409SAlexander Shishkin master->nr_free -= output->nr_chans; 2157bd1d409SAlexander Shishkin } 2167bd1d409SAlexander Shishkin 2177bd1d409SAlexander Shishkin static void 2187bd1d409SAlexander Shishkin stm_output_disclaim(struct stm_device *stm, struct stm_output *output) 2197bd1d409SAlexander Shishkin { 2207bd1d409SAlexander Shishkin struct stp_master *master = stm_master(stm, output->master); 2217bd1d409SAlexander Shishkin 222cde4ad83SAlexander Shishkin lockdep_assert_held(&stm->mc_lock); 223cde4ad83SAlexander Shishkin lockdep_assert_held(&output->lock); 224cde4ad83SAlexander Shishkin 2257bd1d409SAlexander Shishkin bitmap_release_region(&master->chan_map[0], output->channel, 2267bd1d409SAlexander Shishkin ilog2(output->nr_chans)); 2277bd1d409SAlexander Shishkin 2287bd1d409SAlexander Shishkin output->nr_chans = 0; 2297bd1d409SAlexander Shishkin master->nr_free += output->nr_chans; 2307bd1d409SAlexander Shishkin } 2317bd1d409SAlexander Shishkin 2327bd1d409SAlexander Shishkin /* 2337bd1d409SAlexander Shishkin * This is like bitmap_find_free_region(), except it can ignore @start bits 2347bd1d409SAlexander Shishkin * at the beginning. 2357bd1d409SAlexander Shishkin */ 2367bd1d409SAlexander Shishkin static int find_free_channels(unsigned long *bitmap, unsigned int start, 2377bd1d409SAlexander Shishkin unsigned int end, unsigned int width) 2387bd1d409SAlexander Shishkin { 2397bd1d409SAlexander Shishkin unsigned int pos; 2407bd1d409SAlexander Shishkin int i; 2417bd1d409SAlexander Shishkin 2427bd1d409SAlexander Shishkin for (pos = start; pos < end + 1; pos = ALIGN(pos, width)) { 2437bd1d409SAlexander Shishkin pos = find_next_zero_bit(bitmap, end + 1, pos); 2447bd1d409SAlexander Shishkin if (pos + width > end + 1) 2457bd1d409SAlexander Shishkin break; 2467bd1d409SAlexander Shishkin 2477bd1d409SAlexander Shishkin if (pos & (width - 1)) 2487bd1d409SAlexander Shishkin continue; 2497bd1d409SAlexander Shishkin 2507bd1d409SAlexander Shishkin for (i = 1; i < width && !test_bit(pos + i, bitmap); i++) 2517bd1d409SAlexander Shishkin ; 2527bd1d409SAlexander Shishkin if (i == width) 2537bd1d409SAlexander Shishkin return pos; 2547bd1d409SAlexander Shishkin } 2557bd1d409SAlexander Shishkin 2567bd1d409SAlexander Shishkin return -1; 2577bd1d409SAlexander Shishkin } 2587bd1d409SAlexander Shishkin 259f45f40adSLucas Tanure static int 2607bd1d409SAlexander Shishkin stm_find_master_chan(struct stm_device *stm, unsigned int width, 2617bd1d409SAlexander Shishkin unsigned int *mstart, unsigned int mend, 2627bd1d409SAlexander Shishkin unsigned int *cstart, unsigned int cend) 2637bd1d409SAlexander Shishkin { 2647bd1d409SAlexander Shishkin struct stp_master *master; 2657bd1d409SAlexander Shishkin unsigned int midx; 2667bd1d409SAlexander Shishkin int pos, err; 2677bd1d409SAlexander Shishkin 2687bd1d409SAlexander Shishkin for (midx = *mstart; midx <= mend; midx++) { 2697bd1d409SAlexander Shishkin if (!stm_master(stm, midx)) { 2707bd1d409SAlexander Shishkin err = stp_master_alloc(stm, midx); 2717bd1d409SAlexander Shishkin if (err) 2727bd1d409SAlexander Shishkin return err; 2737bd1d409SAlexander Shishkin } 2747bd1d409SAlexander Shishkin 2757bd1d409SAlexander Shishkin master = stm_master(stm, midx); 2767bd1d409SAlexander Shishkin 2777bd1d409SAlexander Shishkin if (!master->nr_free) 2787bd1d409SAlexander Shishkin continue; 2797bd1d409SAlexander Shishkin 2807bd1d409SAlexander Shishkin pos = find_free_channels(master->chan_map, *cstart, cend, 2817bd1d409SAlexander Shishkin width); 2827bd1d409SAlexander Shishkin if (pos < 0) 2837bd1d409SAlexander Shishkin continue; 2847bd1d409SAlexander Shishkin 2857bd1d409SAlexander Shishkin *mstart = midx; 2867bd1d409SAlexander Shishkin *cstart = pos; 2877bd1d409SAlexander Shishkin return 0; 2887bd1d409SAlexander Shishkin } 2897bd1d409SAlexander Shishkin 2907bd1d409SAlexander Shishkin return -ENOSPC; 2917bd1d409SAlexander Shishkin } 2927bd1d409SAlexander Shishkin 2937bd1d409SAlexander Shishkin static int stm_output_assign(struct stm_device *stm, unsigned int width, 2947bd1d409SAlexander Shishkin struct stp_policy_node *policy_node, 2957bd1d409SAlexander Shishkin struct stm_output *output) 2967bd1d409SAlexander Shishkin { 2977bd1d409SAlexander Shishkin unsigned int midx, cidx, mend, cend; 2987bd1d409SAlexander Shishkin int ret = -EINVAL; 2997bd1d409SAlexander Shishkin 3007bd1d409SAlexander Shishkin if (width > stm->data->sw_nchannels) 3017bd1d409SAlexander Shishkin return -EINVAL; 3027bd1d409SAlexander Shishkin 3037bd1d409SAlexander Shishkin if (policy_node) { 3047bd1d409SAlexander Shishkin stp_policy_node_get_ranges(policy_node, 3057bd1d409SAlexander Shishkin &midx, &mend, &cidx, &cend); 3067bd1d409SAlexander Shishkin } else { 3077bd1d409SAlexander Shishkin midx = stm->data->sw_start; 3087bd1d409SAlexander Shishkin cidx = 0; 3097bd1d409SAlexander Shishkin mend = stm->data->sw_end; 3107bd1d409SAlexander Shishkin cend = stm->data->sw_nchannels - 1; 3117bd1d409SAlexander Shishkin } 3127bd1d409SAlexander Shishkin 3137bd1d409SAlexander Shishkin spin_lock(&stm->mc_lock); 314cde4ad83SAlexander Shishkin spin_lock(&output->lock); 3157bd1d409SAlexander Shishkin /* output is already assigned -- shouldn't happen */ 3167bd1d409SAlexander Shishkin if (WARN_ON_ONCE(output->nr_chans)) 3177bd1d409SAlexander Shishkin goto unlock; 3187bd1d409SAlexander Shishkin 3197bd1d409SAlexander Shishkin ret = stm_find_master_chan(stm, width, &midx, mend, &cidx, cend); 320f45f40adSLucas Tanure if (ret < 0) 3217bd1d409SAlexander Shishkin goto unlock; 3227bd1d409SAlexander Shishkin 3237bd1d409SAlexander Shishkin output->master = midx; 3247bd1d409SAlexander Shishkin output->channel = cidx; 3257bd1d409SAlexander Shishkin output->nr_chans = width; 3267bd1d409SAlexander Shishkin stm_output_claim(stm, output); 3277bd1d409SAlexander Shishkin dev_dbg(&stm->dev, "assigned %u:%u (+%u)\n", midx, cidx, width); 3287bd1d409SAlexander Shishkin 3297bd1d409SAlexander Shishkin ret = 0; 3307bd1d409SAlexander Shishkin unlock: 331cde4ad83SAlexander Shishkin spin_unlock(&output->lock); 3327bd1d409SAlexander Shishkin spin_unlock(&stm->mc_lock); 3337bd1d409SAlexander Shishkin 3347bd1d409SAlexander Shishkin return ret; 3357bd1d409SAlexander Shishkin } 3367bd1d409SAlexander Shishkin 3377bd1d409SAlexander Shishkin static void stm_output_free(struct stm_device *stm, struct stm_output *output) 3387bd1d409SAlexander Shishkin { 3397bd1d409SAlexander Shishkin spin_lock(&stm->mc_lock); 340cde4ad83SAlexander Shishkin spin_lock(&output->lock); 3417bd1d409SAlexander Shishkin if (output->nr_chans) 3427bd1d409SAlexander Shishkin stm_output_disclaim(stm, output); 343cde4ad83SAlexander Shishkin spin_unlock(&output->lock); 3447bd1d409SAlexander Shishkin spin_unlock(&stm->mc_lock); 3457bd1d409SAlexander Shishkin } 3467bd1d409SAlexander Shishkin 347cde4ad83SAlexander Shishkin static void stm_output_init(struct stm_output *output) 348cde4ad83SAlexander Shishkin { 349cde4ad83SAlexander Shishkin spin_lock_init(&output->lock); 350cde4ad83SAlexander Shishkin } 351cde4ad83SAlexander Shishkin 3527bd1d409SAlexander Shishkin static int major_match(struct device *dev, const void *data) 3537bd1d409SAlexander Shishkin { 3547bd1d409SAlexander Shishkin unsigned int major = *(unsigned int *)data; 3557bd1d409SAlexander Shishkin 3567bd1d409SAlexander Shishkin return MAJOR(dev->devt) == major; 3577bd1d409SAlexander Shishkin } 3587bd1d409SAlexander Shishkin 3597bd1d409SAlexander Shishkin static int stm_char_open(struct inode *inode, struct file *file) 3607bd1d409SAlexander Shishkin { 3617bd1d409SAlexander Shishkin struct stm_file *stmf; 3627bd1d409SAlexander Shishkin struct device *dev; 3637bd1d409SAlexander Shishkin unsigned int major = imajor(inode); 364a0ebf519SJohan Hovold int err = -ENOMEM; 3657bd1d409SAlexander Shishkin 3667bd1d409SAlexander Shishkin dev = class_find_device(&stm_class, NULL, &major, major_match); 3677bd1d409SAlexander Shishkin if (!dev) 3687bd1d409SAlexander Shishkin return -ENODEV; 3697bd1d409SAlexander Shishkin 3707bd1d409SAlexander Shishkin stmf = kzalloc(sizeof(*stmf), GFP_KERNEL); 3717bd1d409SAlexander Shishkin if (!stmf) 372a0ebf519SJohan Hovold goto err_put_device; 3737bd1d409SAlexander Shishkin 374a0ebf519SJohan Hovold err = -ENODEV; 375cde4ad83SAlexander Shishkin stm_output_init(&stmf->output); 3767bd1d409SAlexander Shishkin stmf->stm = to_stm_device(dev); 3777bd1d409SAlexander Shishkin 3787bd1d409SAlexander Shishkin if (!try_module_get(stmf->stm->owner)) 3797bd1d409SAlexander Shishkin goto err_free; 3807bd1d409SAlexander Shishkin 3817bd1d409SAlexander Shishkin file->private_data = stmf; 3827bd1d409SAlexander Shishkin 3837bd1d409SAlexander Shishkin return nonseekable_open(inode, file); 3847bd1d409SAlexander Shishkin 3857bd1d409SAlexander Shishkin err_free: 386a0ebf519SJohan Hovold kfree(stmf); 387a0ebf519SJohan Hovold err_put_device: 388f7c81c71SAlexander Shishkin /* matches class_find_device() above */ 389f7c81c71SAlexander Shishkin put_device(dev); 3907bd1d409SAlexander Shishkin 3917bd1d409SAlexander Shishkin return err; 3927bd1d409SAlexander Shishkin } 3937bd1d409SAlexander Shishkin 3947bd1d409SAlexander Shishkin static int stm_char_release(struct inode *inode, struct file *file) 3957bd1d409SAlexander Shishkin { 3967bd1d409SAlexander Shishkin struct stm_file *stmf = file->private_data; 397cc842407SAlexander Shishkin struct stm_device *stm = stmf->stm; 3987bd1d409SAlexander Shishkin 399cc842407SAlexander Shishkin if (stm->data->unlink) 400cc842407SAlexander Shishkin stm->data->unlink(stm->data, stmf->output.master, 401cc842407SAlexander Shishkin stmf->output.channel); 402cc842407SAlexander Shishkin 403cc842407SAlexander Shishkin stm_output_free(stm, &stmf->output); 404f7c81c71SAlexander Shishkin 405f7c81c71SAlexander Shishkin /* 406f7c81c71SAlexander Shishkin * matches the stm_char_open()'s 407f7c81c71SAlexander Shishkin * class_find_device() + try_module_get() 408f7c81c71SAlexander Shishkin */ 409cc842407SAlexander Shishkin stm_put_device(stm); 4107bd1d409SAlexander Shishkin kfree(stmf); 4117bd1d409SAlexander Shishkin 4127bd1d409SAlexander Shishkin return 0; 4137bd1d409SAlexander Shishkin } 4147bd1d409SAlexander Shishkin 4157bd1d409SAlexander Shishkin static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width) 4167bd1d409SAlexander Shishkin { 4177bd1d409SAlexander Shishkin struct stm_device *stm = stmf->stm; 4187bd1d409SAlexander Shishkin int ret; 4197bd1d409SAlexander Shishkin 4207bd1d409SAlexander Shishkin stmf->policy_node = stp_policy_node_lookup(stm, id); 4217bd1d409SAlexander Shishkin 4227bd1d409SAlexander Shishkin ret = stm_output_assign(stm, width, stmf->policy_node, &stmf->output); 4237bd1d409SAlexander Shishkin 4247bd1d409SAlexander Shishkin if (stmf->policy_node) 4257bd1d409SAlexander Shishkin stp_policy_node_put(stmf->policy_node); 4267bd1d409SAlexander Shishkin 4277bd1d409SAlexander Shishkin return ret; 4287bd1d409SAlexander Shishkin } 4297bd1d409SAlexander Shishkin 430f8560a9bSAlexander Shishkin static ssize_t stm_write(struct stm_data *data, unsigned int master, 4317bd1d409SAlexander Shishkin unsigned int channel, const char *buf, size_t count) 4327bd1d409SAlexander Shishkin { 4337bd1d409SAlexander Shishkin unsigned int flags = STP_PACKET_TIMESTAMPED; 4347bd1d409SAlexander Shishkin const unsigned char *p = buf, nil = 0; 4357bd1d409SAlexander Shishkin size_t pos; 4367bd1d409SAlexander Shishkin ssize_t sz; 4377bd1d409SAlexander Shishkin 4387bd1d409SAlexander Shishkin for (pos = 0, p = buf; count > pos; pos += sz, p += sz) { 4397bd1d409SAlexander Shishkin sz = min_t(unsigned int, count - pos, 8); 4407bd1d409SAlexander Shishkin sz = data->packet(data, master, channel, STP_PACKET_DATA, flags, 4417bd1d409SAlexander Shishkin sz, p); 4427bd1d409SAlexander Shishkin flags = 0; 443f8560a9bSAlexander Shishkin 444f8560a9bSAlexander Shishkin if (sz < 0) 445f8560a9bSAlexander Shishkin break; 4467bd1d409SAlexander Shishkin } 4477bd1d409SAlexander Shishkin 4487bd1d409SAlexander Shishkin data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil); 449f8560a9bSAlexander Shishkin 450f8560a9bSAlexander Shishkin return pos; 4517bd1d409SAlexander Shishkin } 4527bd1d409SAlexander Shishkin 4537bd1d409SAlexander Shishkin static ssize_t stm_char_write(struct file *file, const char __user *buf, 4547bd1d409SAlexander Shishkin size_t count, loff_t *ppos) 4557bd1d409SAlexander Shishkin { 4567bd1d409SAlexander Shishkin struct stm_file *stmf = file->private_data; 4577bd1d409SAlexander Shishkin struct stm_device *stm = stmf->stm; 4587bd1d409SAlexander Shishkin char *kbuf; 4597bd1d409SAlexander Shishkin int err; 4607bd1d409SAlexander Shishkin 461f08b1826SAlexander Shishkin if (count + 1 > PAGE_SIZE) 462f08b1826SAlexander Shishkin count = PAGE_SIZE - 1; 463f08b1826SAlexander Shishkin 4647bd1d409SAlexander Shishkin /* 4657bd1d409SAlexander Shishkin * if no m/c have been assigned to this writer up to this 4667bd1d409SAlexander Shishkin * point, use "default" policy entry 4677bd1d409SAlexander Shishkin */ 4687bd1d409SAlexander Shishkin if (!stmf->output.nr_chans) { 4697bd1d409SAlexander Shishkin err = stm_file_assign(stmf, "default", 1); 4707bd1d409SAlexander Shishkin /* 4717bd1d409SAlexander Shishkin * EBUSY means that somebody else just assigned this 4727bd1d409SAlexander Shishkin * output, which is just fine for write() 4737bd1d409SAlexander Shishkin */ 4747bd1d409SAlexander Shishkin if (err && err != -EBUSY) 4757bd1d409SAlexander Shishkin return err; 4767bd1d409SAlexander Shishkin } 4777bd1d409SAlexander Shishkin 4787bd1d409SAlexander Shishkin kbuf = kmalloc(count + 1, GFP_KERNEL); 4797bd1d409SAlexander Shishkin if (!kbuf) 4807bd1d409SAlexander Shishkin return -ENOMEM; 4817bd1d409SAlexander Shishkin 4827bd1d409SAlexander Shishkin err = copy_from_user(kbuf, buf, count); 4837bd1d409SAlexander Shishkin if (err) { 4847bd1d409SAlexander Shishkin kfree(kbuf); 4857bd1d409SAlexander Shishkin return -EFAULT; 4867bd1d409SAlexander Shishkin } 4877bd1d409SAlexander Shishkin 4888e0469a4SAlexander Shishkin pm_runtime_get_sync(&stm->dev); 4898e0469a4SAlexander Shishkin 490f8560a9bSAlexander Shishkin count = stm_write(stm->data, stmf->output.master, stmf->output.channel, 491f8560a9bSAlexander Shishkin kbuf, count); 4927bd1d409SAlexander Shishkin 4938e0469a4SAlexander Shishkin pm_runtime_mark_last_busy(&stm->dev); 4948e0469a4SAlexander Shishkin pm_runtime_put_autosuspend(&stm->dev); 4957bd1d409SAlexander Shishkin kfree(kbuf); 4967bd1d409SAlexander Shishkin 4977bd1d409SAlexander Shishkin return count; 4987bd1d409SAlexander Shishkin } 4997bd1d409SAlexander Shishkin 5008e0469a4SAlexander Shishkin static void stm_mmap_open(struct vm_area_struct *vma) 5018e0469a4SAlexander Shishkin { 5028e0469a4SAlexander Shishkin struct stm_file *stmf = vma->vm_file->private_data; 5038e0469a4SAlexander Shishkin struct stm_device *stm = stmf->stm; 5048e0469a4SAlexander Shishkin 5058e0469a4SAlexander Shishkin pm_runtime_get(&stm->dev); 5068e0469a4SAlexander Shishkin } 5078e0469a4SAlexander Shishkin 5088e0469a4SAlexander Shishkin static void stm_mmap_close(struct vm_area_struct *vma) 5098e0469a4SAlexander Shishkin { 5108e0469a4SAlexander Shishkin struct stm_file *stmf = vma->vm_file->private_data; 5118e0469a4SAlexander Shishkin struct stm_device *stm = stmf->stm; 5128e0469a4SAlexander Shishkin 5138e0469a4SAlexander Shishkin pm_runtime_mark_last_busy(&stm->dev); 5148e0469a4SAlexander Shishkin pm_runtime_put_autosuspend(&stm->dev); 5158e0469a4SAlexander Shishkin } 5168e0469a4SAlexander Shishkin 5178e0469a4SAlexander Shishkin static const struct vm_operations_struct stm_mmap_vmops = { 5188e0469a4SAlexander Shishkin .open = stm_mmap_open, 5198e0469a4SAlexander Shishkin .close = stm_mmap_close, 5208e0469a4SAlexander Shishkin }; 5218e0469a4SAlexander Shishkin 5227bd1d409SAlexander Shishkin static int stm_char_mmap(struct file *file, struct vm_area_struct *vma) 5237bd1d409SAlexander Shishkin { 5247bd1d409SAlexander Shishkin struct stm_file *stmf = file->private_data; 5257bd1d409SAlexander Shishkin struct stm_device *stm = stmf->stm; 5267bd1d409SAlexander Shishkin unsigned long size, phys; 5277bd1d409SAlexander Shishkin 5287bd1d409SAlexander Shishkin if (!stm->data->mmio_addr) 5297bd1d409SAlexander Shishkin return -EOPNOTSUPP; 5307bd1d409SAlexander Shishkin 5317bd1d409SAlexander Shishkin if (vma->vm_pgoff) 5327bd1d409SAlexander Shishkin return -EINVAL; 5337bd1d409SAlexander Shishkin 5347bd1d409SAlexander Shishkin size = vma->vm_end - vma->vm_start; 5357bd1d409SAlexander Shishkin 5367bd1d409SAlexander Shishkin if (stmf->output.nr_chans * stm->data->sw_mmiosz != size) 5377bd1d409SAlexander Shishkin return -EINVAL; 5387bd1d409SAlexander Shishkin 5397bd1d409SAlexander Shishkin phys = stm->data->mmio_addr(stm->data, stmf->output.master, 5407bd1d409SAlexander Shishkin stmf->output.channel, 5417bd1d409SAlexander Shishkin stmf->output.nr_chans); 5427bd1d409SAlexander Shishkin 5437bd1d409SAlexander Shishkin if (!phys) 5447bd1d409SAlexander Shishkin return -EINVAL; 5457bd1d409SAlexander Shishkin 5468e0469a4SAlexander Shishkin pm_runtime_get_sync(&stm->dev); 5478e0469a4SAlexander Shishkin 5487bd1d409SAlexander Shishkin vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 5497bd1d409SAlexander Shishkin vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; 5508e0469a4SAlexander Shishkin vma->vm_ops = &stm_mmap_vmops; 5517bd1d409SAlexander Shishkin vm_iomap_memory(vma, phys, size); 5527bd1d409SAlexander Shishkin 5537bd1d409SAlexander Shishkin return 0; 5547bd1d409SAlexander Shishkin } 5557bd1d409SAlexander Shishkin 5567bd1d409SAlexander Shishkin static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) 5577bd1d409SAlexander Shishkin { 5587bd1d409SAlexander Shishkin struct stm_device *stm = stmf->stm; 5597bd1d409SAlexander Shishkin struct stp_policy_id *id; 5607bd1d409SAlexander Shishkin int ret = -EINVAL; 5617bd1d409SAlexander Shishkin u32 size; 5627bd1d409SAlexander Shishkin 5637bd1d409SAlexander Shishkin if (stmf->output.nr_chans) 5647bd1d409SAlexander Shishkin return -EBUSY; 5657bd1d409SAlexander Shishkin 5667bd1d409SAlexander Shishkin if (copy_from_user(&size, arg, sizeof(size))) 5677bd1d409SAlexander Shishkin return -EFAULT; 5687bd1d409SAlexander Shishkin 5697bd1d409SAlexander Shishkin if (size >= PATH_MAX + sizeof(*id)) 5707bd1d409SAlexander Shishkin return -EINVAL; 5717bd1d409SAlexander Shishkin 5727bd1d409SAlexander Shishkin /* 5737bd1d409SAlexander Shishkin * size + 1 to make sure the .id string at the bottom is terminated, 5747bd1d409SAlexander Shishkin * which is also why memdup_user() is not useful here 5757bd1d409SAlexander Shishkin */ 5767bd1d409SAlexander Shishkin id = kzalloc(size + 1, GFP_KERNEL); 5777bd1d409SAlexander Shishkin if (!id) 5787bd1d409SAlexander Shishkin return -ENOMEM; 5797bd1d409SAlexander Shishkin 5807bd1d409SAlexander Shishkin if (copy_from_user(id, arg, size)) { 5817bd1d409SAlexander Shishkin ret = -EFAULT; 5827bd1d409SAlexander Shishkin goto err_free; 5837bd1d409SAlexander Shishkin } 5847bd1d409SAlexander Shishkin 5857bd1d409SAlexander Shishkin if (id->__reserved_0 || id->__reserved_1) 5867bd1d409SAlexander Shishkin goto err_free; 5877bd1d409SAlexander Shishkin 5887bd1d409SAlexander Shishkin if (id->width < 1 || 5897bd1d409SAlexander Shishkin id->width > PAGE_SIZE / stm->data->sw_mmiosz) 5907bd1d409SAlexander Shishkin goto err_free; 5917bd1d409SAlexander Shishkin 5927bd1d409SAlexander Shishkin ret = stm_file_assign(stmf, id->id, id->width); 5937bd1d409SAlexander Shishkin if (ret) 5947bd1d409SAlexander Shishkin goto err_free; 5957bd1d409SAlexander Shishkin 5967bd1d409SAlexander Shishkin if (stm->data->link) 5977bd1d409SAlexander Shishkin ret = stm->data->link(stm->data, stmf->output.master, 5987bd1d409SAlexander Shishkin stmf->output.channel); 5997bd1d409SAlexander Shishkin 600f7c81c71SAlexander Shishkin if (ret) 6017bd1d409SAlexander Shishkin stm_output_free(stmf->stm, &stmf->output); 6027bd1d409SAlexander Shishkin 6037bd1d409SAlexander Shishkin err_free: 6047bd1d409SAlexander Shishkin kfree(id); 6057bd1d409SAlexander Shishkin 6067bd1d409SAlexander Shishkin return ret; 6077bd1d409SAlexander Shishkin } 6087bd1d409SAlexander Shishkin 6097bd1d409SAlexander Shishkin static int stm_char_policy_get_ioctl(struct stm_file *stmf, void __user *arg) 6107bd1d409SAlexander Shishkin { 6117bd1d409SAlexander Shishkin struct stp_policy_id id = { 6127bd1d409SAlexander Shishkin .size = sizeof(id), 6137bd1d409SAlexander Shishkin .master = stmf->output.master, 6147bd1d409SAlexander Shishkin .channel = stmf->output.channel, 6157bd1d409SAlexander Shishkin .width = stmf->output.nr_chans, 6167bd1d409SAlexander Shishkin .__reserved_0 = 0, 6177bd1d409SAlexander Shishkin .__reserved_1 = 0, 6187bd1d409SAlexander Shishkin }; 6197bd1d409SAlexander Shishkin 6207bd1d409SAlexander Shishkin return copy_to_user(arg, &id, id.size) ? -EFAULT : 0; 6217bd1d409SAlexander Shishkin } 6227bd1d409SAlexander Shishkin 6237bd1d409SAlexander Shishkin static long 6247bd1d409SAlexander Shishkin stm_char_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 6257bd1d409SAlexander Shishkin { 6267bd1d409SAlexander Shishkin struct stm_file *stmf = file->private_data; 6277bd1d409SAlexander Shishkin struct stm_data *stm_data = stmf->stm->data; 6287bd1d409SAlexander Shishkin int err = -ENOTTY; 6297bd1d409SAlexander Shishkin u64 options; 6307bd1d409SAlexander Shishkin 6317bd1d409SAlexander Shishkin switch (cmd) { 6327bd1d409SAlexander Shishkin case STP_POLICY_ID_SET: 6337bd1d409SAlexander Shishkin err = stm_char_policy_set_ioctl(stmf, (void __user *)arg); 6347bd1d409SAlexander Shishkin if (err) 6357bd1d409SAlexander Shishkin return err; 6367bd1d409SAlexander Shishkin 6377bd1d409SAlexander Shishkin return stm_char_policy_get_ioctl(stmf, (void __user *)arg); 6387bd1d409SAlexander Shishkin 6397bd1d409SAlexander Shishkin case STP_POLICY_ID_GET: 6407bd1d409SAlexander Shishkin return stm_char_policy_get_ioctl(stmf, (void __user *)arg); 6417bd1d409SAlexander Shishkin 6427bd1d409SAlexander Shishkin case STP_SET_OPTIONS: 6437bd1d409SAlexander Shishkin if (copy_from_user(&options, (u64 __user *)arg, sizeof(u64))) 6447bd1d409SAlexander Shishkin return -EFAULT; 6457bd1d409SAlexander Shishkin 6467bd1d409SAlexander Shishkin if (stm_data->set_options) 6477bd1d409SAlexander Shishkin err = stm_data->set_options(stm_data, 6487bd1d409SAlexander Shishkin stmf->output.master, 6497bd1d409SAlexander Shishkin stmf->output.channel, 6507bd1d409SAlexander Shishkin stmf->output.nr_chans, 6517bd1d409SAlexander Shishkin options); 6527bd1d409SAlexander Shishkin 6537bd1d409SAlexander Shishkin break; 6547bd1d409SAlexander Shishkin default: 6557bd1d409SAlexander Shishkin break; 6567bd1d409SAlexander Shishkin } 6577bd1d409SAlexander Shishkin 6587bd1d409SAlexander Shishkin return err; 6597bd1d409SAlexander Shishkin } 6607bd1d409SAlexander Shishkin 6617bd1d409SAlexander Shishkin #ifdef CONFIG_COMPAT 6627bd1d409SAlexander Shishkin static long 6637bd1d409SAlexander Shishkin stm_char_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 6647bd1d409SAlexander Shishkin { 6657bd1d409SAlexander Shishkin return stm_char_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); 6667bd1d409SAlexander Shishkin } 6677bd1d409SAlexander Shishkin #else 6687bd1d409SAlexander Shishkin #define stm_char_compat_ioctl NULL 6697bd1d409SAlexander Shishkin #endif 6707bd1d409SAlexander Shishkin 6717bd1d409SAlexander Shishkin static const struct file_operations stm_fops = { 6727bd1d409SAlexander Shishkin .open = stm_char_open, 6737bd1d409SAlexander Shishkin .release = stm_char_release, 6747bd1d409SAlexander Shishkin .write = stm_char_write, 6757bd1d409SAlexander Shishkin .mmap = stm_char_mmap, 6767bd1d409SAlexander Shishkin .unlocked_ioctl = stm_char_ioctl, 6777bd1d409SAlexander Shishkin .compat_ioctl = stm_char_compat_ioctl, 6787bd1d409SAlexander Shishkin .llseek = no_llseek, 6797bd1d409SAlexander Shishkin }; 6807bd1d409SAlexander Shishkin 6817bd1d409SAlexander Shishkin static void stm_device_release(struct device *dev) 6827bd1d409SAlexander Shishkin { 6837bd1d409SAlexander Shishkin struct stm_device *stm = to_stm_device(dev); 6847bd1d409SAlexander Shishkin 6857bd1d409SAlexander Shishkin kfree(stm); 6867bd1d409SAlexander Shishkin } 6877bd1d409SAlexander Shishkin 6887bd1d409SAlexander Shishkin int stm_register_device(struct device *parent, struct stm_data *stm_data, 6897bd1d409SAlexander Shishkin struct module *owner) 6907bd1d409SAlexander Shishkin { 6917bd1d409SAlexander Shishkin struct stm_device *stm; 6927bd1d409SAlexander Shishkin unsigned int nmasters; 6937bd1d409SAlexander Shishkin int err = -ENOMEM; 6947bd1d409SAlexander Shishkin 6957bd1d409SAlexander Shishkin if (!stm_core_up) 6967bd1d409SAlexander Shishkin return -EPROBE_DEFER; 6977bd1d409SAlexander Shishkin 6987bd1d409SAlexander Shishkin if (!stm_data->packet || !stm_data->sw_nchannels) 6997bd1d409SAlexander Shishkin return -EINVAL; 7007bd1d409SAlexander Shishkin 7017b3bb0e7SChunyan Zhang nmasters = stm_data->sw_end - stm_data->sw_start + 1; 7027bd1d409SAlexander Shishkin stm = kzalloc(sizeof(*stm) + nmasters * sizeof(void *), GFP_KERNEL); 7037bd1d409SAlexander Shishkin if (!stm) 7047bd1d409SAlexander Shishkin return -ENOMEM; 7057bd1d409SAlexander Shishkin 7067bd1d409SAlexander Shishkin stm->major = register_chrdev(0, stm_data->name, &stm_fops); 7077bd1d409SAlexander Shishkin if (stm->major < 0) 7087bd1d409SAlexander Shishkin goto err_free; 7097bd1d409SAlexander Shishkin 7107bd1d409SAlexander Shishkin device_initialize(&stm->dev); 7117bd1d409SAlexander Shishkin stm->dev.devt = MKDEV(stm->major, 0); 7127bd1d409SAlexander Shishkin stm->dev.class = &stm_class; 7137bd1d409SAlexander Shishkin stm->dev.parent = parent; 7147bd1d409SAlexander Shishkin stm->dev.release = stm_device_release; 7157bd1d409SAlexander Shishkin 716389b6699SAlexander Shishkin mutex_init(&stm->link_mutex); 717389b6699SAlexander Shishkin spin_lock_init(&stm->link_lock); 718389b6699SAlexander Shishkin INIT_LIST_HEAD(&stm->link_list); 719389b6699SAlexander Shishkin 720389b6699SAlexander Shishkin /* initialize the object before it is accessible via sysfs */ 721389b6699SAlexander Shishkin spin_lock_init(&stm->mc_lock); 722389b6699SAlexander Shishkin mutex_init(&stm->policy_mutex); 723389b6699SAlexander Shishkin stm->sw_nmasters = nmasters; 724389b6699SAlexander Shishkin stm->owner = owner; 725389b6699SAlexander Shishkin stm->data = stm_data; 726389b6699SAlexander Shishkin stm_data->stm = stm; 727389b6699SAlexander Shishkin 7287bd1d409SAlexander Shishkin err = kobject_set_name(&stm->dev.kobj, "%s", stm_data->name); 7297bd1d409SAlexander Shishkin if (err) 7307bd1d409SAlexander Shishkin goto err_device; 7317bd1d409SAlexander Shishkin 7327bd1d409SAlexander Shishkin err = device_add(&stm->dev); 7337bd1d409SAlexander Shishkin if (err) 7347bd1d409SAlexander Shishkin goto err_device; 7357bd1d409SAlexander Shishkin 7368e0469a4SAlexander Shishkin /* 7378e0469a4SAlexander Shishkin * Use delayed autosuspend to avoid bouncing back and forth 7388e0469a4SAlexander Shishkin * on recurring character device writes, with the initial 7398e0469a4SAlexander Shishkin * delay time of 2 seconds. 7408e0469a4SAlexander Shishkin */ 7418e0469a4SAlexander Shishkin pm_runtime_no_callbacks(&stm->dev); 7428e0469a4SAlexander Shishkin pm_runtime_use_autosuspend(&stm->dev); 7438e0469a4SAlexander Shishkin pm_runtime_set_autosuspend_delay(&stm->dev, 2000); 7448e0469a4SAlexander Shishkin pm_runtime_set_suspended(&stm->dev); 7458e0469a4SAlexander Shishkin pm_runtime_enable(&stm->dev); 7468e0469a4SAlexander Shishkin 7477bd1d409SAlexander Shishkin return 0; 7487bd1d409SAlexander Shishkin 7497bd1d409SAlexander Shishkin err_device: 750cbe4a61dSAlexander Shishkin unregister_chrdev(stm->major, stm_data->name); 751cbe4a61dSAlexander Shishkin 752f7c81c71SAlexander Shishkin /* matches device_initialize() above */ 7537bd1d409SAlexander Shishkin put_device(&stm->dev); 7547bd1d409SAlexander Shishkin err_free: 7557bd1d409SAlexander Shishkin kfree(stm); 7567bd1d409SAlexander Shishkin 7577bd1d409SAlexander Shishkin return err; 7587bd1d409SAlexander Shishkin } 7597bd1d409SAlexander Shishkin EXPORT_SYMBOL_GPL(stm_register_device); 7607bd1d409SAlexander Shishkin 761b4ca34aaSAlexander Shishkin static int __stm_source_link_drop(struct stm_source_device *src, 7627bd1d409SAlexander Shishkin struct stm_device *stm); 7637bd1d409SAlexander Shishkin 7647bd1d409SAlexander Shishkin void stm_unregister_device(struct stm_data *stm_data) 7657bd1d409SAlexander Shishkin { 7667bd1d409SAlexander Shishkin struct stm_device *stm = stm_data->stm; 7677bd1d409SAlexander Shishkin struct stm_source_device *src, *iter; 768b4ca34aaSAlexander Shishkin int i, ret; 7697bd1d409SAlexander Shishkin 7708e0469a4SAlexander Shishkin pm_runtime_dont_use_autosuspend(&stm->dev); 7718e0469a4SAlexander Shishkin pm_runtime_disable(&stm->dev); 7728e0469a4SAlexander Shishkin 773c74f7e82SAlexander Shishkin mutex_lock(&stm->link_mutex); 7747bd1d409SAlexander Shishkin list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) { 775b4ca34aaSAlexander Shishkin ret = __stm_source_link_drop(src, stm); 776b4ca34aaSAlexander Shishkin /* 777b4ca34aaSAlexander Shishkin * src <-> stm link must not change under the same 778b4ca34aaSAlexander Shishkin * stm::link_mutex, so complain loudly if it has; 779b4ca34aaSAlexander Shishkin * also in this situation ret!=0 means this src is 780b4ca34aaSAlexander Shishkin * not connected to this stm and it should be otherwise 781b4ca34aaSAlexander Shishkin * safe to proceed with the tear-down of stm. 782b4ca34aaSAlexander Shishkin */ 783b4ca34aaSAlexander Shishkin WARN_ON_ONCE(ret); 7847bd1d409SAlexander Shishkin } 785c74f7e82SAlexander Shishkin mutex_unlock(&stm->link_mutex); 7867bd1d409SAlexander Shishkin 7877bd1d409SAlexander Shishkin synchronize_srcu(&stm_source_srcu); 7887bd1d409SAlexander Shishkin 7897bd1d409SAlexander Shishkin unregister_chrdev(stm->major, stm_data->name); 7907bd1d409SAlexander Shishkin 7917bd1d409SAlexander Shishkin mutex_lock(&stm->policy_mutex); 7927bd1d409SAlexander Shishkin if (stm->policy) 7937bd1d409SAlexander Shishkin stp_policy_unbind(stm->policy); 7947bd1d409SAlexander Shishkin mutex_unlock(&stm->policy_mutex); 7957bd1d409SAlexander Shishkin 79673a3ed19SChunyan Zhang for (i = stm->data->sw_start; i <= stm->data->sw_end; i++) 7977bd1d409SAlexander Shishkin stp_master_free(stm, i); 7987bd1d409SAlexander Shishkin 7997bd1d409SAlexander Shishkin device_unregister(&stm->dev); 8007bd1d409SAlexander Shishkin stm_data->stm = NULL; 8017bd1d409SAlexander Shishkin } 8027bd1d409SAlexander Shishkin EXPORT_SYMBOL_GPL(stm_unregister_device); 8037bd1d409SAlexander Shishkin 804c74f7e82SAlexander Shishkin /* 805c74f7e82SAlexander Shishkin * stm::link_list access serialization uses a spinlock and a mutex; holding 806c74f7e82SAlexander Shishkin * either of them guarantees that the list is stable; modification requires 807c74f7e82SAlexander Shishkin * holding both of them. 808c74f7e82SAlexander Shishkin * 809c74f7e82SAlexander Shishkin * Lock ordering is as follows: 810c74f7e82SAlexander Shishkin * stm::link_mutex 811c74f7e82SAlexander Shishkin * stm::link_lock 812c74f7e82SAlexander Shishkin * src::link_lock 813c74f7e82SAlexander Shishkin */ 814c74f7e82SAlexander Shishkin 8157bd1d409SAlexander Shishkin /** 8167bd1d409SAlexander Shishkin * stm_source_link_add() - connect an stm_source device to an stm device 8177bd1d409SAlexander Shishkin * @src: stm_source device 8187bd1d409SAlexander Shishkin * @stm: stm device 8197bd1d409SAlexander Shishkin * 8207bd1d409SAlexander Shishkin * This function establishes a link from stm_source to an stm device so that 8217bd1d409SAlexander Shishkin * the former can send out trace data to the latter. 8227bd1d409SAlexander Shishkin * 8237bd1d409SAlexander Shishkin * Return: 0 on success, -errno otherwise. 8247bd1d409SAlexander Shishkin */ 8257bd1d409SAlexander Shishkin static int stm_source_link_add(struct stm_source_device *src, 8267bd1d409SAlexander Shishkin struct stm_device *stm) 8277bd1d409SAlexander Shishkin { 8287bd1d409SAlexander Shishkin char *id; 8297bd1d409SAlexander Shishkin int err; 8307bd1d409SAlexander Shishkin 831c74f7e82SAlexander Shishkin mutex_lock(&stm->link_mutex); 8327bd1d409SAlexander Shishkin spin_lock(&stm->link_lock); 8337bd1d409SAlexander Shishkin spin_lock(&src->link_lock); 8347bd1d409SAlexander Shishkin 8357bd1d409SAlexander Shishkin /* src->link is dereferenced under stm_source_srcu but not the list */ 8367bd1d409SAlexander Shishkin rcu_assign_pointer(src->link, stm); 8377bd1d409SAlexander Shishkin list_add_tail(&src->link_entry, &stm->link_list); 8387bd1d409SAlexander Shishkin 8397bd1d409SAlexander Shishkin spin_unlock(&src->link_lock); 8407bd1d409SAlexander Shishkin spin_unlock(&stm->link_lock); 841c74f7e82SAlexander Shishkin mutex_unlock(&stm->link_mutex); 8427bd1d409SAlexander Shishkin 8437bd1d409SAlexander Shishkin id = kstrdup(src->data->name, GFP_KERNEL); 8447bd1d409SAlexander Shishkin if (id) { 8457bd1d409SAlexander Shishkin src->policy_node = 8467bd1d409SAlexander Shishkin stp_policy_node_lookup(stm, id); 8477bd1d409SAlexander Shishkin 8487bd1d409SAlexander Shishkin kfree(id); 8497bd1d409SAlexander Shishkin } 8507bd1d409SAlexander Shishkin 8517bd1d409SAlexander Shishkin err = stm_output_assign(stm, src->data->nr_chans, 8527bd1d409SAlexander Shishkin src->policy_node, &src->output); 8537bd1d409SAlexander Shishkin 8547bd1d409SAlexander Shishkin if (src->policy_node) 8557bd1d409SAlexander Shishkin stp_policy_node_put(src->policy_node); 8567bd1d409SAlexander Shishkin 8577bd1d409SAlexander Shishkin if (err) 8587bd1d409SAlexander Shishkin goto fail_detach; 8597bd1d409SAlexander Shishkin 8607bd1d409SAlexander Shishkin /* this is to notify the STM device that a new link has been made */ 8617bd1d409SAlexander Shishkin if (stm->data->link) 8627bd1d409SAlexander Shishkin err = stm->data->link(stm->data, src->output.master, 8637bd1d409SAlexander Shishkin src->output.channel); 8647bd1d409SAlexander Shishkin 8657bd1d409SAlexander Shishkin if (err) 8667bd1d409SAlexander Shishkin goto fail_free_output; 8677bd1d409SAlexander Shishkin 8687bd1d409SAlexander Shishkin /* this is to let the source carry out all necessary preparations */ 8697bd1d409SAlexander Shishkin if (src->data->link) 8707bd1d409SAlexander Shishkin src->data->link(src->data); 8717bd1d409SAlexander Shishkin 8727bd1d409SAlexander Shishkin return 0; 8737bd1d409SAlexander Shishkin 8747bd1d409SAlexander Shishkin fail_free_output: 8757bd1d409SAlexander Shishkin stm_output_free(stm, &src->output); 8767bd1d409SAlexander Shishkin 8777bd1d409SAlexander Shishkin fail_detach: 878c74f7e82SAlexander Shishkin mutex_lock(&stm->link_mutex); 8797bd1d409SAlexander Shishkin spin_lock(&stm->link_lock); 8807bd1d409SAlexander Shishkin spin_lock(&src->link_lock); 8817bd1d409SAlexander Shishkin 8827bd1d409SAlexander Shishkin rcu_assign_pointer(src->link, NULL); 8837bd1d409SAlexander Shishkin list_del_init(&src->link_entry); 8847bd1d409SAlexander Shishkin 8857bd1d409SAlexander Shishkin spin_unlock(&src->link_lock); 8867bd1d409SAlexander Shishkin spin_unlock(&stm->link_lock); 887c74f7e82SAlexander Shishkin mutex_unlock(&stm->link_mutex); 8887bd1d409SAlexander Shishkin 8897bd1d409SAlexander Shishkin return err; 8907bd1d409SAlexander Shishkin } 8917bd1d409SAlexander Shishkin 8927bd1d409SAlexander Shishkin /** 8937bd1d409SAlexander Shishkin * __stm_source_link_drop() - detach stm_source from an stm device 8947bd1d409SAlexander Shishkin * @src: stm_source device 8957bd1d409SAlexander Shishkin * @stm: stm device 8967bd1d409SAlexander Shishkin * 8977bd1d409SAlexander Shishkin * If @stm is @src::link, disconnect them from one another and put the 8987bd1d409SAlexander Shishkin * reference on the @stm device. 8997bd1d409SAlexander Shishkin * 900c74f7e82SAlexander Shishkin * Caller must hold stm::link_mutex. 9017bd1d409SAlexander Shishkin */ 902b4ca34aaSAlexander Shishkin static int __stm_source_link_drop(struct stm_source_device *src, 9037bd1d409SAlexander Shishkin struct stm_device *stm) 9047bd1d409SAlexander Shishkin { 9050df771deSAlexander Shishkin struct stm_device *link; 906b4ca34aaSAlexander Shishkin int ret = 0; 9070df771deSAlexander Shishkin 908c74f7e82SAlexander Shishkin lockdep_assert_held(&stm->link_mutex); 909c74f7e82SAlexander Shishkin 910c74f7e82SAlexander Shishkin /* for stm::link_list modification, we hold both mutex and spinlock */ 911c74f7e82SAlexander Shishkin spin_lock(&stm->link_lock); 9127bd1d409SAlexander Shishkin spin_lock(&src->link_lock); 9130df771deSAlexander Shishkin link = srcu_dereference_check(src->link, &stm_source_srcu, 1); 914b4ca34aaSAlexander Shishkin 915b4ca34aaSAlexander Shishkin /* 916b4ca34aaSAlexander Shishkin * The linked device may have changed since we last looked, because 917b4ca34aaSAlexander Shishkin * we weren't holding the src::link_lock back then; if this is the 918b4ca34aaSAlexander Shishkin * case, tell the caller to retry. 919b4ca34aaSAlexander Shishkin */ 920b4ca34aaSAlexander Shishkin if (link != stm) { 921b4ca34aaSAlexander Shishkin ret = -EAGAIN; 9221810f2c4SAlexander Shishkin goto unlock; 923b4ca34aaSAlexander Shishkin } 9247bd1d409SAlexander Shishkin 9250df771deSAlexander Shishkin stm_output_free(link, &src->output); 9267bd1d409SAlexander Shishkin list_del_init(&src->link_entry); 9278e0469a4SAlexander Shishkin pm_runtime_mark_last_busy(&link->dev); 9288e0469a4SAlexander Shishkin pm_runtime_put_autosuspend(&link->dev); 9297bd1d409SAlexander Shishkin /* matches stm_find_device() from stm_source_link_store() */ 9300df771deSAlexander Shishkin stm_put_device(link); 9317bd1d409SAlexander Shishkin rcu_assign_pointer(src->link, NULL); 9327bd1d409SAlexander Shishkin 9331810f2c4SAlexander Shishkin unlock: 9347bd1d409SAlexander Shishkin spin_unlock(&src->link_lock); 935c74f7e82SAlexander Shishkin spin_unlock(&stm->link_lock); 936b4ca34aaSAlexander Shishkin 937cc842407SAlexander Shishkin /* 938cc842407SAlexander Shishkin * Call the unlink callbacks for both source and stm, when we know 939cc842407SAlexander Shishkin * that we have actually performed the unlinking. 940cc842407SAlexander Shishkin */ 941cc842407SAlexander Shishkin if (!ret) { 942cc842407SAlexander Shishkin if (src->data->unlink) 943b4ca34aaSAlexander Shishkin src->data->unlink(src->data); 944b4ca34aaSAlexander Shishkin 945cc842407SAlexander Shishkin if (stm->data->unlink) 946cc842407SAlexander Shishkin stm->data->unlink(stm->data, src->output.master, 947cc842407SAlexander Shishkin src->output.channel); 948cc842407SAlexander Shishkin } 949cc842407SAlexander Shishkin 950b4ca34aaSAlexander Shishkin return ret; 9517bd1d409SAlexander Shishkin } 9527bd1d409SAlexander Shishkin 9537bd1d409SAlexander Shishkin /** 9547bd1d409SAlexander Shishkin * stm_source_link_drop() - detach stm_source from its stm device 9557bd1d409SAlexander Shishkin * @src: stm_source device 9567bd1d409SAlexander Shishkin * 9577bd1d409SAlexander Shishkin * Unlinking means disconnecting from source's STM device; after this 9587bd1d409SAlexander Shishkin * writes will be unsuccessful until it is linked to a new STM device. 9597bd1d409SAlexander Shishkin * 9607bd1d409SAlexander Shishkin * This will happen on "stm_source_link" sysfs attribute write to undo 9617bd1d409SAlexander Shishkin * the existing link (if any), or on linked STM device's de-registration. 9627bd1d409SAlexander Shishkin */ 9637bd1d409SAlexander Shishkin static void stm_source_link_drop(struct stm_source_device *src) 9647bd1d409SAlexander Shishkin { 9657bd1d409SAlexander Shishkin struct stm_device *stm; 966b4ca34aaSAlexander Shishkin int idx, ret; 9677bd1d409SAlexander Shishkin 968b4ca34aaSAlexander Shishkin retry: 9697bd1d409SAlexander Shishkin idx = srcu_read_lock(&stm_source_srcu); 970b4ca34aaSAlexander Shishkin /* 971b4ca34aaSAlexander Shishkin * The stm device will be valid for the duration of this 972b4ca34aaSAlexander Shishkin * read section, but the link may change before we grab 973b4ca34aaSAlexander Shishkin * the src::link_lock in __stm_source_link_drop(). 974b4ca34aaSAlexander Shishkin */ 9757bd1d409SAlexander Shishkin stm = srcu_dereference(src->link, &stm_source_srcu); 9767bd1d409SAlexander Shishkin 977b4ca34aaSAlexander Shishkin ret = 0; 9787bd1d409SAlexander Shishkin if (stm) { 979c74f7e82SAlexander Shishkin mutex_lock(&stm->link_mutex); 980b4ca34aaSAlexander Shishkin ret = __stm_source_link_drop(src, stm); 981c74f7e82SAlexander Shishkin mutex_unlock(&stm->link_mutex); 9827bd1d409SAlexander Shishkin } 9837bd1d409SAlexander Shishkin 9847bd1d409SAlexander Shishkin srcu_read_unlock(&stm_source_srcu, idx); 985b4ca34aaSAlexander Shishkin 986b4ca34aaSAlexander Shishkin /* if it did change, retry */ 987b4ca34aaSAlexander Shishkin if (ret == -EAGAIN) 988b4ca34aaSAlexander Shishkin goto retry; 9897bd1d409SAlexander Shishkin } 9907bd1d409SAlexander Shishkin 9917bd1d409SAlexander Shishkin static ssize_t stm_source_link_show(struct device *dev, 9927bd1d409SAlexander Shishkin struct device_attribute *attr, 9937bd1d409SAlexander Shishkin char *buf) 9947bd1d409SAlexander Shishkin { 9957bd1d409SAlexander Shishkin struct stm_source_device *src = to_stm_source_device(dev); 9967bd1d409SAlexander Shishkin struct stm_device *stm; 9977bd1d409SAlexander Shishkin int idx, ret; 9987bd1d409SAlexander Shishkin 9997bd1d409SAlexander Shishkin idx = srcu_read_lock(&stm_source_srcu); 10007bd1d409SAlexander Shishkin stm = srcu_dereference(src->link, &stm_source_srcu); 10017bd1d409SAlexander Shishkin ret = sprintf(buf, "%s\n", 10027bd1d409SAlexander Shishkin stm ? dev_name(&stm->dev) : "<none>"); 10037bd1d409SAlexander Shishkin srcu_read_unlock(&stm_source_srcu, idx); 10047bd1d409SAlexander Shishkin 10057bd1d409SAlexander Shishkin return ret; 10067bd1d409SAlexander Shishkin } 10077bd1d409SAlexander Shishkin 10087bd1d409SAlexander Shishkin static ssize_t stm_source_link_store(struct device *dev, 10097bd1d409SAlexander Shishkin struct device_attribute *attr, 10107bd1d409SAlexander Shishkin const char *buf, size_t count) 10117bd1d409SAlexander Shishkin { 10127bd1d409SAlexander Shishkin struct stm_source_device *src = to_stm_source_device(dev); 10137bd1d409SAlexander Shishkin struct stm_device *link; 10147bd1d409SAlexander Shishkin int err; 10157bd1d409SAlexander Shishkin 10167bd1d409SAlexander Shishkin stm_source_link_drop(src); 10177bd1d409SAlexander Shishkin 10187bd1d409SAlexander Shishkin link = stm_find_device(buf); 10197bd1d409SAlexander Shishkin if (!link) 10207bd1d409SAlexander Shishkin return -EINVAL; 10217bd1d409SAlexander Shishkin 10228e0469a4SAlexander Shishkin pm_runtime_get(&link->dev); 10238e0469a4SAlexander Shishkin 10247bd1d409SAlexander Shishkin err = stm_source_link_add(src, link); 1025f7c81c71SAlexander Shishkin if (err) { 10268e0469a4SAlexander Shishkin pm_runtime_put_autosuspend(&link->dev); 1027f7c81c71SAlexander Shishkin /* matches the stm_find_device() above */ 10287bd1d409SAlexander Shishkin stm_put_device(link); 1029f7c81c71SAlexander Shishkin } 10307bd1d409SAlexander Shishkin 10317bd1d409SAlexander Shishkin return err ? : count; 10327bd1d409SAlexander Shishkin } 10337bd1d409SAlexander Shishkin 10347bd1d409SAlexander Shishkin static DEVICE_ATTR_RW(stm_source_link); 10357bd1d409SAlexander Shishkin 10367bd1d409SAlexander Shishkin static struct attribute *stm_source_attrs[] = { 10377bd1d409SAlexander Shishkin &dev_attr_stm_source_link.attr, 10387bd1d409SAlexander Shishkin NULL, 10397bd1d409SAlexander Shishkin }; 10407bd1d409SAlexander Shishkin 10417bd1d409SAlexander Shishkin ATTRIBUTE_GROUPS(stm_source); 10427bd1d409SAlexander Shishkin 10437bd1d409SAlexander Shishkin static struct class stm_source_class = { 10447bd1d409SAlexander Shishkin .name = "stm_source", 10457bd1d409SAlexander Shishkin .dev_groups = stm_source_groups, 10467bd1d409SAlexander Shishkin }; 10477bd1d409SAlexander Shishkin 10487bd1d409SAlexander Shishkin static void stm_source_device_release(struct device *dev) 10497bd1d409SAlexander Shishkin { 10507bd1d409SAlexander Shishkin struct stm_source_device *src = to_stm_source_device(dev); 10517bd1d409SAlexander Shishkin 10527bd1d409SAlexander Shishkin kfree(src); 10537bd1d409SAlexander Shishkin } 10547bd1d409SAlexander Shishkin 10557bd1d409SAlexander Shishkin /** 10567bd1d409SAlexander Shishkin * stm_source_register_device() - register an stm_source device 10577bd1d409SAlexander Shishkin * @parent: parent device 10587bd1d409SAlexander Shishkin * @data: device description structure 10597bd1d409SAlexander Shishkin * 10607bd1d409SAlexander Shishkin * This will create a device of stm_source class that can write 10617bd1d409SAlexander Shishkin * data to an stm device once linked. 10627bd1d409SAlexander Shishkin * 10637bd1d409SAlexander Shishkin * Return: 0 on success, -errno otherwise. 10647bd1d409SAlexander Shishkin */ 10657bd1d409SAlexander Shishkin int stm_source_register_device(struct device *parent, 10667bd1d409SAlexander Shishkin struct stm_source_data *data) 10677bd1d409SAlexander Shishkin { 10687bd1d409SAlexander Shishkin struct stm_source_device *src; 10697bd1d409SAlexander Shishkin int err; 10707bd1d409SAlexander Shishkin 10717bd1d409SAlexander Shishkin if (!stm_core_up) 10727bd1d409SAlexander Shishkin return -EPROBE_DEFER; 10737bd1d409SAlexander Shishkin 10747bd1d409SAlexander Shishkin src = kzalloc(sizeof(*src), GFP_KERNEL); 10757bd1d409SAlexander Shishkin if (!src) 10767bd1d409SAlexander Shishkin return -ENOMEM; 10777bd1d409SAlexander Shishkin 10787bd1d409SAlexander Shishkin device_initialize(&src->dev); 10797bd1d409SAlexander Shishkin src->dev.class = &stm_source_class; 10807bd1d409SAlexander Shishkin src->dev.parent = parent; 10817bd1d409SAlexander Shishkin src->dev.release = stm_source_device_release; 10827bd1d409SAlexander Shishkin 10837bd1d409SAlexander Shishkin err = kobject_set_name(&src->dev.kobj, "%s", data->name); 10847bd1d409SAlexander Shishkin if (err) 10857bd1d409SAlexander Shishkin goto err; 10867bd1d409SAlexander Shishkin 10878e0469a4SAlexander Shishkin pm_runtime_no_callbacks(&src->dev); 10888e0469a4SAlexander Shishkin pm_runtime_forbid(&src->dev); 10898e0469a4SAlexander Shishkin 10907bd1d409SAlexander Shishkin err = device_add(&src->dev); 10917bd1d409SAlexander Shishkin if (err) 10927bd1d409SAlexander Shishkin goto err; 10937bd1d409SAlexander Shishkin 1094cde4ad83SAlexander Shishkin stm_output_init(&src->output); 10957bd1d409SAlexander Shishkin spin_lock_init(&src->link_lock); 10967bd1d409SAlexander Shishkin INIT_LIST_HEAD(&src->link_entry); 10977bd1d409SAlexander Shishkin src->data = data; 10987bd1d409SAlexander Shishkin data->src = src; 10997bd1d409SAlexander Shishkin 11007bd1d409SAlexander Shishkin return 0; 11017bd1d409SAlexander Shishkin 11027bd1d409SAlexander Shishkin err: 11037bd1d409SAlexander Shishkin put_device(&src->dev); 11047bd1d409SAlexander Shishkin kfree(src); 11057bd1d409SAlexander Shishkin 11067bd1d409SAlexander Shishkin return err; 11077bd1d409SAlexander Shishkin } 11087bd1d409SAlexander Shishkin EXPORT_SYMBOL_GPL(stm_source_register_device); 11097bd1d409SAlexander Shishkin 11107bd1d409SAlexander Shishkin /** 11117bd1d409SAlexander Shishkin * stm_source_unregister_device() - unregister an stm_source device 11127bd1d409SAlexander Shishkin * @data: device description that was used to register the device 11137bd1d409SAlexander Shishkin * 11147bd1d409SAlexander Shishkin * This will remove a previously created stm_source device from the system. 11157bd1d409SAlexander Shishkin */ 11167bd1d409SAlexander Shishkin void stm_source_unregister_device(struct stm_source_data *data) 11177bd1d409SAlexander Shishkin { 11187bd1d409SAlexander Shishkin struct stm_source_device *src = data->src; 11197bd1d409SAlexander Shishkin 11207bd1d409SAlexander Shishkin stm_source_link_drop(src); 11217bd1d409SAlexander Shishkin 11227bd1d409SAlexander Shishkin device_destroy(&stm_source_class, src->dev.devt); 11237bd1d409SAlexander Shishkin } 11247bd1d409SAlexander Shishkin EXPORT_SYMBOL_GPL(stm_source_unregister_device); 11257bd1d409SAlexander Shishkin 11267bd1d409SAlexander Shishkin int stm_source_write(struct stm_source_data *data, unsigned int chan, 11277bd1d409SAlexander Shishkin const char *buf, size_t count) 11287bd1d409SAlexander Shishkin { 11297bd1d409SAlexander Shishkin struct stm_source_device *src = data->src; 11307bd1d409SAlexander Shishkin struct stm_device *stm; 11317bd1d409SAlexander Shishkin int idx; 11327bd1d409SAlexander Shishkin 11337bd1d409SAlexander Shishkin if (!src->output.nr_chans) 11347bd1d409SAlexander Shishkin return -ENODEV; 11357bd1d409SAlexander Shishkin 11367bd1d409SAlexander Shishkin if (chan >= src->output.nr_chans) 11377bd1d409SAlexander Shishkin return -EINVAL; 11387bd1d409SAlexander Shishkin 11397bd1d409SAlexander Shishkin idx = srcu_read_lock(&stm_source_srcu); 11407bd1d409SAlexander Shishkin 11417bd1d409SAlexander Shishkin stm = srcu_dereference(src->link, &stm_source_srcu); 11427bd1d409SAlexander Shishkin if (stm) 1143f8560a9bSAlexander Shishkin count = stm_write(stm->data, src->output.master, 11447bd1d409SAlexander Shishkin src->output.channel + chan, 11457bd1d409SAlexander Shishkin buf, count); 11467bd1d409SAlexander Shishkin else 11477bd1d409SAlexander Shishkin count = -ENODEV; 11487bd1d409SAlexander Shishkin 11497bd1d409SAlexander Shishkin srcu_read_unlock(&stm_source_srcu, idx); 11507bd1d409SAlexander Shishkin 11517bd1d409SAlexander Shishkin return count; 11527bd1d409SAlexander Shishkin } 11537bd1d409SAlexander Shishkin EXPORT_SYMBOL_GPL(stm_source_write); 11547bd1d409SAlexander Shishkin 11557bd1d409SAlexander Shishkin static int __init stm_core_init(void) 11567bd1d409SAlexander Shishkin { 11577bd1d409SAlexander Shishkin int err; 11587bd1d409SAlexander Shishkin 11597bd1d409SAlexander Shishkin err = class_register(&stm_class); 11607bd1d409SAlexander Shishkin if (err) 11617bd1d409SAlexander Shishkin return err; 11627bd1d409SAlexander Shishkin 11637bd1d409SAlexander Shishkin err = class_register(&stm_source_class); 11647bd1d409SAlexander Shishkin if (err) 11657bd1d409SAlexander Shishkin goto err_stm; 11667bd1d409SAlexander Shishkin 11677bd1d409SAlexander Shishkin err = stp_configfs_init(); 11687bd1d409SAlexander Shishkin if (err) 11697bd1d409SAlexander Shishkin goto err_src; 11707bd1d409SAlexander Shishkin 11717bd1d409SAlexander Shishkin init_srcu_struct(&stm_source_srcu); 11727bd1d409SAlexander Shishkin 11737bd1d409SAlexander Shishkin stm_core_up++; 11747bd1d409SAlexander Shishkin 11757bd1d409SAlexander Shishkin return 0; 11767bd1d409SAlexander Shishkin 11777bd1d409SAlexander Shishkin err_src: 11787bd1d409SAlexander Shishkin class_unregister(&stm_source_class); 11797bd1d409SAlexander Shishkin err_stm: 11807bd1d409SAlexander Shishkin class_unregister(&stm_class); 11817bd1d409SAlexander Shishkin 11827bd1d409SAlexander Shishkin return err; 11837bd1d409SAlexander Shishkin } 11847bd1d409SAlexander Shishkin 11857bd1d409SAlexander Shishkin module_init(stm_core_init); 11867bd1d409SAlexander Shishkin 11877bd1d409SAlexander Shishkin static void __exit stm_core_exit(void) 11887bd1d409SAlexander Shishkin { 11897bd1d409SAlexander Shishkin cleanup_srcu_struct(&stm_source_srcu); 11907bd1d409SAlexander Shishkin class_unregister(&stm_source_class); 11917bd1d409SAlexander Shishkin class_unregister(&stm_class); 11927bd1d409SAlexander Shishkin stp_configfs_exit(); 11937bd1d409SAlexander Shishkin } 11947bd1d409SAlexander Shishkin 11957bd1d409SAlexander Shishkin module_exit(stm_core_exit); 11967bd1d409SAlexander Shishkin 11977bd1d409SAlexander Shishkin MODULE_LICENSE("GPL v2"); 11987bd1d409SAlexander Shishkin MODULE_DESCRIPTION("System Trace Module device class"); 11997bd1d409SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); 1200