18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel(R) Trace Hub Memory Storage Unit 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Intel Corporation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 148c2ecf20Sopenharmony_ci#include <linux/sizes.h> 158c2ecf20Sopenharmony_ci#include <linux/printk.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/mm.h> 188c2ecf20Sopenharmony_ci#include <linux/fs.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 248c2ecf20Sopenharmony_ci#include <asm/set_memory.h> 258c2ecf20Sopenharmony_ci#endif 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/intel_th.h> 288c2ecf20Sopenharmony_ci#include "intel_th.h" 298c2ecf20Sopenharmony_ci#include "msu.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define msc_dev(x) (&(x)->thdev->dev) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * Lockout state transitions: 358c2ecf20Sopenharmony_ci * READY -> INUSE -+-> LOCKED -+-> READY -> etc. 368c2ecf20Sopenharmony_ci * \-----------/ 378c2ecf20Sopenharmony_ci * WIN_READY: window can be used by HW 388c2ecf20Sopenharmony_ci * WIN_INUSE: window is in use 398c2ecf20Sopenharmony_ci * WIN_LOCKED: window is filled up and is being processed by the buffer 408c2ecf20Sopenharmony_ci * handling code 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * All state transitions happen automatically, except for the LOCKED->READY, 438c2ecf20Sopenharmony_ci * which needs to be signalled by the buffer code by calling 448c2ecf20Sopenharmony_ci * intel_th_msc_window_unlock(). 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * When the interrupt handler has to switch to the next window, it checks 478c2ecf20Sopenharmony_ci * whether it's READY, and if it is, it performs the switch and tracing 488c2ecf20Sopenharmony_ci * continues. If it's LOCKED, it stops the trace. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cienum lockout_state { 518c2ecf20Sopenharmony_ci WIN_READY = 0, 528c2ecf20Sopenharmony_ci WIN_INUSE, 538c2ecf20Sopenharmony_ci WIN_LOCKED 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * struct msc_window - multiblock mode window descriptor 588c2ecf20Sopenharmony_ci * @entry: window list linkage (msc::win_list) 598c2ecf20Sopenharmony_ci * @pgoff: page offset into the buffer that this window starts at 608c2ecf20Sopenharmony_ci * @lockout: lockout state, see comment below 618c2ecf20Sopenharmony_ci * @lo_lock: lockout state serialization 628c2ecf20Sopenharmony_ci * @nr_blocks: number of blocks (pages) in this window 638c2ecf20Sopenharmony_ci * @nr_segs: number of segments in this window (<= @nr_blocks) 648c2ecf20Sopenharmony_ci * @_sgt: array of block descriptors 658c2ecf20Sopenharmony_ci * @sgt: array of block descriptors 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistruct msc_window { 688c2ecf20Sopenharmony_ci struct list_head entry; 698c2ecf20Sopenharmony_ci unsigned long pgoff; 708c2ecf20Sopenharmony_ci enum lockout_state lockout; 718c2ecf20Sopenharmony_ci spinlock_t lo_lock; 728c2ecf20Sopenharmony_ci unsigned int nr_blocks; 738c2ecf20Sopenharmony_ci unsigned int nr_segs; 748c2ecf20Sopenharmony_ci struct msc *msc; 758c2ecf20Sopenharmony_ci struct sg_table _sgt; 768c2ecf20Sopenharmony_ci struct sg_table *sgt; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/** 808c2ecf20Sopenharmony_ci * struct msc_iter - iterator for msc buffer 818c2ecf20Sopenharmony_ci * @entry: msc::iter_list linkage 828c2ecf20Sopenharmony_ci * @msc: pointer to the MSC device 838c2ecf20Sopenharmony_ci * @start_win: oldest window 848c2ecf20Sopenharmony_ci * @win: current window 858c2ecf20Sopenharmony_ci * @offset: current logical offset into the buffer 868c2ecf20Sopenharmony_ci * @start_block: oldest block in the window 878c2ecf20Sopenharmony_ci * @block: block number in the window 888c2ecf20Sopenharmony_ci * @block_off: offset into current block 898c2ecf20Sopenharmony_ci * @wrap_count: block wrapping handling 908c2ecf20Sopenharmony_ci * @eof: end of buffer reached 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistruct msc_iter { 938c2ecf20Sopenharmony_ci struct list_head entry; 948c2ecf20Sopenharmony_ci struct msc *msc; 958c2ecf20Sopenharmony_ci struct msc_window *start_win; 968c2ecf20Sopenharmony_ci struct msc_window *win; 978c2ecf20Sopenharmony_ci unsigned long offset; 988c2ecf20Sopenharmony_ci struct scatterlist *start_block; 998c2ecf20Sopenharmony_ci struct scatterlist *block; 1008c2ecf20Sopenharmony_ci unsigned int block_off; 1018c2ecf20Sopenharmony_ci unsigned int wrap_count; 1028c2ecf20Sopenharmony_ci unsigned int eof; 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/** 1068c2ecf20Sopenharmony_ci * struct msc - MSC device representation 1078c2ecf20Sopenharmony_ci * @reg_base: register window base address 1088c2ecf20Sopenharmony_ci * @thdev: intel_th_device pointer 1098c2ecf20Sopenharmony_ci * @mbuf: MSU buffer, if assigned 1108c2ecf20Sopenharmony_ci * @mbuf_priv MSU buffer's private data, if @mbuf 1118c2ecf20Sopenharmony_ci * @win_list: list of windows in multiblock mode 1128c2ecf20Sopenharmony_ci * @single_sgt: single mode buffer 1138c2ecf20Sopenharmony_ci * @cur_win: current window 1148c2ecf20Sopenharmony_ci * @nr_pages: total number of pages allocated for this buffer 1158c2ecf20Sopenharmony_ci * @single_sz: amount of data in single mode 1168c2ecf20Sopenharmony_ci * @single_wrap: single mode wrap occurred 1178c2ecf20Sopenharmony_ci * @base: buffer's base pointer 1188c2ecf20Sopenharmony_ci * @base_addr: buffer's base address 1198c2ecf20Sopenharmony_ci * @user_count: number of users of the buffer 1208c2ecf20Sopenharmony_ci * @mmap_count: number of mappings 1218c2ecf20Sopenharmony_ci * @buf_mutex: mutex to serialize access to buffer-related bits 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci * @enabled: MSC is enabled 1248c2ecf20Sopenharmony_ci * @wrap: wrapping is enabled 1258c2ecf20Sopenharmony_ci * @mode: MSC operating mode 1268c2ecf20Sopenharmony_ci * @burst_len: write burst length 1278c2ecf20Sopenharmony_ci * @index: number of this MSC in the MSU 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_cistruct msc { 1308c2ecf20Sopenharmony_ci void __iomem *reg_base; 1318c2ecf20Sopenharmony_ci void __iomem *msu_base; 1328c2ecf20Sopenharmony_ci struct intel_th_device *thdev; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci const struct msu_buffer *mbuf; 1358c2ecf20Sopenharmony_ci void *mbuf_priv; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci struct work_struct work; 1388c2ecf20Sopenharmony_ci struct list_head win_list; 1398c2ecf20Sopenharmony_ci struct sg_table single_sgt; 1408c2ecf20Sopenharmony_ci struct msc_window *cur_win; 1418c2ecf20Sopenharmony_ci struct msc_window *switch_on_unlock; 1428c2ecf20Sopenharmony_ci unsigned long nr_pages; 1438c2ecf20Sopenharmony_ci unsigned long single_sz; 1448c2ecf20Sopenharmony_ci unsigned int single_wrap : 1; 1458c2ecf20Sopenharmony_ci void *base; 1468c2ecf20Sopenharmony_ci dma_addr_t base_addr; 1478c2ecf20Sopenharmony_ci u32 orig_addr; 1488c2ecf20Sopenharmony_ci u32 orig_sz; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* <0: no buffer, 0: no users, >0: active users */ 1518c2ecf20Sopenharmony_ci atomic_t user_count; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci atomic_t mmap_count; 1548c2ecf20Sopenharmony_ci struct mutex buf_mutex; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci struct list_head iter_list; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci bool stop_on_full; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* config */ 1618c2ecf20Sopenharmony_ci unsigned int enabled : 1, 1628c2ecf20Sopenharmony_ci wrap : 1, 1638c2ecf20Sopenharmony_ci do_irq : 1, 1648c2ecf20Sopenharmony_ci multi_is_broken : 1; 1658c2ecf20Sopenharmony_ci unsigned int mode; 1668c2ecf20Sopenharmony_ci unsigned int burst_len; 1678c2ecf20Sopenharmony_ci unsigned int index; 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic LIST_HEAD(msu_buffer_list); 1718c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(msu_buffer_mutex); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/** 1748c2ecf20Sopenharmony_ci * struct msu_buffer_entry - internal MSU buffer bookkeeping 1758c2ecf20Sopenharmony_ci * @entry: link to msu_buffer_list 1768c2ecf20Sopenharmony_ci * @mbuf: MSU buffer object 1778c2ecf20Sopenharmony_ci * @owner: module that provides this MSU buffer 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_cistruct msu_buffer_entry { 1808c2ecf20Sopenharmony_ci struct list_head entry; 1818c2ecf20Sopenharmony_ci const struct msu_buffer *mbuf; 1828c2ecf20Sopenharmony_ci struct module *owner; 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic struct msu_buffer_entry *__msu_buffer_entry_find(const char *name) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct msu_buffer_entry *mbe; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci lockdep_assert_held(&msu_buffer_mutex); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci list_for_each_entry(mbe, &msu_buffer_list, entry) { 1928c2ecf20Sopenharmony_ci if (!strcmp(mbe->mbuf->name, name)) 1938c2ecf20Sopenharmony_ci return mbe; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return NULL; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic const struct msu_buffer * 2008c2ecf20Sopenharmony_cimsu_buffer_get(const char *name) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct msu_buffer_entry *mbe; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci mutex_lock(&msu_buffer_mutex); 2058c2ecf20Sopenharmony_ci mbe = __msu_buffer_entry_find(name); 2068c2ecf20Sopenharmony_ci if (mbe && !try_module_get(mbe->owner)) 2078c2ecf20Sopenharmony_ci mbe = NULL; 2088c2ecf20Sopenharmony_ci mutex_unlock(&msu_buffer_mutex); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return mbe ? mbe->mbuf : NULL; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void msu_buffer_put(const struct msu_buffer *mbuf) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct msu_buffer_entry *mbe; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci mutex_lock(&msu_buffer_mutex); 2188c2ecf20Sopenharmony_ci mbe = __msu_buffer_entry_find(mbuf->name); 2198c2ecf20Sopenharmony_ci if (mbe) 2208c2ecf20Sopenharmony_ci module_put(mbe->owner); 2218c2ecf20Sopenharmony_ci mutex_unlock(&msu_buffer_mutex); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ciint intel_th_msu_buffer_register(const struct msu_buffer *mbuf, 2258c2ecf20Sopenharmony_ci struct module *owner) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct msu_buffer_entry *mbe; 2288c2ecf20Sopenharmony_ci int ret = 0; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci mbe = kzalloc(sizeof(*mbe), GFP_KERNEL); 2318c2ecf20Sopenharmony_ci if (!mbe) 2328c2ecf20Sopenharmony_ci return -ENOMEM; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci mutex_lock(&msu_buffer_mutex); 2358c2ecf20Sopenharmony_ci if (__msu_buffer_entry_find(mbuf->name)) { 2368c2ecf20Sopenharmony_ci ret = -EEXIST; 2378c2ecf20Sopenharmony_ci kfree(mbe); 2388c2ecf20Sopenharmony_ci goto unlock; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci mbe->mbuf = mbuf; 2428c2ecf20Sopenharmony_ci mbe->owner = owner; 2438c2ecf20Sopenharmony_ci list_add_tail(&mbe->entry, &msu_buffer_list); 2448c2ecf20Sopenharmony_ciunlock: 2458c2ecf20Sopenharmony_ci mutex_unlock(&msu_buffer_mutex); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_th_msu_buffer_register); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_civoid intel_th_msu_buffer_unregister(const struct msu_buffer *mbuf) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct msu_buffer_entry *mbe; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci mutex_lock(&msu_buffer_mutex); 2568c2ecf20Sopenharmony_ci mbe = __msu_buffer_entry_find(mbuf->name); 2578c2ecf20Sopenharmony_ci if (mbe) { 2588c2ecf20Sopenharmony_ci list_del(&mbe->entry); 2598c2ecf20Sopenharmony_ci kfree(mbe); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci mutex_unlock(&msu_buffer_mutex); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_th_msu_buffer_unregister); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic inline bool msc_block_is_empty(struct msc_block_desc *bdesc) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci /* header hasn't been written */ 2688c2ecf20Sopenharmony_ci if (!bdesc->valid_dw) 2698c2ecf20Sopenharmony_ci return true; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* valid_dw includes the header */ 2728c2ecf20Sopenharmony_ci if (!msc_data_sz(bdesc)) 2738c2ecf20Sopenharmony_ci return true; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return false; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic inline struct scatterlist *msc_win_base_sg(struct msc_window *win) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci return win->sgt->sgl; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic inline struct msc_block_desc *msc_win_base(struct msc_window *win) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci return sg_virt(msc_win_base_sg(win)); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic inline dma_addr_t msc_win_base_dma(struct msc_window *win) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci return sg_dma_address(msc_win_base_sg(win)); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic inline unsigned long 2948c2ecf20Sopenharmony_cimsc_win_base_pfn(struct msc_window *win) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci return PFN_DOWN(msc_win_base_dma(win)); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/** 3008c2ecf20Sopenharmony_ci * msc_is_last_win() - check if a window is the last one for a given MSC 3018c2ecf20Sopenharmony_ci * @win: window 3028c2ecf20Sopenharmony_ci * Return: true if @win is the last window in MSC's multiblock buffer 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistatic inline bool msc_is_last_win(struct msc_window *win) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci return win->entry.next == &win->msc->win_list; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci/** 3108c2ecf20Sopenharmony_ci * msc_next_window() - return next window in the multiblock buffer 3118c2ecf20Sopenharmony_ci * @win: current window 3128c2ecf20Sopenharmony_ci * 3138c2ecf20Sopenharmony_ci * Return: window following the current one 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_cistatic struct msc_window *msc_next_window(struct msc_window *win) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci if (msc_is_last_win(win)) 3188c2ecf20Sopenharmony_ci return list_first_entry(&win->msc->win_list, struct msc_window, 3198c2ecf20Sopenharmony_ci entry); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return list_next_entry(win, entry); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic size_t msc_win_total_sz(struct msc_window *win) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct scatterlist *sg; 3278c2ecf20Sopenharmony_ci unsigned int blk; 3288c2ecf20Sopenharmony_ci size_t size = 0; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci for_each_sg(win->sgt->sgl, sg, win->nr_segs, blk) { 3318c2ecf20Sopenharmony_ci struct msc_block_desc *bdesc = sg_virt(sg); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (msc_block_wrapped(bdesc)) 3348c2ecf20Sopenharmony_ci return (size_t)win->nr_blocks << PAGE_SHIFT; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci size += msc_total_sz(bdesc); 3378c2ecf20Sopenharmony_ci if (msc_block_last_written(bdesc)) 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return size; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/** 3458c2ecf20Sopenharmony_ci * msc_find_window() - find a window matching a given sg_table 3468c2ecf20Sopenharmony_ci * @msc: MSC device 3478c2ecf20Sopenharmony_ci * @sgt: SG table of the window 3488c2ecf20Sopenharmony_ci * @nonempty: skip over empty windows 3498c2ecf20Sopenharmony_ci * 3508c2ecf20Sopenharmony_ci * Return: MSC window structure pointer or NULL if the window 3518c2ecf20Sopenharmony_ci * could not be found. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_cistatic struct msc_window * 3548c2ecf20Sopenharmony_cimsc_find_window(struct msc *msc, struct sg_table *sgt, bool nonempty) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct msc_window *win; 3578c2ecf20Sopenharmony_ci unsigned int found = 0; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (list_empty(&msc->win_list)) 3608c2ecf20Sopenharmony_ci return NULL; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* 3638c2ecf20Sopenharmony_ci * we might need a radix tree for this, depending on how 3648c2ecf20Sopenharmony_ci * many windows a typical user would allocate; ideally it's 3658c2ecf20Sopenharmony_ci * something like 2, in which case we're good 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci list_for_each_entry(win, &msc->win_list, entry) { 3688c2ecf20Sopenharmony_ci if (win->sgt == sgt) 3698c2ecf20Sopenharmony_ci found++; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* skip the empty ones */ 3728c2ecf20Sopenharmony_ci if (nonempty && msc_block_is_empty(msc_win_base(win))) 3738c2ecf20Sopenharmony_ci continue; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (found) 3768c2ecf20Sopenharmony_ci return win; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return NULL; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/** 3838c2ecf20Sopenharmony_ci * msc_oldest_window() - locate the window with oldest data 3848c2ecf20Sopenharmony_ci * @msc: MSC device 3858c2ecf20Sopenharmony_ci * 3868c2ecf20Sopenharmony_ci * This should only be used in multiblock mode. Caller should hold the 3878c2ecf20Sopenharmony_ci * msc::user_count reference. 3888c2ecf20Sopenharmony_ci * 3898c2ecf20Sopenharmony_ci * Return: the oldest window with valid data 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_cistatic struct msc_window *msc_oldest_window(struct msc *msc) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct msc_window *win; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (list_empty(&msc->win_list)) 3968c2ecf20Sopenharmony_ci return NULL; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci win = msc_find_window(msc, msc_next_window(msc->cur_win)->sgt, true); 3998c2ecf20Sopenharmony_ci if (win) 4008c2ecf20Sopenharmony_ci return win; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return list_first_entry(&msc->win_list, struct msc_window, entry); 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/** 4068c2ecf20Sopenharmony_ci * msc_win_oldest_sg() - locate the oldest block in a given window 4078c2ecf20Sopenharmony_ci * @win: window to look at 4088c2ecf20Sopenharmony_ci * 4098c2ecf20Sopenharmony_ci * Return: index of the block with the oldest data 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_cistatic struct scatterlist *msc_win_oldest_sg(struct msc_window *win) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci unsigned int blk; 4148c2ecf20Sopenharmony_ci struct scatterlist *sg; 4158c2ecf20Sopenharmony_ci struct msc_block_desc *bdesc = msc_win_base(win); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* without wrapping, first block is the oldest */ 4188c2ecf20Sopenharmony_ci if (!msc_block_wrapped(bdesc)) 4198c2ecf20Sopenharmony_ci return msc_win_base_sg(win); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* 4228c2ecf20Sopenharmony_ci * with wrapping, last written block contains both the newest and the 4238c2ecf20Sopenharmony_ci * oldest data for this window. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci for_each_sg(win->sgt->sgl, sg, win->nr_segs, blk) { 4268c2ecf20Sopenharmony_ci struct msc_block_desc *bdesc = sg_virt(sg); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (msc_block_last_written(bdesc)) 4298c2ecf20Sopenharmony_ci return sg; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return msc_win_base_sg(win); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic struct msc_block_desc *msc_iter_bdesc(struct msc_iter *iter) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci return sg_virt(iter->block); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic struct msc_iter *msc_iter_install(struct msc *msc) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct msc_iter *iter; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci iter = kzalloc(sizeof(*iter), GFP_KERNEL); 4458c2ecf20Sopenharmony_ci if (!iter) 4468c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci mutex_lock(&msc->buf_mutex); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* 4518c2ecf20Sopenharmony_ci * Reading and tracing are mutually exclusive; if msc is 4528c2ecf20Sopenharmony_ci * enabled, open() will fail; otherwise existing readers 4538c2ecf20Sopenharmony_ci * will prevent enabling the msc and the rest of fops don't 4548c2ecf20Sopenharmony_ci * need to worry about it. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci if (msc->enabled) { 4578c2ecf20Sopenharmony_ci kfree(iter); 4588c2ecf20Sopenharmony_ci iter = ERR_PTR(-EBUSY); 4598c2ecf20Sopenharmony_ci goto unlock; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci iter->msc = msc; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci list_add_tail(&iter->entry, &msc->iter_list); 4658c2ecf20Sopenharmony_ciunlock: 4668c2ecf20Sopenharmony_ci mutex_unlock(&msc->buf_mutex); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return iter; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic void msc_iter_remove(struct msc_iter *iter, struct msc *msc) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci mutex_lock(&msc->buf_mutex); 4748c2ecf20Sopenharmony_ci list_del(&iter->entry); 4758c2ecf20Sopenharmony_ci mutex_unlock(&msc->buf_mutex); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci kfree(iter); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic void msc_iter_block_start(struct msc_iter *iter) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci if (iter->start_block) 4838c2ecf20Sopenharmony_ci return; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci iter->start_block = msc_win_oldest_sg(iter->win); 4868c2ecf20Sopenharmony_ci iter->block = iter->start_block; 4878c2ecf20Sopenharmony_ci iter->wrap_count = 0; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* 4908c2ecf20Sopenharmony_ci * start with the block with oldest data; if data has wrapped 4918c2ecf20Sopenharmony_ci * in this window, it should be in this block 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ci if (msc_block_wrapped(msc_iter_bdesc(iter))) 4948c2ecf20Sopenharmony_ci iter->wrap_count = 2; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic int msc_iter_win_start(struct msc_iter *iter, struct msc *msc) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci /* already started, nothing to do */ 5018c2ecf20Sopenharmony_ci if (iter->start_win) 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci iter->start_win = msc_oldest_window(msc); 5058c2ecf20Sopenharmony_ci if (!iter->start_win) 5068c2ecf20Sopenharmony_ci return -EINVAL; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci iter->win = iter->start_win; 5098c2ecf20Sopenharmony_ci iter->start_block = NULL; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci msc_iter_block_start(iter); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return 0; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int msc_iter_win_advance(struct msc_iter *iter) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci iter->win = msc_next_window(iter->win); 5198c2ecf20Sopenharmony_ci iter->start_block = NULL; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (iter->win == iter->start_win) { 5228c2ecf20Sopenharmony_ci iter->eof++; 5238c2ecf20Sopenharmony_ci return 1; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci msc_iter_block_start(iter); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int msc_iter_block_advance(struct msc_iter *iter) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci iter->block_off = 0; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* wrapping */ 5368c2ecf20Sopenharmony_ci if (iter->wrap_count && iter->block == iter->start_block) { 5378c2ecf20Sopenharmony_ci iter->wrap_count--; 5388c2ecf20Sopenharmony_ci if (!iter->wrap_count) 5398c2ecf20Sopenharmony_ci /* copied newest data from the wrapped block */ 5408c2ecf20Sopenharmony_ci return msc_iter_win_advance(iter); 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* no wrapping, check for last written block */ 5448c2ecf20Sopenharmony_ci if (!iter->wrap_count && msc_block_last_written(msc_iter_bdesc(iter))) 5458c2ecf20Sopenharmony_ci /* copied newest data for the window */ 5468c2ecf20Sopenharmony_ci return msc_iter_win_advance(iter); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* block advance */ 5498c2ecf20Sopenharmony_ci if (sg_is_last(iter->block)) 5508c2ecf20Sopenharmony_ci iter->block = msc_win_base_sg(iter->win); 5518c2ecf20Sopenharmony_ci else 5528c2ecf20Sopenharmony_ci iter->block = sg_next(iter->block); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* no wrapping, sanity check in case there is no last written block */ 5558c2ecf20Sopenharmony_ci if (!iter->wrap_count && iter->block == iter->start_block) 5568c2ecf20Sopenharmony_ci return msc_iter_win_advance(iter); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci return 0; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/** 5628c2ecf20Sopenharmony_ci * msc_buffer_iterate() - go through multiblock buffer's data 5638c2ecf20Sopenharmony_ci * @iter: iterator structure 5648c2ecf20Sopenharmony_ci * @size: amount of data to scan 5658c2ecf20Sopenharmony_ci * @data: callback's private data 5668c2ecf20Sopenharmony_ci * @fn: iterator callback 5678c2ecf20Sopenharmony_ci * 5688c2ecf20Sopenharmony_ci * This will start at the window which will be written to next (containing 5698c2ecf20Sopenharmony_ci * the oldest data) and work its way to the current window, calling @fn 5708c2ecf20Sopenharmony_ci * for each chunk of data as it goes. 5718c2ecf20Sopenharmony_ci * 5728c2ecf20Sopenharmony_ci * Caller should have msc::user_count reference to make sure the buffer 5738c2ecf20Sopenharmony_ci * doesn't disappear from under us. 5748c2ecf20Sopenharmony_ci * 5758c2ecf20Sopenharmony_ci * Return: amount of data actually scanned. 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_cistatic ssize_t 5788c2ecf20Sopenharmony_cimsc_buffer_iterate(struct msc_iter *iter, size_t size, void *data, 5798c2ecf20Sopenharmony_ci unsigned long (*fn)(void *, void *, size_t)) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct msc *msc = iter->msc; 5828c2ecf20Sopenharmony_ci size_t len = size; 5838c2ecf20Sopenharmony_ci unsigned int advance; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (iter->eof) 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* start with the oldest window */ 5898c2ecf20Sopenharmony_ci if (msc_iter_win_start(iter, msc)) 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci do { 5938c2ecf20Sopenharmony_ci unsigned long data_bytes = msc_data_sz(msc_iter_bdesc(iter)); 5948c2ecf20Sopenharmony_ci void *src = (void *)msc_iter_bdesc(iter) + MSC_BDESC; 5958c2ecf20Sopenharmony_ci size_t tocopy = data_bytes, copied = 0; 5968c2ecf20Sopenharmony_ci size_t remaining = 0; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci advance = 1; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* 6018c2ecf20Sopenharmony_ci * If block wrapping happened, we need to visit the last block 6028c2ecf20Sopenharmony_ci * twice, because it contains both the oldest and the newest 6038c2ecf20Sopenharmony_ci * data in this window. 6048c2ecf20Sopenharmony_ci * 6058c2ecf20Sopenharmony_ci * First time (wrap_count==2), in the very beginning, to collect 6068c2ecf20Sopenharmony_ci * the oldest data, which is in the range 6078c2ecf20Sopenharmony_ci * (data_bytes..DATA_IN_PAGE). 6088c2ecf20Sopenharmony_ci * 6098c2ecf20Sopenharmony_ci * Second time (wrap_count==1), it's just like any other block, 6108c2ecf20Sopenharmony_ci * containing data in the range of [MSC_BDESC..data_bytes]. 6118c2ecf20Sopenharmony_ci */ 6128c2ecf20Sopenharmony_ci if (iter->block == iter->start_block && iter->wrap_count == 2) { 6138c2ecf20Sopenharmony_ci tocopy = DATA_IN_PAGE - data_bytes; 6148c2ecf20Sopenharmony_ci src += data_bytes; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (!tocopy) 6188c2ecf20Sopenharmony_ci goto next_block; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci tocopy -= iter->block_off; 6218c2ecf20Sopenharmony_ci src += iter->block_off; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (len < tocopy) { 6248c2ecf20Sopenharmony_ci tocopy = len; 6258c2ecf20Sopenharmony_ci advance = 0; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci remaining = fn(data, src, tocopy); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (remaining) 6318c2ecf20Sopenharmony_ci advance = 0; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci copied = tocopy - remaining; 6348c2ecf20Sopenharmony_ci len -= copied; 6358c2ecf20Sopenharmony_ci iter->block_off += copied; 6368c2ecf20Sopenharmony_ci iter->offset += copied; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (!advance) 6398c2ecf20Sopenharmony_ci break; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cinext_block: 6428c2ecf20Sopenharmony_ci if (msc_iter_block_advance(iter)) 6438c2ecf20Sopenharmony_ci break; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci } while (len); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci return size - len; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci/** 6518c2ecf20Sopenharmony_ci * msc_buffer_clear_hw_header() - clear hw header for multiblock 6528c2ecf20Sopenharmony_ci * @msc: MSC device 6538c2ecf20Sopenharmony_ci */ 6548c2ecf20Sopenharmony_cistatic void msc_buffer_clear_hw_header(struct msc *msc) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct msc_window *win; 6578c2ecf20Sopenharmony_ci struct scatterlist *sg; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci list_for_each_entry(win, &msc->win_list, entry) { 6608c2ecf20Sopenharmony_ci unsigned int blk; 6618c2ecf20Sopenharmony_ci size_t hw_sz = sizeof(struct msc_block_desc) - 6628c2ecf20Sopenharmony_ci offsetof(struct msc_block_desc, hw_tag); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci for_each_sg(win->sgt->sgl, sg, win->nr_segs, blk) { 6658c2ecf20Sopenharmony_ci struct msc_block_desc *bdesc = sg_virt(sg); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci memset(&bdesc->hw_tag, 0, hw_sz); 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic int intel_th_msu_init(struct msc *msc) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci u32 mintctl, msusts; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (!msc->do_irq) 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (!msc->mbuf) 6808c2ecf20Sopenharmony_ci return 0; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci mintctl = ioread32(msc->msu_base + REG_MSU_MINTCTL); 6838c2ecf20Sopenharmony_ci mintctl |= msc->index ? M1BLIE : M0BLIE; 6848c2ecf20Sopenharmony_ci iowrite32(mintctl, msc->msu_base + REG_MSU_MINTCTL); 6858c2ecf20Sopenharmony_ci if (mintctl != ioread32(msc->msu_base + REG_MSU_MINTCTL)) { 6868c2ecf20Sopenharmony_ci dev_info(msc_dev(msc), "MINTCTL ignores writes: no usable interrupts\n"); 6878c2ecf20Sopenharmony_ci msc->do_irq = 0; 6888c2ecf20Sopenharmony_ci return 0; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci msusts = ioread32(msc->msu_base + REG_MSU_MSUSTS); 6928c2ecf20Sopenharmony_ci iowrite32(msusts, msc->msu_base + REG_MSU_MSUSTS); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return 0; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic void intel_th_msu_deinit(struct msc *msc) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci u32 mintctl; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (!msc->do_irq) 7028c2ecf20Sopenharmony_ci return; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci mintctl = ioread32(msc->msu_base + REG_MSU_MINTCTL); 7058c2ecf20Sopenharmony_ci mintctl &= msc->index ? ~M1BLIE : ~M0BLIE; 7068c2ecf20Sopenharmony_ci iowrite32(mintctl, msc->msu_base + REG_MSU_MINTCTL); 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic int msc_win_set_lockout(struct msc_window *win, 7108c2ecf20Sopenharmony_ci enum lockout_state expect, 7118c2ecf20Sopenharmony_ci enum lockout_state new) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci enum lockout_state old; 7148c2ecf20Sopenharmony_ci unsigned long flags; 7158c2ecf20Sopenharmony_ci int ret = 0; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (!win->msc->mbuf) 7188c2ecf20Sopenharmony_ci return 0; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci spin_lock_irqsave(&win->lo_lock, flags); 7218c2ecf20Sopenharmony_ci old = win->lockout; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (old != expect) { 7248c2ecf20Sopenharmony_ci ret = -EINVAL; 7258c2ecf20Sopenharmony_ci goto unlock; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci win->lockout = new; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (old == expect && new == WIN_LOCKED) 7318c2ecf20Sopenharmony_ci atomic_inc(&win->msc->user_count); 7328c2ecf20Sopenharmony_ci else if (old == expect && old == WIN_LOCKED) 7338c2ecf20Sopenharmony_ci atomic_dec(&win->msc->user_count); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ciunlock: 7368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&win->lo_lock, flags); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (ret) { 7398c2ecf20Sopenharmony_ci if (expect == WIN_READY && old == WIN_LOCKED) 7408c2ecf20Sopenharmony_ci return -EBUSY; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* from intel_th_msc_window_unlock(), don't warn if not locked */ 7438c2ecf20Sopenharmony_ci if (expect == WIN_LOCKED && old == new) 7448c2ecf20Sopenharmony_ci return 0; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci dev_warn_ratelimited(msc_dev(win->msc), 7478c2ecf20Sopenharmony_ci "expected lockout state %d, got %d\n", 7488c2ecf20Sopenharmony_ci expect, old); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci return ret; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci/** 7548c2ecf20Sopenharmony_ci * msc_configure() - set up MSC hardware 7558c2ecf20Sopenharmony_ci * @msc: the MSC device to configure 7568c2ecf20Sopenharmony_ci * 7578c2ecf20Sopenharmony_ci * Program storage mode, wrapping, burst length and trace buffer address 7588c2ecf20Sopenharmony_ci * into a given MSC. Then, enable tracing and set msc::enabled. 7598c2ecf20Sopenharmony_ci * The latter is serialized on msc::buf_mutex, so make sure to hold it. 7608c2ecf20Sopenharmony_ci */ 7618c2ecf20Sopenharmony_cistatic int msc_configure(struct msc *msc) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci u32 reg; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci lockdep_assert_held(&msc->buf_mutex); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (msc->mode > MSC_MODE_MULTI) 7688c2ecf20Sopenharmony_ci return -EINVAL; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if (msc->mode == MSC_MODE_MULTI) { 7718c2ecf20Sopenharmony_ci if (msc_win_set_lockout(msc->cur_win, WIN_READY, WIN_INUSE)) 7728c2ecf20Sopenharmony_ci return -EBUSY; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci msc_buffer_clear_hw_header(msc); 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci msc->orig_addr = ioread32(msc->reg_base + REG_MSU_MSC0BAR); 7788c2ecf20Sopenharmony_ci msc->orig_sz = ioread32(msc->reg_base + REG_MSU_MSC0SIZE); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci reg = msc->base_addr >> PAGE_SHIFT; 7818c2ecf20Sopenharmony_ci iowrite32(reg, msc->reg_base + REG_MSU_MSC0BAR); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (msc->mode == MSC_MODE_SINGLE) { 7848c2ecf20Sopenharmony_ci reg = msc->nr_pages; 7858c2ecf20Sopenharmony_ci iowrite32(reg, msc->reg_base + REG_MSU_MSC0SIZE); 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci reg = ioread32(msc->reg_base + REG_MSU_MSC0CTL); 7898c2ecf20Sopenharmony_ci reg &= ~(MSC_MODE | MSC_WRAPEN | MSC_EN | MSC_RD_HDR_OVRD); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci reg |= MSC_EN; 7928c2ecf20Sopenharmony_ci reg |= msc->mode << __ffs(MSC_MODE); 7938c2ecf20Sopenharmony_ci reg |= msc->burst_len << __ffs(MSC_LEN); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (msc->wrap) 7968c2ecf20Sopenharmony_ci reg |= MSC_WRAPEN; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci iowrite32(reg, msc->reg_base + REG_MSU_MSC0CTL); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci intel_th_msu_init(msc); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci msc->thdev->output.multiblock = msc->mode == MSC_MODE_MULTI; 8038c2ecf20Sopenharmony_ci intel_th_trace_enable(msc->thdev); 8048c2ecf20Sopenharmony_ci msc->enabled = 1; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (msc->mbuf && msc->mbuf->activate) 8078c2ecf20Sopenharmony_ci msc->mbuf->activate(msc->mbuf_priv); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci return 0; 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci/** 8138c2ecf20Sopenharmony_ci * msc_disable() - disable MSC hardware 8148c2ecf20Sopenharmony_ci * @msc: MSC device to disable 8158c2ecf20Sopenharmony_ci * 8168c2ecf20Sopenharmony_ci * If @msc is enabled, disable tracing on the switch and then disable MSC 8178c2ecf20Sopenharmony_ci * storage. Caller must hold msc::buf_mutex. 8188c2ecf20Sopenharmony_ci */ 8198c2ecf20Sopenharmony_cistatic void msc_disable(struct msc *msc) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci struct msc_window *win = msc->cur_win; 8228c2ecf20Sopenharmony_ci u32 reg; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci lockdep_assert_held(&msc->buf_mutex); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (msc->mode == MSC_MODE_MULTI) 8278c2ecf20Sopenharmony_ci msc_win_set_lockout(win, WIN_INUSE, WIN_LOCKED); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (msc->mbuf && msc->mbuf->deactivate) 8308c2ecf20Sopenharmony_ci msc->mbuf->deactivate(msc->mbuf_priv); 8318c2ecf20Sopenharmony_ci intel_th_msu_deinit(msc); 8328c2ecf20Sopenharmony_ci intel_th_trace_disable(msc->thdev); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (msc->mode == MSC_MODE_SINGLE) { 8358c2ecf20Sopenharmony_ci reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); 8368c2ecf20Sopenharmony_ci msc->single_wrap = !!(reg & MSCSTS_WRAPSTAT); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci reg = ioread32(msc->reg_base + REG_MSU_MSC0MWP); 8398c2ecf20Sopenharmony_ci msc->single_sz = reg & ((msc->nr_pages << PAGE_SHIFT) - 1); 8408c2ecf20Sopenharmony_ci dev_dbg(msc_dev(msc), "MSCnMWP: %08x/%08lx, wrap: %d\n", 8418c2ecf20Sopenharmony_ci reg, msc->single_sz, msc->single_wrap); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci reg = ioread32(msc->reg_base + REG_MSU_MSC0CTL); 8458c2ecf20Sopenharmony_ci reg &= ~MSC_EN; 8468c2ecf20Sopenharmony_ci iowrite32(reg, msc->reg_base + REG_MSU_MSC0CTL); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (msc->mbuf && msc->mbuf->ready) 8498c2ecf20Sopenharmony_ci msc->mbuf->ready(msc->mbuf_priv, win->sgt, 8508c2ecf20Sopenharmony_ci msc_win_total_sz(win)); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci msc->enabled = 0; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci iowrite32(msc->orig_addr, msc->reg_base + REG_MSU_MSC0BAR); 8558c2ecf20Sopenharmony_ci iowrite32(msc->orig_sz, msc->reg_base + REG_MSU_MSC0SIZE); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci dev_dbg(msc_dev(msc), "MSCnNWSA: %08x\n", 8588c2ecf20Sopenharmony_ci ioread32(msc->reg_base + REG_MSU_MSC0NWSA)); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); 8618c2ecf20Sopenharmony_ci dev_dbg(msc_dev(msc), "MSCnSTS: %08x\n", reg); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci reg = ioread32(msc->reg_base + REG_MSU_MSUSTS); 8648c2ecf20Sopenharmony_ci reg &= msc->index ? MSUSTS_MSC1BLAST : MSUSTS_MSC0BLAST; 8658c2ecf20Sopenharmony_ci iowrite32(reg, msc->reg_base + REG_MSU_MSUSTS); 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic int intel_th_msc_activate(struct intel_th_device *thdev) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(&thdev->dev); 8718c2ecf20Sopenharmony_ci int ret = -EBUSY; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (!atomic_inc_unless_negative(&msc->user_count)) 8748c2ecf20Sopenharmony_ci return -ENODEV; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci mutex_lock(&msc->buf_mutex); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* if there are readers, refuse */ 8798c2ecf20Sopenharmony_ci if (list_empty(&msc->iter_list)) 8808c2ecf20Sopenharmony_ci ret = msc_configure(msc); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci mutex_unlock(&msc->buf_mutex); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci if (ret) 8858c2ecf20Sopenharmony_ci atomic_dec(&msc->user_count); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci return ret; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic void intel_th_msc_deactivate(struct intel_th_device *thdev) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(&thdev->dev); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci mutex_lock(&msc->buf_mutex); 8958c2ecf20Sopenharmony_ci if (msc->enabled) { 8968c2ecf20Sopenharmony_ci msc_disable(msc); 8978c2ecf20Sopenharmony_ci atomic_dec(&msc->user_count); 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci mutex_unlock(&msc->buf_mutex); 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci/** 9038c2ecf20Sopenharmony_ci * msc_buffer_contig_alloc() - allocate a contiguous buffer for SINGLE mode 9048c2ecf20Sopenharmony_ci * @msc: MSC device 9058c2ecf20Sopenharmony_ci * @size: allocation size in bytes 9068c2ecf20Sopenharmony_ci * 9078c2ecf20Sopenharmony_ci * This modifies msc::base, which requires msc::buf_mutex to serialize, so the 9088c2ecf20Sopenharmony_ci * caller is expected to hold it. 9098c2ecf20Sopenharmony_ci * 9108c2ecf20Sopenharmony_ci * Return: 0 on success, -errno otherwise. 9118c2ecf20Sopenharmony_ci */ 9128c2ecf20Sopenharmony_cistatic int msc_buffer_contig_alloc(struct msc *msc, unsigned long size) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci unsigned long nr_pages = size >> PAGE_SHIFT; 9158c2ecf20Sopenharmony_ci unsigned int order = get_order(size); 9168c2ecf20Sopenharmony_ci struct page *page; 9178c2ecf20Sopenharmony_ci int ret; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci if (!size) 9208c2ecf20Sopenharmony_ci return 0; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci ret = sg_alloc_table(&msc->single_sgt, 1, GFP_KERNEL); 9238c2ecf20Sopenharmony_ci if (ret) 9248c2ecf20Sopenharmony_ci goto err_out; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci ret = -ENOMEM; 9278c2ecf20Sopenharmony_ci page = alloc_pages(GFP_KERNEL | __GFP_ZERO | GFP_DMA32, order); 9288c2ecf20Sopenharmony_ci if (!page) 9298c2ecf20Sopenharmony_ci goto err_free_sgt; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci split_page(page, order); 9328c2ecf20Sopenharmony_ci sg_set_buf(msc->single_sgt.sgl, page_address(page), size); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci ret = dma_map_sg(msc_dev(msc)->parent->parent, msc->single_sgt.sgl, 1, 9358c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 9368c2ecf20Sopenharmony_ci if (ret < 0) 9378c2ecf20Sopenharmony_ci goto err_free_pages; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci msc->nr_pages = nr_pages; 9408c2ecf20Sopenharmony_ci msc->base = page_address(page); 9418c2ecf20Sopenharmony_ci msc->base_addr = sg_dma_address(msc->single_sgt.sgl); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci return 0; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_cierr_free_pages: 9468c2ecf20Sopenharmony_ci __free_pages(page, order); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cierr_free_sgt: 9498c2ecf20Sopenharmony_ci sg_free_table(&msc->single_sgt); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cierr_out: 9528c2ecf20Sopenharmony_ci return ret; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci/** 9568c2ecf20Sopenharmony_ci * msc_buffer_contig_free() - free a contiguous buffer 9578c2ecf20Sopenharmony_ci * @msc: MSC configured in SINGLE mode 9588c2ecf20Sopenharmony_ci */ 9598c2ecf20Sopenharmony_cistatic void msc_buffer_contig_free(struct msc *msc) 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci unsigned long off; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci dma_unmap_sg(msc_dev(msc)->parent->parent, msc->single_sgt.sgl, 9648c2ecf20Sopenharmony_ci 1, DMA_FROM_DEVICE); 9658c2ecf20Sopenharmony_ci sg_free_table(&msc->single_sgt); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci for (off = 0; off < msc->nr_pages << PAGE_SHIFT; off += PAGE_SIZE) { 9688c2ecf20Sopenharmony_ci struct page *page = virt_to_page(msc->base + off); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci page->mapping = NULL; 9718c2ecf20Sopenharmony_ci __free_page(page); 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci msc->nr_pages = 0; 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci/** 9788c2ecf20Sopenharmony_ci * msc_buffer_contig_get_page() - find a page at a given offset 9798c2ecf20Sopenharmony_ci * @msc: MSC configured in SINGLE mode 9808c2ecf20Sopenharmony_ci * @pgoff: page offset 9818c2ecf20Sopenharmony_ci * 9828c2ecf20Sopenharmony_ci * Return: page, if @pgoff is within the range, NULL otherwise. 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_cistatic struct page *msc_buffer_contig_get_page(struct msc *msc, 9858c2ecf20Sopenharmony_ci unsigned long pgoff) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci if (pgoff >= msc->nr_pages) 9888c2ecf20Sopenharmony_ci return NULL; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return virt_to_page(msc->base + (pgoff << PAGE_SHIFT)); 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic int __msc_buffer_win_alloc(struct msc_window *win, 9948c2ecf20Sopenharmony_ci unsigned int nr_segs) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct scatterlist *sg_ptr; 9978c2ecf20Sopenharmony_ci void *block; 9988c2ecf20Sopenharmony_ci int i, ret; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci ret = sg_alloc_table(win->sgt, nr_segs, GFP_KERNEL); 10018c2ecf20Sopenharmony_ci if (ret) 10028c2ecf20Sopenharmony_ci return -ENOMEM; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci for_each_sg(win->sgt->sgl, sg_ptr, nr_segs, i) { 10058c2ecf20Sopenharmony_ci block = dma_alloc_coherent(msc_dev(win->msc)->parent->parent, 10068c2ecf20Sopenharmony_ci PAGE_SIZE, &sg_dma_address(sg_ptr), 10078c2ecf20Sopenharmony_ci GFP_KERNEL); 10088c2ecf20Sopenharmony_ci if (!block) 10098c2ecf20Sopenharmony_ci goto err_nomem; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci sg_set_buf(sg_ptr, block, PAGE_SIZE); 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci return nr_segs; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_cierr_nomem: 10178c2ecf20Sopenharmony_ci for_each_sg(win->sgt->sgl, sg_ptr, i, ret) 10188c2ecf20Sopenharmony_ci dma_free_coherent(msc_dev(win->msc)->parent->parent, PAGE_SIZE, 10198c2ecf20Sopenharmony_ci sg_virt(sg_ptr), sg_dma_address(sg_ptr)); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci sg_free_table(win->sgt); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci return -ENOMEM; 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 10278c2ecf20Sopenharmony_cistatic void msc_buffer_set_uc(struct msc_window *win, unsigned int nr_segs) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci struct scatterlist *sg_ptr; 10308c2ecf20Sopenharmony_ci int i; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci for_each_sg(win->sgt->sgl, sg_ptr, nr_segs, i) { 10338c2ecf20Sopenharmony_ci /* Set the page as uncached */ 10348c2ecf20Sopenharmony_ci set_memory_uc((unsigned long)sg_virt(sg_ptr), 10358c2ecf20Sopenharmony_ci PFN_DOWN(sg_ptr->length)); 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic void msc_buffer_set_wb(struct msc_window *win) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci struct scatterlist *sg_ptr; 10428c2ecf20Sopenharmony_ci int i; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci for_each_sg(win->sgt->sgl, sg_ptr, win->nr_segs, i) { 10458c2ecf20Sopenharmony_ci /* Reset the page to write-back */ 10468c2ecf20Sopenharmony_ci set_memory_wb((unsigned long)sg_virt(sg_ptr), 10478c2ecf20Sopenharmony_ci PFN_DOWN(sg_ptr->length)); 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ci#else /* !X86 */ 10518c2ecf20Sopenharmony_cistatic inline void 10528c2ecf20Sopenharmony_cimsc_buffer_set_uc(struct msc_window *win, unsigned int nr_segs) {} 10538c2ecf20Sopenharmony_cistatic inline void msc_buffer_set_wb(struct msc_window *win) {} 10548c2ecf20Sopenharmony_ci#endif /* CONFIG_X86 */ 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic struct page *msc_sg_page(struct scatterlist *sg) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci void *addr = sg_virt(sg); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci if (is_vmalloc_addr(addr)) 10618c2ecf20Sopenharmony_ci return vmalloc_to_page(addr); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci return sg_page(sg); 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci/** 10678c2ecf20Sopenharmony_ci * msc_buffer_win_alloc() - alloc a window for a multiblock mode 10688c2ecf20Sopenharmony_ci * @msc: MSC device 10698c2ecf20Sopenharmony_ci * @nr_blocks: number of pages in this window 10708c2ecf20Sopenharmony_ci * 10718c2ecf20Sopenharmony_ci * This modifies msc::win_list and msc::base, which requires msc::buf_mutex 10728c2ecf20Sopenharmony_ci * to serialize, so the caller is expected to hold it. 10738c2ecf20Sopenharmony_ci * 10748c2ecf20Sopenharmony_ci * Return: 0 on success, -errno otherwise. 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_cistatic int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci struct msc_window *win; 10798c2ecf20Sopenharmony_ci int ret = -ENOMEM; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (!nr_blocks) 10828c2ecf20Sopenharmony_ci return 0; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci win = kzalloc(sizeof(*win), GFP_KERNEL); 10858c2ecf20Sopenharmony_ci if (!win) 10868c2ecf20Sopenharmony_ci return -ENOMEM; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci win->msc = msc; 10898c2ecf20Sopenharmony_ci win->sgt = &win->_sgt; 10908c2ecf20Sopenharmony_ci win->lockout = WIN_READY; 10918c2ecf20Sopenharmony_ci spin_lock_init(&win->lo_lock); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if (!list_empty(&msc->win_list)) { 10948c2ecf20Sopenharmony_ci struct msc_window *prev = list_last_entry(&msc->win_list, 10958c2ecf20Sopenharmony_ci struct msc_window, 10968c2ecf20Sopenharmony_ci entry); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci win->pgoff = prev->pgoff + prev->nr_blocks; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (msc->mbuf && msc->mbuf->alloc_window) 11028c2ecf20Sopenharmony_ci ret = msc->mbuf->alloc_window(msc->mbuf_priv, &win->sgt, 11038c2ecf20Sopenharmony_ci nr_blocks << PAGE_SHIFT); 11048c2ecf20Sopenharmony_ci else 11058c2ecf20Sopenharmony_ci ret = __msc_buffer_win_alloc(win, nr_blocks); 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (ret <= 0) 11088c2ecf20Sopenharmony_ci goto err_nomem; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci msc_buffer_set_uc(win, ret); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci win->nr_segs = ret; 11138c2ecf20Sopenharmony_ci win->nr_blocks = nr_blocks; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (list_empty(&msc->win_list)) { 11168c2ecf20Sopenharmony_ci msc->base = msc_win_base(win); 11178c2ecf20Sopenharmony_ci msc->base_addr = msc_win_base_dma(win); 11188c2ecf20Sopenharmony_ci msc->cur_win = win; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci list_add_tail(&win->entry, &msc->win_list); 11228c2ecf20Sopenharmony_ci msc->nr_pages += nr_blocks; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci return 0; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_cierr_nomem: 11278c2ecf20Sopenharmony_ci kfree(win); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci return ret; 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic void __msc_buffer_win_free(struct msc *msc, struct msc_window *win) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci struct scatterlist *sg; 11358c2ecf20Sopenharmony_ci int i; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci for_each_sg(win->sgt->sgl, sg, win->nr_segs, i) { 11388c2ecf20Sopenharmony_ci struct page *page = msc_sg_page(sg); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci page->mapping = NULL; 11418c2ecf20Sopenharmony_ci dma_free_coherent(msc_dev(win->msc)->parent->parent, PAGE_SIZE, 11428c2ecf20Sopenharmony_ci sg_virt(sg), sg_dma_address(sg)); 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci sg_free_table(win->sgt); 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci/** 11488c2ecf20Sopenharmony_ci * msc_buffer_win_free() - free a window from MSC's window list 11498c2ecf20Sopenharmony_ci * @msc: MSC device 11508c2ecf20Sopenharmony_ci * @win: window to free 11518c2ecf20Sopenharmony_ci * 11528c2ecf20Sopenharmony_ci * This modifies msc::win_list and msc::base, which requires msc::buf_mutex 11538c2ecf20Sopenharmony_ci * to serialize, so the caller is expected to hold it. 11548c2ecf20Sopenharmony_ci */ 11558c2ecf20Sopenharmony_cistatic void msc_buffer_win_free(struct msc *msc, struct msc_window *win) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci msc->nr_pages -= win->nr_blocks; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci list_del(&win->entry); 11608c2ecf20Sopenharmony_ci if (list_empty(&msc->win_list)) { 11618c2ecf20Sopenharmony_ci msc->base = NULL; 11628c2ecf20Sopenharmony_ci msc->base_addr = 0; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci msc_buffer_set_wb(win); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci if (msc->mbuf && msc->mbuf->free_window) 11688c2ecf20Sopenharmony_ci msc->mbuf->free_window(msc->mbuf_priv, win->sgt); 11698c2ecf20Sopenharmony_ci else 11708c2ecf20Sopenharmony_ci __msc_buffer_win_free(msc, win); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci kfree(win); 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci/** 11768c2ecf20Sopenharmony_ci * msc_buffer_relink() - set up block descriptors for multiblock mode 11778c2ecf20Sopenharmony_ci * @msc: MSC device 11788c2ecf20Sopenharmony_ci * 11798c2ecf20Sopenharmony_ci * This traverses msc::win_list, which requires msc::buf_mutex to serialize, 11808c2ecf20Sopenharmony_ci * so the caller is expected to hold it. 11818c2ecf20Sopenharmony_ci */ 11828c2ecf20Sopenharmony_cistatic void msc_buffer_relink(struct msc *msc) 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci struct msc_window *win, *next_win; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* call with msc::mutex locked */ 11878c2ecf20Sopenharmony_ci list_for_each_entry(win, &msc->win_list, entry) { 11888c2ecf20Sopenharmony_ci struct scatterlist *sg; 11898c2ecf20Sopenharmony_ci unsigned int blk; 11908c2ecf20Sopenharmony_ci u32 sw_tag = 0; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* 11938c2ecf20Sopenharmony_ci * Last window's next_win should point to the first window 11948c2ecf20Sopenharmony_ci * and MSC_SW_TAG_LASTWIN should be set. 11958c2ecf20Sopenharmony_ci */ 11968c2ecf20Sopenharmony_ci if (msc_is_last_win(win)) { 11978c2ecf20Sopenharmony_ci sw_tag |= MSC_SW_TAG_LASTWIN; 11988c2ecf20Sopenharmony_ci next_win = list_first_entry(&msc->win_list, 11998c2ecf20Sopenharmony_ci struct msc_window, entry); 12008c2ecf20Sopenharmony_ci } else { 12018c2ecf20Sopenharmony_ci next_win = list_next_entry(win, entry); 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci for_each_sg(win->sgt->sgl, sg, win->nr_segs, blk) { 12058c2ecf20Sopenharmony_ci struct msc_block_desc *bdesc = sg_virt(sg); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci memset(bdesc, 0, sizeof(*bdesc)); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci bdesc->next_win = msc_win_base_pfn(next_win); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci /* 12128c2ecf20Sopenharmony_ci * Similarly to last window, last block should point 12138c2ecf20Sopenharmony_ci * to the first one. 12148c2ecf20Sopenharmony_ci */ 12158c2ecf20Sopenharmony_ci if (blk == win->nr_segs - 1) { 12168c2ecf20Sopenharmony_ci sw_tag |= MSC_SW_TAG_LASTBLK; 12178c2ecf20Sopenharmony_ci bdesc->next_blk = msc_win_base_pfn(win); 12188c2ecf20Sopenharmony_ci } else { 12198c2ecf20Sopenharmony_ci dma_addr_t addr = sg_dma_address(sg_next(sg)); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci bdesc->next_blk = PFN_DOWN(addr); 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci bdesc->sw_tag = sw_tag; 12258c2ecf20Sopenharmony_ci bdesc->block_sz = sg->length / 64; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* 12308c2ecf20Sopenharmony_ci * Make the above writes globally visible before tracing is 12318c2ecf20Sopenharmony_ci * enabled to make sure hardware sees them coherently. 12328c2ecf20Sopenharmony_ci */ 12338c2ecf20Sopenharmony_ci wmb(); 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic void msc_buffer_multi_free(struct msc *msc) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci struct msc_window *win, *iter; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci list_for_each_entry_safe(win, iter, &msc->win_list, entry) 12418c2ecf20Sopenharmony_ci msc_buffer_win_free(msc, win); 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic int msc_buffer_multi_alloc(struct msc *msc, unsigned long *nr_pages, 12458c2ecf20Sopenharmony_ci unsigned int nr_wins) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci int ret, i; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci for (i = 0; i < nr_wins; i++) { 12508c2ecf20Sopenharmony_ci ret = msc_buffer_win_alloc(msc, nr_pages[i]); 12518c2ecf20Sopenharmony_ci if (ret) { 12528c2ecf20Sopenharmony_ci msc_buffer_multi_free(msc); 12538c2ecf20Sopenharmony_ci return ret; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci msc_buffer_relink(msc); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci return 0; 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci/** 12638c2ecf20Sopenharmony_ci * msc_buffer_free() - free buffers for MSC 12648c2ecf20Sopenharmony_ci * @msc: MSC device 12658c2ecf20Sopenharmony_ci * 12668c2ecf20Sopenharmony_ci * Free MSC's storage buffers. 12678c2ecf20Sopenharmony_ci * 12688c2ecf20Sopenharmony_ci * This modifies msc::win_list and msc::base, which requires msc::buf_mutex to 12698c2ecf20Sopenharmony_ci * serialize, so the caller is expected to hold it. 12708c2ecf20Sopenharmony_ci */ 12718c2ecf20Sopenharmony_cistatic void msc_buffer_free(struct msc *msc) 12728c2ecf20Sopenharmony_ci{ 12738c2ecf20Sopenharmony_ci if (msc->mode == MSC_MODE_SINGLE) 12748c2ecf20Sopenharmony_ci msc_buffer_contig_free(msc); 12758c2ecf20Sopenharmony_ci else if (msc->mode == MSC_MODE_MULTI) 12768c2ecf20Sopenharmony_ci msc_buffer_multi_free(msc); 12778c2ecf20Sopenharmony_ci} 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci/** 12808c2ecf20Sopenharmony_ci * msc_buffer_alloc() - allocate a buffer for MSC 12818c2ecf20Sopenharmony_ci * @msc: MSC device 12828c2ecf20Sopenharmony_ci * @size: allocation size in bytes 12838c2ecf20Sopenharmony_ci * 12848c2ecf20Sopenharmony_ci * Allocate a storage buffer for MSC, depending on the msc::mode, it will be 12858c2ecf20Sopenharmony_ci * either done via msc_buffer_contig_alloc() for SINGLE operation mode or 12868c2ecf20Sopenharmony_ci * msc_buffer_win_alloc() for multiblock operation. The latter allocates one 12878c2ecf20Sopenharmony_ci * window per invocation, so in multiblock mode this can be called multiple 12888c2ecf20Sopenharmony_ci * times for the same MSC to allocate multiple windows. 12898c2ecf20Sopenharmony_ci * 12908c2ecf20Sopenharmony_ci * This modifies msc::win_list and msc::base, which requires msc::buf_mutex 12918c2ecf20Sopenharmony_ci * to serialize, so the caller is expected to hold it. 12928c2ecf20Sopenharmony_ci * 12938c2ecf20Sopenharmony_ci * Return: 0 on success, -errno otherwise. 12948c2ecf20Sopenharmony_ci */ 12958c2ecf20Sopenharmony_cistatic int msc_buffer_alloc(struct msc *msc, unsigned long *nr_pages, 12968c2ecf20Sopenharmony_ci unsigned int nr_wins) 12978c2ecf20Sopenharmony_ci{ 12988c2ecf20Sopenharmony_ci int ret; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci /* -1: buffer not allocated */ 13018c2ecf20Sopenharmony_ci if (atomic_read(&msc->user_count) != -1) 13028c2ecf20Sopenharmony_ci return -EBUSY; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci if (msc->mode == MSC_MODE_SINGLE) { 13058c2ecf20Sopenharmony_ci if (nr_wins != 1) 13068c2ecf20Sopenharmony_ci return -EINVAL; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci ret = msc_buffer_contig_alloc(msc, nr_pages[0] << PAGE_SHIFT); 13098c2ecf20Sopenharmony_ci } else if (msc->mode == MSC_MODE_MULTI) { 13108c2ecf20Sopenharmony_ci ret = msc_buffer_multi_alloc(msc, nr_pages, nr_wins); 13118c2ecf20Sopenharmony_ci } else { 13128c2ecf20Sopenharmony_ci ret = -EINVAL; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (!ret) { 13168c2ecf20Sopenharmony_ci /* allocation should be visible before the counter goes to 0 */ 13178c2ecf20Sopenharmony_ci smp_mb__before_atomic(); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(atomic_cmpxchg(&msc->user_count, -1, 0) != -1)) 13208c2ecf20Sopenharmony_ci return -EINVAL; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci return ret; 13248c2ecf20Sopenharmony_ci} 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci/** 13278c2ecf20Sopenharmony_ci * msc_buffer_unlocked_free_unless_used() - free a buffer unless it's in use 13288c2ecf20Sopenharmony_ci * @msc: MSC device 13298c2ecf20Sopenharmony_ci * 13308c2ecf20Sopenharmony_ci * This will free MSC buffer unless it is in use or there is no allocated 13318c2ecf20Sopenharmony_ci * buffer. 13328c2ecf20Sopenharmony_ci * Caller needs to hold msc::buf_mutex. 13338c2ecf20Sopenharmony_ci * 13348c2ecf20Sopenharmony_ci * Return: 0 on successful deallocation or if there was no buffer to 13358c2ecf20Sopenharmony_ci * deallocate, -EBUSY if there are active users. 13368c2ecf20Sopenharmony_ci */ 13378c2ecf20Sopenharmony_cistatic int msc_buffer_unlocked_free_unless_used(struct msc *msc) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci int count, ret = 0; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci count = atomic_cmpxchg(&msc->user_count, 0, -1); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* > 0: buffer is allocated and has users */ 13448c2ecf20Sopenharmony_ci if (count > 0) 13458c2ecf20Sopenharmony_ci ret = -EBUSY; 13468c2ecf20Sopenharmony_ci /* 0: buffer is allocated, no users */ 13478c2ecf20Sopenharmony_ci else if (!count) 13488c2ecf20Sopenharmony_ci msc_buffer_free(msc); 13498c2ecf20Sopenharmony_ci /* < 0: no buffer, nothing to do */ 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci return ret; 13528c2ecf20Sopenharmony_ci} 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci/** 13558c2ecf20Sopenharmony_ci * msc_buffer_free_unless_used() - free a buffer unless it's in use 13568c2ecf20Sopenharmony_ci * @msc: MSC device 13578c2ecf20Sopenharmony_ci * 13588c2ecf20Sopenharmony_ci * This is a locked version of msc_buffer_unlocked_free_unless_used(). 13598c2ecf20Sopenharmony_ci */ 13608c2ecf20Sopenharmony_cistatic int msc_buffer_free_unless_used(struct msc *msc) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci int ret; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci mutex_lock(&msc->buf_mutex); 13658c2ecf20Sopenharmony_ci ret = msc_buffer_unlocked_free_unless_used(msc); 13668c2ecf20Sopenharmony_ci mutex_unlock(&msc->buf_mutex); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci return ret; 13698c2ecf20Sopenharmony_ci} 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci/** 13728c2ecf20Sopenharmony_ci * msc_buffer_get_page() - get MSC buffer page at a given offset 13738c2ecf20Sopenharmony_ci * @msc: MSC device 13748c2ecf20Sopenharmony_ci * @pgoff: page offset into the storage buffer 13758c2ecf20Sopenharmony_ci * 13768c2ecf20Sopenharmony_ci * This traverses msc::win_list, so holding msc::buf_mutex is expected from 13778c2ecf20Sopenharmony_ci * the caller. 13788c2ecf20Sopenharmony_ci * 13798c2ecf20Sopenharmony_ci * Return: page if @pgoff corresponds to a valid buffer page or NULL. 13808c2ecf20Sopenharmony_ci */ 13818c2ecf20Sopenharmony_cistatic struct page *msc_buffer_get_page(struct msc *msc, unsigned long pgoff) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci struct msc_window *win; 13848c2ecf20Sopenharmony_ci struct scatterlist *sg; 13858c2ecf20Sopenharmony_ci unsigned int blk; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci if (msc->mode == MSC_MODE_SINGLE) 13888c2ecf20Sopenharmony_ci return msc_buffer_contig_get_page(msc, pgoff); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci list_for_each_entry(win, &msc->win_list, entry) 13918c2ecf20Sopenharmony_ci if (pgoff >= win->pgoff && pgoff < win->pgoff + win->nr_blocks) 13928c2ecf20Sopenharmony_ci goto found; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci return NULL; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cifound: 13978c2ecf20Sopenharmony_ci pgoff -= win->pgoff; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci for_each_sg(win->sgt->sgl, sg, win->nr_segs, blk) { 14008c2ecf20Sopenharmony_ci struct page *page = msc_sg_page(sg); 14018c2ecf20Sopenharmony_ci size_t pgsz = PFN_DOWN(sg->length); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci if (pgoff < pgsz) 14048c2ecf20Sopenharmony_ci return page + pgoff; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci pgoff -= pgsz; 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci return NULL; 14108c2ecf20Sopenharmony_ci} 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci/** 14138c2ecf20Sopenharmony_ci * struct msc_win_to_user_struct - data for copy_to_user() callback 14148c2ecf20Sopenharmony_ci * @buf: userspace buffer to copy data to 14158c2ecf20Sopenharmony_ci * @offset: running offset 14168c2ecf20Sopenharmony_ci */ 14178c2ecf20Sopenharmony_cistruct msc_win_to_user_struct { 14188c2ecf20Sopenharmony_ci char __user *buf; 14198c2ecf20Sopenharmony_ci unsigned long offset; 14208c2ecf20Sopenharmony_ci}; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci/** 14238c2ecf20Sopenharmony_ci * msc_win_to_user() - iterator for msc_buffer_iterate() to copy data to user 14248c2ecf20Sopenharmony_ci * @data: callback's private data 14258c2ecf20Sopenharmony_ci * @src: source buffer 14268c2ecf20Sopenharmony_ci * @len: amount of data to copy from the source buffer 14278c2ecf20Sopenharmony_ci */ 14288c2ecf20Sopenharmony_cistatic unsigned long msc_win_to_user(void *data, void *src, size_t len) 14298c2ecf20Sopenharmony_ci{ 14308c2ecf20Sopenharmony_ci struct msc_win_to_user_struct *u = data; 14318c2ecf20Sopenharmony_ci unsigned long ret; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci ret = copy_to_user(u->buf + u->offset, src, len); 14348c2ecf20Sopenharmony_ci u->offset += len - ret; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci return ret; 14378c2ecf20Sopenharmony_ci} 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci/* 14418c2ecf20Sopenharmony_ci * file operations' callbacks 14428c2ecf20Sopenharmony_ci */ 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_cistatic int intel_th_msc_open(struct inode *inode, struct file *file) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci struct intel_th_device *thdev = file->private_data; 14478c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(&thdev->dev); 14488c2ecf20Sopenharmony_ci struct msc_iter *iter; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 14518c2ecf20Sopenharmony_ci return -EPERM; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci iter = msc_iter_install(msc); 14548c2ecf20Sopenharmony_ci if (IS_ERR(iter)) 14558c2ecf20Sopenharmony_ci return PTR_ERR(iter); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci file->private_data = iter; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci return nonseekable_open(inode, file); 14608c2ecf20Sopenharmony_ci} 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_cistatic int intel_th_msc_release(struct inode *inode, struct file *file) 14638c2ecf20Sopenharmony_ci{ 14648c2ecf20Sopenharmony_ci struct msc_iter *iter = file->private_data; 14658c2ecf20Sopenharmony_ci struct msc *msc = iter->msc; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci msc_iter_remove(iter, msc); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci return 0; 14708c2ecf20Sopenharmony_ci} 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_cistatic ssize_t 14738c2ecf20Sopenharmony_cimsc_single_to_user(struct msc *msc, char __user *buf, loff_t off, size_t len) 14748c2ecf20Sopenharmony_ci{ 14758c2ecf20Sopenharmony_ci unsigned long size = msc->nr_pages << PAGE_SHIFT, rem = len; 14768c2ecf20Sopenharmony_ci unsigned long start = off, tocopy = 0; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci if (msc->single_wrap) { 14798c2ecf20Sopenharmony_ci start += msc->single_sz; 14808c2ecf20Sopenharmony_ci if (start < size) { 14818c2ecf20Sopenharmony_ci tocopy = min(rem, size - start); 14828c2ecf20Sopenharmony_ci if (copy_to_user(buf, msc->base + start, tocopy)) 14838c2ecf20Sopenharmony_ci return -EFAULT; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci buf += tocopy; 14868c2ecf20Sopenharmony_ci rem -= tocopy; 14878c2ecf20Sopenharmony_ci start += tocopy; 14888c2ecf20Sopenharmony_ci } 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci start &= size - 1; 14918c2ecf20Sopenharmony_ci if (rem) { 14928c2ecf20Sopenharmony_ci tocopy = min(rem, msc->single_sz - start); 14938c2ecf20Sopenharmony_ci if (copy_to_user(buf, msc->base + start, tocopy)) 14948c2ecf20Sopenharmony_ci return -EFAULT; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci rem -= tocopy; 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci return len - rem; 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci if (copy_to_user(buf, msc->base + start, rem)) 15038c2ecf20Sopenharmony_ci return -EFAULT; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci return len; 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cistatic ssize_t intel_th_msc_read(struct file *file, char __user *buf, 15098c2ecf20Sopenharmony_ci size_t len, loff_t *ppos) 15108c2ecf20Sopenharmony_ci{ 15118c2ecf20Sopenharmony_ci struct msc_iter *iter = file->private_data; 15128c2ecf20Sopenharmony_ci struct msc *msc = iter->msc; 15138c2ecf20Sopenharmony_ci size_t size; 15148c2ecf20Sopenharmony_ci loff_t off = *ppos; 15158c2ecf20Sopenharmony_ci ssize_t ret = 0; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci if (!atomic_inc_unless_negative(&msc->user_count)) 15188c2ecf20Sopenharmony_ci return 0; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci if (msc->mode == MSC_MODE_SINGLE && !msc->single_wrap) 15218c2ecf20Sopenharmony_ci size = msc->single_sz; 15228c2ecf20Sopenharmony_ci else 15238c2ecf20Sopenharmony_ci size = msc->nr_pages << PAGE_SHIFT; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci if (!size) 15268c2ecf20Sopenharmony_ci goto put_count; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci if (off >= size) 15298c2ecf20Sopenharmony_ci goto put_count; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci if (off + len >= size) 15328c2ecf20Sopenharmony_ci len = size - off; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci if (msc->mode == MSC_MODE_SINGLE) { 15358c2ecf20Sopenharmony_ci ret = msc_single_to_user(msc, buf, off, len); 15368c2ecf20Sopenharmony_ci if (ret >= 0) 15378c2ecf20Sopenharmony_ci *ppos += ret; 15388c2ecf20Sopenharmony_ci } else if (msc->mode == MSC_MODE_MULTI) { 15398c2ecf20Sopenharmony_ci struct msc_win_to_user_struct u = { 15408c2ecf20Sopenharmony_ci .buf = buf, 15418c2ecf20Sopenharmony_ci .offset = 0, 15428c2ecf20Sopenharmony_ci }; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci ret = msc_buffer_iterate(iter, len, &u, msc_win_to_user); 15458c2ecf20Sopenharmony_ci if (ret >= 0) 15468c2ecf20Sopenharmony_ci *ppos = iter->offset; 15478c2ecf20Sopenharmony_ci } else { 15488c2ecf20Sopenharmony_ci ret = -EINVAL; 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ciput_count: 15528c2ecf20Sopenharmony_ci atomic_dec(&msc->user_count); 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci return ret; 15558c2ecf20Sopenharmony_ci} 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci/* 15588c2ecf20Sopenharmony_ci * vm operations callbacks (vm_ops) 15598c2ecf20Sopenharmony_ci */ 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_cistatic void msc_mmap_open(struct vm_area_struct *vma) 15628c2ecf20Sopenharmony_ci{ 15638c2ecf20Sopenharmony_ci struct msc_iter *iter = vma->vm_file->private_data; 15648c2ecf20Sopenharmony_ci struct msc *msc = iter->msc; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci atomic_inc(&msc->mmap_count); 15678c2ecf20Sopenharmony_ci} 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_cistatic void msc_mmap_close(struct vm_area_struct *vma) 15708c2ecf20Sopenharmony_ci{ 15718c2ecf20Sopenharmony_ci struct msc_iter *iter = vma->vm_file->private_data; 15728c2ecf20Sopenharmony_ci struct msc *msc = iter->msc; 15738c2ecf20Sopenharmony_ci unsigned long pg; 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci if (!atomic_dec_and_mutex_lock(&msc->mmap_count, &msc->buf_mutex)) 15768c2ecf20Sopenharmony_ci return; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci /* drop page _refcounts */ 15798c2ecf20Sopenharmony_ci for (pg = 0; pg < msc->nr_pages; pg++) { 15808c2ecf20Sopenharmony_ci struct page *page = msc_buffer_get_page(msc, pg); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!page)) 15838c2ecf20Sopenharmony_ci continue; 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci if (page->mapping) 15868c2ecf20Sopenharmony_ci page->mapping = NULL; 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci /* last mapping -- drop user_count */ 15908c2ecf20Sopenharmony_ci atomic_dec(&msc->user_count); 15918c2ecf20Sopenharmony_ci mutex_unlock(&msc->buf_mutex); 15928c2ecf20Sopenharmony_ci} 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_cistatic vm_fault_t msc_mmap_fault(struct vm_fault *vmf) 15958c2ecf20Sopenharmony_ci{ 15968c2ecf20Sopenharmony_ci struct msc_iter *iter = vmf->vma->vm_file->private_data; 15978c2ecf20Sopenharmony_ci struct msc *msc = iter->msc; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci vmf->page = msc_buffer_get_page(msc, vmf->pgoff); 16008c2ecf20Sopenharmony_ci if (!vmf->page) 16018c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci get_page(vmf->page); 16048c2ecf20Sopenharmony_ci vmf->page->mapping = vmf->vma->vm_file->f_mapping; 16058c2ecf20Sopenharmony_ci vmf->page->index = vmf->pgoff; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci return 0; 16088c2ecf20Sopenharmony_ci} 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_cistatic const struct vm_operations_struct msc_mmap_ops = { 16118c2ecf20Sopenharmony_ci .open = msc_mmap_open, 16128c2ecf20Sopenharmony_ci .close = msc_mmap_close, 16138c2ecf20Sopenharmony_ci .fault = msc_mmap_fault, 16148c2ecf20Sopenharmony_ci}; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_cistatic int intel_th_msc_mmap(struct file *file, struct vm_area_struct *vma) 16178c2ecf20Sopenharmony_ci{ 16188c2ecf20Sopenharmony_ci unsigned long size = vma->vm_end - vma->vm_start; 16198c2ecf20Sopenharmony_ci struct msc_iter *iter = vma->vm_file->private_data; 16208c2ecf20Sopenharmony_ci struct msc *msc = iter->msc; 16218c2ecf20Sopenharmony_ci int ret = -EINVAL; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci if (!size || offset_in_page(size)) 16248c2ecf20Sopenharmony_ci return -EINVAL; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci if (vma->vm_pgoff) 16278c2ecf20Sopenharmony_ci return -EINVAL; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* grab user_count once per mmap; drop in msc_mmap_close() */ 16308c2ecf20Sopenharmony_ci if (!atomic_inc_unless_negative(&msc->user_count)) 16318c2ecf20Sopenharmony_ci return -EINVAL; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci if (msc->mode != MSC_MODE_SINGLE && 16348c2ecf20Sopenharmony_ci msc->mode != MSC_MODE_MULTI) 16358c2ecf20Sopenharmony_ci goto out; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci if (size >> PAGE_SHIFT != msc->nr_pages) 16388c2ecf20Sopenharmony_ci goto out; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci atomic_set(&msc->mmap_count, 1); 16418c2ecf20Sopenharmony_ci ret = 0; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ciout: 16448c2ecf20Sopenharmony_ci if (ret) 16458c2ecf20Sopenharmony_ci atomic_dec(&msc->user_count); 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 16488c2ecf20Sopenharmony_ci vma->vm_flags |= VM_DONTEXPAND | VM_DONTCOPY; 16498c2ecf20Sopenharmony_ci vma->vm_ops = &msc_mmap_ops; 16508c2ecf20Sopenharmony_ci return ret; 16518c2ecf20Sopenharmony_ci} 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_cistatic const struct file_operations intel_th_msc_fops = { 16548c2ecf20Sopenharmony_ci .open = intel_th_msc_open, 16558c2ecf20Sopenharmony_ci .release = intel_th_msc_release, 16568c2ecf20Sopenharmony_ci .read = intel_th_msc_read, 16578c2ecf20Sopenharmony_ci .mmap = intel_th_msc_mmap, 16588c2ecf20Sopenharmony_ci .llseek = no_llseek, 16598c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 16608c2ecf20Sopenharmony_ci}; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_cistatic void intel_th_msc_wait_empty(struct intel_th_device *thdev) 16638c2ecf20Sopenharmony_ci{ 16648c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(&thdev->dev); 16658c2ecf20Sopenharmony_ci unsigned long count; 16668c2ecf20Sopenharmony_ci u32 reg; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci for (reg = 0, count = MSC_PLE_WAITLOOP_DEPTH; 16698c2ecf20Sopenharmony_ci count && !(reg & MSCSTS_PLE); count--) { 16708c2ecf20Sopenharmony_ci reg = __raw_readl(msc->reg_base + REG_MSU_MSC0STS); 16718c2ecf20Sopenharmony_ci cpu_relax(); 16728c2ecf20Sopenharmony_ci } 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci if (!count) 16758c2ecf20Sopenharmony_ci dev_dbg(msc_dev(msc), "timeout waiting for MSC0 PLE\n"); 16768c2ecf20Sopenharmony_ci} 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_cistatic int intel_th_msc_init(struct msc *msc) 16798c2ecf20Sopenharmony_ci{ 16808c2ecf20Sopenharmony_ci atomic_set(&msc->user_count, -1); 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci msc->mode = msc->multi_is_broken ? MSC_MODE_SINGLE : MSC_MODE_MULTI; 16838c2ecf20Sopenharmony_ci mutex_init(&msc->buf_mutex); 16848c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&msc->win_list); 16858c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&msc->iter_list); 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci msc->burst_len = 16888c2ecf20Sopenharmony_ci (ioread32(msc->reg_base + REG_MSU_MSC0CTL) & MSC_LEN) >> 16898c2ecf20Sopenharmony_ci __ffs(MSC_LEN); 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci return 0; 16928c2ecf20Sopenharmony_ci} 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic int msc_win_switch(struct msc *msc) 16958c2ecf20Sopenharmony_ci{ 16968c2ecf20Sopenharmony_ci struct msc_window *first; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci if (list_empty(&msc->win_list)) 16998c2ecf20Sopenharmony_ci return -EINVAL; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci first = list_first_entry(&msc->win_list, struct msc_window, entry); 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci if (msc_is_last_win(msc->cur_win)) 17048c2ecf20Sopenharmony_ci msc->cur_win = first; 17058c2ecf20Sopenharmony_ci else 17068c2ecf20Sopenharmony_ci msc->cur_win = list_next_entry(msc->cur_win, entry); 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci msc->base = msc_win_base(msc->cur_win); 17098c2ecf20Sopenharmony_ci msc->base_addr = msc_win_base_dma(msc->cur_win); 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci intel_th_trace_switch(msc->thdev); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci return 0; 17148c2ecf20Sopenharmony_ci} 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci/** 17178c2ecf20Sopenharmony_ci * intel_th_msc_window_unlock - put the window back in rotation 17188c2ecf20Sopenharmony_ci * @dev: MSC device to which this relates 17198c2ecf20Sopenharmony_ci * @sgt: buffer's sg_table for the window, does nothing if NULL 17208c2ecf20Sopenharmony_ci */ 17218c2ecf20Sopenharmony_civoid intel_th_msc_window_unlock(struct device *dev, struct sg_table *sgt) 17228c2ecf20Sopenharmony_ci{ 17238c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(dev); 17248c2ecf20Sopenharmony_ci struct msc_window *win; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci if (!sgt) 17278c2ecf20Sopenharmony_ci return; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci win = msc_find_window(msc, sgt, false); 17308c2ecf20Sopenharmony_ci if (!win) 17318c2ecf20Sopenharmony_ci return; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci msc_win_set_lockout(win, WIN_LOCKED, WIN_READY); 17348c2ecf20Sopenharmony_ci if (msc->switch_on_unlock == win) { 17358c2ecf20Sopenharmony_ci msc->switch_on_unlock = NULL; 17368c2ecf20Sopenharmony_ci msc_win_switch(msc); 17378c2ecf20Sopenharmony_ci } 17388c2ecf20Sopenharmony_ci} 17398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(intel_th_msc_window_unlock); 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_cistatic void msc_work(struct work_struct *work) 17428c2ecf20Sopenharmony_ci{ 17438c2ecf20Sopenharmony_ci struct msc *msc = container_of(work, struct msc, work); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci intel_th_msc_deactivate(msc->thdev); 17468c2ecf20Sopenharmony_ci} 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_cistatic irqreturn_t intel_th_msc_interrupt(struct intel_th_device *thdev) 17498c2ecf20Sopenharmony_ci{ 17508c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(&thdev->dev); 17518c2ecf20Sopenharmony_ci u32 msusts = ioread32(msc->msu_base + REG_MSU_MSUSTS); 17528c2ecf20Sopenharmony_ci u32 mask = msc->index ? MSUSTS_MSC1BLAST : MSUSTS_MSC0BLAST; 17538c2ecf20Sopenharmony_ci struct msc_window *win, *next_win; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci if (!msc->do_irq || !msc->mbuf) 17568c2ecf20Sopenharmony_ci return IRQ_NONE; 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci msusts &= mask; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci if (!msusts) 17618c2ecf20Sopenharmony_ci return msc->enabled ? IRQ_HANDLED : IRQ_NONE; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci iowrite32(msusts, msc->msu_base + REG_MSU_MSUSTS); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci if (!msc->enabled) 17668c2ecf20Sopenharmony_ci return IRQ_NONE; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci /* grab the window before we do the switch */ 17698c2ecf20Sopenharmony_ci win = msc->cur_win; 17708c2ecf20Sopenharmony_ci if (!win) 17718c2ecf20Sopenharmony_ci return IRQ_HANDLED; 17728c2ecf20Sopenharmony_ci next_win = msc_next_window(win); 17738c2ecf20Sopenharmony_ci if (!next_win) 17748c2ecf20Sopenharmony_ci return IRQ_HANDLED; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci /* next window: if READY, proceed, if LOCKED, stop the trace */ 17778c2ecf20Sopenharmony_ci if (msc_win_set_lockout(next_win, WIN_READY, WIN_INUSE)) { 17788c2ecf20Sopenharmony_ci if (msc->stop_on_full) 17798c2ecf20Sopenharmony_ci schedule_work(&msc->work); 17808c2ecf20Sopenharmony_ci else 17818c2ecf20Sopenharmony_ci msc->switch_on_unlock = next_win; 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci return IRQ_HANDLED; 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci /* current window: INUSE -> LOCKED */ 17878c2ecf20Sopenharmony_ci msc_win_set_lockout(win, WIN_INUSE, WIN_LOCKED); 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci msc_win_switch(msc); 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci if (msc->mbuf && msc->mbuf->ready) 17928c2ecf20Sopenharmony_ci msc->mbuf->ready(msc->mbuf_priv, win->sgt, 17938c2ecf20Sopenharmony_ci msc_win_total_sz(win)); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci return IRQ_HANDLED; 17968c2ecf20Sopenharmony_ci} 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_cistatic const char * const msc_mode[] = { 17998c2ecf20Sopenharmony_ci [MSC_MODE_SINGLE] = "single", 18008c2ecf20Sopenharmony_ci [MSC_MODE_MULTI] = "multi", 18018c2ecf20Sopenharmony_ci [MSC_MODE_EXI] = "ExI", 18028c2ecf20Sopenharmony_ci [MSC_MODE_DEBUG] = "debug", 18038c2ecf20Sopenharmony_ci}; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_cistatic ssize_t 18068c2ecf20Sopenharmony_ciwrap_show(struct device *dev, struct device_attribute *attr, char *buf) 18078c2ecf20Sopenharmony_ci{ 18088c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(dev); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", msc->wrap); 18118c2ecf20Sopenharmony_ci} 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_cistatic ssize_t 18148c2ecf20Sopenharmony_ciwrap_store(struct device *dev, struct device_attribute *attr, const char *buf, 18158c2ecf20Sopenharmony_ci size_t size) 18168c2ecf20Sopenharmony_ci{ 18178c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(dev); 18188c2ecf20Sopenharmony_ci unsigned long val; 18198c2ecf20Sopenharmony_ci int ret; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 10, &val); 18228c2ecf20Sopenharmony_ci if (ret) 18238c2ecf20Sopenharmony_ci return ret; 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci msc->wrap = !!val; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci return size; 18288c2ecf20Sopenharmony_ci} 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(wrap); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_cistatic void msc_buffer_unassign(struct msc *msc) 18338c2ecf20Sopenharmony_ci{ 18348c2ecf20Sopenharmony_ci lockdep_assert_held(&msc->buf_mutex); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci if (!msc->mbuf) 18378c2ecf20Sopenharmony_ci return; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci msc->mbuf->unassign(msc->mbuf_priv); 18408c2ecf20Sopenharmony_ci msu_buffer_put(msc->mbuf); 18418c2ecf20Sopenharmony_ci msc->mbuf_priv = NULL; 18428c2ecf20Sopenharmony_ci msc->mbuf = NULL; 18438c2ecf20Sopenharmony_ci} 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_cistatic ssize_t 18468c2ecf20Sopenharmony_cimode_show(struct device *dev, struct device_attribute *attr, char *buf) 18478c2ecf20Sopenharmony_ci{ 18488c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(dev); 18498c2ecf20Sopenharmony_ci const char *mode = msc_mode[msc->mode]; 18508c2ecf20Sopenharmony_ci ssize_t ret; 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci mutex_lock(&msc->buf_mutex); 18538c2ecf20Sopenharmony_ci if (msc->mbuf) 18548c2ecf20Sopenharmony_ci mode = msc->mbuf->name; 18558c2ecf20Sopenharmony_ci ret = scnprintf(buf, PAGE_SIZE, "%s\n", mode); 18568c2ecf20Sopenharmony_ci mutex_unlock(&msc->buf_mutex); 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci return ret; 18598c2ecf20Sopenharmony_ci} 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_cistatic ssize_t 18628c2ecf20Sopenharmony_cimode_store(struct device *dev, struct device_attribute *attr, const char *buf, 18638c2ecf20Sopenharmony_ci size_t size) 18648c2ecf20Sopenharmony_ci{ 18658c2ecf20Sopenharmony_ci const struct msu_buffer *mbuf = NULL; 18668c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(dev); 18678c2ecf20Sopenharmony_ci size_t len = size; 18688c2ecf20Sopenharmony_ci char *cp, *mode; 18698c2ecf20Sopenharmony_ci int i, ret; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 18728c2ecf20Sopenharmony_ci return -EPERM; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci cp = memchr(buf, '\n', len); 18758c2ecf20Sopenharmony_ci if (cp) 18768c2ecf20Sopenharmony_ci len = cp - buf; 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci mode = kstrndup(buf, len, GFP_KERNEL); 18798c2ecf20Sopenharmony_ci if (!mode) 18808c2ecf20Sopenharmony_ci return -ENOMEM; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci i = match_string(msc_mode, ARRAY_SIZE(msc_mode), mode); 18838c2ecf20Sopenharmony_ci if (i >= 0) { 18848c2ecf20Sopenharmony_ci kfree(mode); 18858c2ecf20Sopenharmony_ci goto found; 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci /* Buffer sinks only work with a usable IRQ */ 18898c2ecf20Sopenharmony_ci if (!msc->do_irq) { 18908c2ecf20Sopenharmony_ci kfree(mode); 18918c2ecf20Sopenharmony_ci return -EINVAL; 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci mbuf = msu_buffer_get(mode); 18958c2ecf20Sopenharmony_ci kfree(mode); 18968c2ecf20Sopenharmony_ci if (mbuf) 18978c2ecf20Sopenharmony_ci goto found; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci return -EINVAL; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_cifound: 19028c2ecf20Sopenharmony_ci if (i == MSC_MODE_MULTI && msc->multi_is_broken) 19038c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci mutex_lock(&msc->buf_mutex); 19068c2ecf20Sopenharmony_ci ret = 0; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci /* Same buffer: do nothing */ 19098c2ecf20Sopenharmony_ci if (mbuf && mbuf == msc->mbuf) { 19108c2ecf20Sopenharmony_ci /* put the extra reference we just got */ 19118c2ecf20Sopenharmony_ci msu_buffer_put(mbuf); 19128c2ecf20Sopenharmony_ci goto unlock; 19138c2ecf20Sopenharmony_ci } 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci ret = msc_buffer_unlocked_free_unless_used(msc); 19168c2ecf20Sopenharmony_ci if (ret) 19178c2ecf20Sopenharmony_ci goto unlock; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci if (mbuf) { 19208c2ecf20Sopenharmony_ci void *mbuf_priv = mbuf->assign(dev, &i); 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci if (!mbuf_priv) { 19238c2ecf20Sopenharmony_ci ret = -ENOMEM; 19248c2ecf20Sopenharmony_ci goto unlock; 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci msc_buffer_unassign(msc); 19288c2ecf20Sopenharmony_ci msc->mbuf_priv = mbuf_priv; 19298c2ecf20Sopenharmony_ci msc->mbuf = mbuf; 19308c2ecf20Sopenharmony_ci } else { 19318c2ecf20Sopenharmony_ci msc_buffer_unassign(msc); 19328c2ecf20Sopenharmony_ci } 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci msc->mode = i; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ciunlock: 19378c2ecf20Sopenharmony_ci if (ret && mbuf) 19388c2ecf20Sopenharmony_ci msu_buffer_put(mbuf); 19398c2ecf20Sopenharmony_ci mutex_unlock(&msc->buf_mutex); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci return ret ? ret : size; 19428c2ecf20Sopenharmony_ci} 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(mode); 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_cistatic ssize_t 19478c2ecf20Sopenharmony_cinr_pages_show(struct device *dev, struct device_attribute *attr, char *buf) 19488c2ecf20Sopenharmony_ci{ 19498c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(dev); 19508c2ecf20Sopenharmony_ci struct msc_window *win; 19518c2ecf20Sopenharmony_ci size_t count = 0; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci mutex_lock(&msc->buf_mutex); 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci if (msc->mode == MSC_MODE_SINGLE) 19568c2ecf20Sopenharmony_ci count = scnprintf(buf, PAGE_SIZE, "%ld\n", msc->nr_pages); 19578c2ecf20Sopenharmony_ci else if (msc->mode == MSC_MODE_MULTI) { 19588c2ecf20Sopenharmony_ci list_for_each_entry(win, &msc->win_list, entry) { 19598c2ecf20Sopenharmony_ci count += scnprintf(buf + count, PAGE_SIZE - count, 19608c2ecf20Sopenharmony_ci "%d%c", win->nr_blocks, 19618c2ecf20Sopenharmony_ci msc_is_last_win(win) ? '\n' : ','); 19628c2ecf20Sopenharmony_ci } 19638c2ecf20Sopenharmony_ci } else { 19648c2ecf20Sopenharmony_ci count = scnprintf(buf, PAGE_SIZE, "unsupported\n"); 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci mutex_unlock(&msc->buf_mutex); 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci return count; 19708c2ecf20Sopenharmony_ci} 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_cistatic ssize_t 19738c2ecf20Sopenharmony_cinr_pages_store(struct device *dev, struct device_attribute *attr, 19748c2ecf20Sopenharmony_ci const char *buf, size_t size) 19758c2ecf20Sopenharmony_ci{ 19768c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(dev); 19778c2ecf20Sopenharmony_ci unsigned long val, *win = NULL, *rewin; 19788c2ecf20Sopenharmony_ci size_t len = size; 19798c2ecf20Sopenharmony_ci const char *p = buf; 19808c2ecf20Sopenharmony_ci char *end, *s; 19818c2ecf20Sopenharmony_ci int ret, nr_wins = 0; 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 19848c2ecf20Sopenharmony_ci return -EPERM; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci ret = msc_buffer_free_unless_used(msc); 19878c2ecf20Sopenharmony_ci if (ret) 19888c2ecf20Sopenharmony_ci return ret; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci /* scan the comma-separated list of allocation sizes */ 19918c2ecf20Sopenharmony_ci end = memchr(buf, '\n', len); 19928c2ecf20Sopenharmony_ci if (end) 19938c2ecf20Sopenharmony_ci len = end - buf; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci do { 19968c2ecf20Sopenharmony_ci end = memchr(p, ',', len); 19978c2ecf20Sopenharmony_ci s = kstrndup(p, end ? end - p : len, GFP_KERNEL); 19988c2ecf20Sopenharmony_ci if (!s) { 19998c2ecf20Sopenharmony_ci ret = -ENOMEM; 20008c2ecf20Sopenharmony_ci goto free_win; 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci ret = kstrtoul(s, 10, &val); 20048c2ecf20Sopenharmony_ci kfree(s); 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci if (ret || !val) 20078c2ecf20Sopenharmony_ci goto free_win; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci if (nr_wins && msc->mode == MSC_MODE_SINGLE) { 20108c2ecf20Sopenharmony_ci ret = -EINVAL; 20118c2ecf20Sopenharmony_ci goto free_win; 20128c2ecf20Sopenharmony_ci } 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci nr_wins++; 20158c2ecf20Sopenharmony_ci rewin = krealloc(win, sizeof(*win) * nr_wins, GFP_KERNEL); 20168c2ecf20Sopenharmony_ci if (!rewin) { 20178c2ecf20Sopenharmony_ci kfree(win); 20188c2ecf20Sopenharmony_ci return -ENOMEM; 20198c2ecf20Sopenharmony_ci } 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci win = rewin; 20228c2ecf20Sopenharmony_ci win[nr_wins - 1] = val; 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci if (!end) 20258c2ecf20Sopenharmony_ci break; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci /* consume the number and the following comma, hence +1 */ 20288c2ecf20Sopenharmony_ci len -= end - p + 1; 20298c2ecf20Sopenharmony_ci p = end + 1; 20308c2ecf20Sopenharmony_ci } while (len); 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci mutex_lock(&msc->buf_mutex); 20338c2ecf20Sopenharmony_ci ret = msc_buffer_alloc(msc, win, nr_wins); 20348c2ecf20Sopenharmony_ci mutex_unlock(&msc->buf_mutex); 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_cifree_win: 20378c2ecf20Sopenharmony_ci kfree(win); 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci return ret ? ret : size; 20408c2ecf20Sopenharmony_ci} 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(nr_pages); 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_cistatic ssize_t 20458c2ecf20Sopenharmony_ciwin_switch_store(struct device *dev, struct device_attribute *attr, 20468c2ecf20Sopenharmony_ci const char *buf, size_t size) 20478c2ecf20Sopenharmony_ci{ 20488c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(dev); 20498c2ecf20Sopenharmony_ci unsigned long val; 20508c2ecf20Sopenharmony_ci int ret; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 10, &val); 20538c2ecf20Sopenharmony_ci if (ret) 20548c2ecf20Sopenharmony_ci return ret; 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci if (val != 1) 20578c2ecf20Sopenharmony_ci return -EINVAL; 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci ret = -EINVAL; 20608c2ecf20Sopenharmony_ci mutex_lock(&msc->buf_mutex); 20618c2ecf20Sopenharmony_ci /* 20628c2ecf20Sopenharmony_ci * Window switch can only happen in the "multi" mode. 20638c2ecf20Sopenharmony_ci * If a external buffer is engaged, they have the full 20648c2ecf20Sopenharmony_ci * control over window switching. 20658c2ecf20Sopenharmony_ci */ 20668c2ecf20Sopenharmony_ci if (msc->mode == MSC_MODE_MULTI && !msc->mbuf) 20678c2ecf20Sopenharmony_ci ret = msc_win_switch(msc); 20688c2ecf20Sopenharmony_ci mutex_unlock(&msc->buf_mutex); 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci return ret ? ret : size; 20718c2ecf20Sopenharmony_ci} 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(win_switch); 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_cistatic ssize_t stop_on_full_show(struct device *dev, 20768c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 20778c2ecf20Sopenharmony_ci{ 20788c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(dev); 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", msc->stop_on_full); 20818c2ecf20Sopenharmony_ci} 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_cistatic ssize_t stop_on_full_store(struct device *dev, 20848c2ecf20Sopenharmony_ci struct device_attribute *attr, 20858c2ecf20Sopenharmony_ci const char *buf, size_t size) 20868c2ecf20Sopenharmony_ci{ 20878c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(dev); 20888c2ecf20Sopenharmony_ci int ret; 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci ret = kstrtobool(buf, &msc->stop_on_full); 20918c2ecf20Sopenharmony_ci if (ret) 20928c2ecf20Sopenharmony_ci return ret; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci return size; 20958c2ecf20Sopenharmony_ci} 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(stop_on_full); 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_cistatic struct attribute *msc_output_attrs[] = { 21008c2ecf20Sopenharmony_ci &dev_attr_wrap.attr, 21018c2ecf20Sopenharmony_ci &dev_attr_mode.attr, 21028c2ecf20Sopenharmony_ci &dev_attr_nr_pages.attr, 21038c2ecf20Sopenharmony_ci &dev_attr_win_switch.attr, 21048c2ecf20Sopenharmony_ci &dev_attr_stop_on_full.attr, 21058c2ecf20Sopenharmony_ci NULL, 21068c2ecf20Sopenharmony_ci}; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_cistatic struct attribute_group msc_output_group = { 21098c2ecf20Sopenharmony_ci .attrs = msc_output_attrs, 21108c2ecf20Sopenharmony_ci}; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_cistatic int intel_th_msc_probe(struct intel_th_device *thdev) 21138c2ecf20Sopenharmony_ci{ 21148c2ecf20Sopenharmony_ci struct device *dev = &thdev->dev; 21158c2ecf20Sopenharmony_ci struct resource *res; 21168c2ecf20Sopenharmony_ci struct msc *msc; 21178c2ecf20Sopenharmony_ci void __iomem *base; 21188c2ecf20Sopenharmony_ci int err; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); 21218c2ecf20Sopenharmony_ci if (!res) 21228c2ecf20Sopenharmony_ci return -ENODEV; 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci base = devm_ioremap(dev, res->start, resource_size(res)); 21258c2ecf20Sopenharmony_ci if (!base) 21268c2ecf20Sopenharmony_ci return -ENOMEM; 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci msc = devm_kzalloc(dev, sizeof(*msc), GFP_KERNEL); 21298c2ecf20Sopenharmony_ci if (!msc) 21308c2ecf20Sopenharmony_ci return -ENOMEM; 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci res = intel_th_device_get_resource(thdev, IORESOURCE_IRQ, 1); 21338c2ecf20Sopenharmony_ci if (!res) 21348c2ecf20Sopenharmony_ci msc->do_irq = 1; 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci if (INTEL_TH_CAP(to_intel_th(thdev), multi_is_broken)) 21378c2ecf20Sopenharmony_ci msc->multi_is_broken = 1; 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci msc->index = thdev->id; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci msc->thdev = thdev; 21428c2ecf20Sopenharmony_ci msc->reg_base = base + msc->index * 0x100; 21438c2ecf20Sopenharmony_ci msc->msu_base = base; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci INIT_WORK(&msc->work, msc_work); 21468c2ecf20Sopenharmony_ci err = intel_th_msc_init(msc); 21478c2ecf20Sopenharmony_ci if (err) 21488c2ecf20Sopenharmony_ci return err; 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci dev_set_drvdata(dev, msc); 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci return 0; 21538c2ecf20Sopenharmony_ci} 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_cistatic void intel_th_msc_remove(struct intel_th_device *thdev) 21568c2ecf20Sopenharmony_ci{ 21578c2ecf20Sopenharmony_ci struct msc *msc = dev_get_drvdata(&thdev->dev); 21588c2ecf20Sopenharmony_ci int ret; 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci intel_th_msc_deactivate(thdev); 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci /* 21638c2ecf20Sopenharmony_ci * Buffers should not be used at this point except if the 21648c2ecf20Sopenharmony_ci * output character device is still open and the parent 21658c2ecf20Sopenharmony_ci * device gets detached from its bus, which is a FIXME. 21668c2ecf20Sopenharmony_ci */ 21678c2ecf20Sopenharmony_ci ret = msc_buffer_free_unless_used(msc); 21688c2ecf20Sopenharmony_ci WARN_ON_ONCE(ret); 21698c2ecf20Sopenharmony_ci} 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_cistatic struct intel_th_driver intel_th_msc_driver = { 21728c2ecf20Sopenharmony_ci .probe = intel_th_msc_probe, 21738c2ecf20Sopenharmony_ci .remove = intel_th_msc_remove, 21748c2ecf20Sopenharmony_ci .irq = intel_th_msc_interrupt, 21758c2ecf20Sopenharmony_ci .wait_empty = intel_th_msc_wait_empty, 21768c2ecf20Sopenharmony_ci .activate = intel_th_msc_activate, 21778c2ecf20Sopenharmony_ci .deactivate = intel_th_msc_deactivate, 21788c2ecf20Sopenharmony_ci .fops = &intel_th_msc_fops, 21798c2ecf20Sopenharmony_ci .attr_group = &msc_output_group, 21808c2ecf20Sopenharmony_ci .driver = { 21818c2ecf20Sopenharmony_ci .name = "msc", 21828c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 21838c2ecf20Sopenharmony_ci }, 21848c2ecf20Sopenharmony_ci}; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_cimodule_driver(intel_th_msc_driver, 21878c2ecf20Sopenharmony_ci intel_th_driver_register, 21888c2ecf20Sopenharmony_ci intel_th_driver_unregister); 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 21918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel(R) Trace Hub Memory Storage Unit driver"); 21928c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); 2193