diff options
author | Scott Wood <scottwood@freescale.com> | 2015-02-13 22:12:06 (GMT) |
---|---|---|
committer | Scott Wood <scottwood@freescale.com> | 2015-02-13 22:19:22 (GMT) |
commit | 6faa2909871d8937cb2f79a10e1b21ffe193fac1 (patch) | |
tree | f558a94f1553814cc122ab8d9e04c0ebad5262a5 /drivers/misc | |
parent | fcb2fb84301c673ee15ca04e7a2fc965712d49a0 (diff) | |
download | linux-fsl-qoriq-6faa2909871d8937cb2f79a10e1b21ffe193fac1.tar.xz |
Reset to 3.12.37
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 42 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/hwlat_detector.c | 1240 | ||||
-rw-r--r-- | drivers/misc/mei/bus.c | 2 | ||||
-rw-r--r-- | drivers/misc/mei/client.c | 77 | ||||
-rw-r--r-- | drivers/misc/mei/client.h | 18 | ||||
-rw-r--r-- | drivers/misc/mei/hw-me-regs.h | 5 | ||||
-rw-r--r-- | drivers/misc/mei/hw-me.c | 32 | ||||
-rw-r--r-- | drivers/misc/mei/interrupt.c | 3 | ||||
-rw-r--r-- | drivers/misc/mei/main.c | 3 | ||||
-rw-r--r-- | drivers/misc/mei/nfc.c | 11 | ||||
-rw-r--r-- | drivers/misc/mei/pci-me.c | 30 |
12 files changed, 115 insertions, 1349 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e9b20e8..8dacd4c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -63,7 +63,6 @@ config ATMEL_PWM config ATMEL_TCLIB bool "Atmel AT32/AT91 Timer/Counter Library" depends on (AVR32 || ARCH_AT91) - default y if PREEMPT_RT_FULL help Select this if you want a library to allocate the Timer/Counter blocks found on many Atmel processors. This facilitates using @@ -79,7 +78,8 @@ config ATMEL_TCB_CLKSRC are combined to make a single 32-bit timer. When GENERIC_CLOCKEVENTS is defined, the third timer channel - may be used as a clock event device supporting oneshot mode. + may be used as a clock event device supporting oneshot mode + (delays of up to two seconds) based on the 32 KiHz clock. config ATMEL_TCB_CLKSRC_BLOCK int @@ -93,15 +93,6 @@ config ATMEL_TCB_CLKSRC_BLOCK TC can be used for other purposes, such as PWM generation and interval timing. -config ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK - bool "TC Block use 32 KiHz clock" - depends on ATMEL_TCB_CLKSRC - default y if !PREEMPT_RT_FULL - help - Select this to use 32 KiHz base clock rate as TC block clock - source for clock events. - - config DUMMY_IRQ tristate "Dummy IRQ handler" default n @@ -131,35 +122,6 @@ config IBM_ASM for information on the specific driver level and support statement for your IBM server. -config HWLAT_DETECTOR - tristate "Testing module to detect hardware-induced latencies" - depends on DEBUG_FS - depends on RING_BUFFER - default m - ---help--- - A simple hardware latency detector. Use this module to detect - large latencies introduced by the behavior of the underlying - system firmware external to Linux. We do this using periodic - use of stop_machine to grab all available CPUs and measure - for unexplainable gaps in the CPU timestamp counter(s). By - default, the module is not enabled until the "enable" file - within the "hwlat_detector" debugfs directory is toggled. - - This module is often used to detect SMI (System Management - Interrupts) on x86 systems, though is not x86 specific. To - this end, we default to using a sample window of 1 second, - during which we will sample for 0.5 seconds. If an SMI or - similar event occurs during that time, it is recorded - into an 8K samples global ring buffer until retreived. - - WARNING: This software should never be enabled (it can be built - but should not be turned on after it is loaded) in a production - environment where high latencies are a concern since the - sampling mechanism actually introduces latencies for - regular tasks while the CPU(s) are being held. - - If unsure, say N - config PHANTOM tristate "Sensable PHANToM (PCI)" depends on PCI diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 6bb08da40..c235d5b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -53,4 +53,3 @@ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o obj-$(CONFIG_SRAM) += sram.o -obj-$(CONFIG_HWLAT_DETECTOR) += hwlat_detector.o diff --git a/drivers/misc/hwlat_detector.c b/drivers/misc/hwlat_detector.c deleted file mode 100644 index 2429c43..0000000 --- a/drivers/misc/hwlat_detector.c +++ /dev/null @@ -1,1240 +0,0 @@ -/* - * hwlat_detector.c - A simple Hardware Latency detector. - * - * Use this module to detect large system latencies induced by the behavior of - * certain underlying system hardware or firmware, independent of Linux itself. - * The code was developed originally to detect the presence of SMIs on Intel - * and AMD systems, although there is no dependency upon x86 herein. - * - * The classical example usage of this module is in detecting the presence of - * SMIs or System Management Interrupts on Intel and AMD systems. An SMI is a - * somewhat special form of hardware interrupt spawned from earlier CPU debug - * modes in which the (BIOS/EFI/etc.) firmware arranges for the South Bridge - * LPC (or other device) to generate a special interrupt under certain - * circumstances, for example, upon expiration of a special SMI timer device, - * due to certain external thermal readings, on certain I/O address accesses, - * and other situations. An SMI hits a special CPU pin, triggers a special - * SMI mode (complete with special memory map), and the OS is unaware. - * - * Although certain hardware-inducing latencies are necessary (for example, - * a modern system often requires an SMI handler for correct thermal control - * and remote management) they can wreak havoc upon any OS-level performance - * guarantees toward low-latency, especially when the OS is not even made - * aware of the presence of these interrupts. For this reason, we need a - * somewhat brute force mechanism to detect these interrupts. In this case, - * we do it by hogging all of the CPU(s) for configurable timer intervals, - * sampling the built-in CPU timer, looking for discontiguous readings. - * - * WARNING: This implementation necessarily introduces latencies. Therefore, - * you should NEVER use this module in a production environment - * requiring any kind of low-latency performance guarantee(s). - * - * Copyright (C) 2008-2009 Jon Masters, Red Hat, Inc. <jcm@redhat.com> - * - * Includes useful feedback from Clark Williams <clark@redhat.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/ring_buffer.h> -#include <linux/time.h> -#include <linux/hrtimer.h> -#include <linux/kthread.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/uaccess.h> -#include <linux/version.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/trace_clock.h> - -#define BUF_SIZE_DEFAULT 262144UL /* 8K*(sizeof(entry)) */ -#define BUF_FLAGS (RB_FL_OVERWRITE) /* no block on full */ -#define U64STR_SIZE 22 /* 20 digits max */ - -#define VERSION "1.0.0" -#define BANNER "hwlat_detector: " -#define DRVNAME "hwlat_detector" -#define DEFAULT_SAMPLE_WINDOW 1000000 /* 1s */ -#define DEFAULT_SAMPLE_WIDTH 500000 /* 0.5s */ -#define DEFAULT_LAT_THRESHOLD 10 /* 10us */ - -/* Module metadata */ - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jon Masters <jcm@redhat.com>"); -MODULE_DESCRIPTION("A simple hardware latency detector"); -MODULE_VERSION(VERSION); - -/* Module parameters */ - -static int debug; -static int enabled; -static int threshold; - -module_param(debug, int, 0); /* enable debug */ -module_param(enabled, int, 0); /* enable detector */ -module_param(threshold, int, 0); /* latency threshold */ - -/* Buffering and sampling */ - -static struct ring_buffer *ring_buffer; /* sample buffer */ -static DEFINE_MUTEX(ring_buffer_mutex); /* lock changes */ -static unsigned long buf_size = BUF_SIZE_DEFAULT; -static struct task_struct *kthread; /* sampling thread */ - -/* DebugFS filesystem entries */ - -static struct dentry *debug_dir; /* debugfs directory */ -static struct dentry *debug_max; /* maximum TSC delta */ -static struct dentry *debug_count; /* total detect count */ -static struct dentry *debug_sample_width; /* sample width us */ -static struct dentry *debug_sample_window; /* sample window us */ -static struct dentry *debug_sample; /* raw samples us */ -static struct dentry *debug_threshold; /* threshold us */ -static struct dentry *debug_enable; /* enable/disable */ - -/* Individual samples and global state */ - -struct sample; /* latency sample */ -struct data; /* Global state */ - -/* Sampling functions */ -static int __buffer_add_sample(struct sample *sample); -static struct sample *buffer_get_sample(struct sample *sample); - -/* Threading and state */ -static int kthread_fn(void *unused); -static int start_kthread(void); -static int stop_kthread(void); -static void __reset_stats(void); -static int init_stats(void); - -/* Debugfs interface */ -static ssize_t simple_data_read(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos, const u64 *entry); -static ssize_t simple_data_write(struct file *filp, const char __user *ubuf, - size_t cnt, loff_t *ppos, u64 *entry); -static int debug_sample_fopen(struct inode *inode, struct file *filp); -static ssize_t debug_sample_fread(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos); -static int debug_sample_release(struct inode *inode, struct file *filp); -static int debug_enable_fopen(struct inode *inode, struct file *filp); -static ssize_t debug_enable_fread(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos); -static ssize_t debug_enable_fwrite(struct file *file, - const char __user *user_buffer, - size_t user_size, loff_t *offset); - -/* Initialization functions */ -static int init_debugfs(void); -static void free_debugfs(void); -static int detector_init(void); -static void detector_exit(void); - -/* Individual latency samples are stored here when detected and packed into - * the ring_buffer circular buffer, where they are overwritten when - * more than buf_size/sizeof(sample) samples are received. */ -struct sample { - u64 seqnum; /* unique sequence */ - u64 duration; /* ktime delta */ - u64 outer_duration; /* ktime delta (outer loop) */ - struct timespec timestamp; /* wall time */ - unsigned long lost; -}; - -/* keep the global state somewhere. */ -static struct data { - - struct mutex lock; /* protect changes */ - - u64 count; /* total since reset */ - u64 max_sample; /* max hardware latency */ - u64 threshold; /* sample threshold level */ - - u64 sample_window; /* total sampling window (on+off) */ - u64 sample_width; /* active sampling portion of window */ - - atomic_t sample_open; /* whether the sample file is open */ - - wait_queue_head_t wq; /* waitqeue for new sample values */ - -} data; - -/** - * __buffer_add_sample - add a new latency sample recording to the ring buffer - * @sample: The new latency sample value - * - * This receives a new latency sample and records it in a global ring buffer. - * No additional locking is used in this case. - */ -static int __buffer_add_sample(struct sample *sample) -{ - return ring_buffer_write(ring_buffer, - sizeof(struct sample), sample); -} - -/** - * buffer_get_sample - remove a hardware latency sample from the ring buffer - * @sample: Pre-allocated storage for the sample - * - * This retrieves a hardware latency sample from the global circular buffer - */ -static struct sample *buffer_get_sample(struct sample *sample) -{ - struct ring_buffer_event *e = NULL; - struct sample *s = NULL; - unsigned int cpu = 0; - - if (!sample) - return NULL; - - mutex_lock(&ring_buffer_mutex); - for_each_online_cpu(cpu) { - e = ring_buffer_consume(ring_buffer, cpu, NULL, &sample->lost); - if (e) - break; - } - - if (e) { - s = ring_buffer_event_data(e); - memcpy(sample, s, sizeof(struct sample)); - } else - sample = NULL; - mutex_unlock(&ring_buffer_mutex); - - return sample; -} - -#ifndef CONFIG_TRACING -#define time_type ktime_t -#define time_get() ktime_get() -#define time_to_us(x) ktime_to_us(x) -#define time_sub(a, b) ktime_sub(a, b) -#define init_time(a, b) (a).tv64 = b -#define time_u64(a) ((a).tv64) -#else -#define time_type u64 -#define time_get() trace_clock_local() -#define time_to_us(x) div_u64(x, 1000) -#define time_sub(a, b) ((a) - (b)) -#define init_time(a, b) (a = b) -#define time_u64(a) a -#endif -/** - * get_sample - sample the CPU TSC and look for likely hardware latencies - * - * Used to repeatedly capture the CPU TSC (or similar), looking for potential - * hardware-induced latency. Called with interrupts disabled and with - * data.lock held. - */ -static int get_sample(void) -{ - time_type start, t1, t2, last_t2; - s64 diff, total = 0; - u64 sample = 0; - u64 outer_sample = 0; - int ret = -1; - - init_time(last_t2, 0); - start = time_get(); /* start timestamp */ - - do { - - t1 = time_get(); /* we'll look for a discontinuity */ - t2 = time_get(); - - if (time_u64(last_t2)) { - /* Check the delta from outer loop (t2 to next t1) */ - diff = time_to_us(time_sub(t1, last_t2)); - /* This shouldn't happen */ - if (diff < 0) { - pr_err(BANNER "time running backwards\n"); - goto out; - } - if (diff > outer_sample) - outer_sample = diff; - } - last_t2 = t2; - - total = time_to_us(time_sub(t2, start)); /* sample width */ - - /* This checks the inner loop (t1 to t2) */ - diff = time_to_us(time_sub(t2, t1)); /* current diff */ - - /* This shouldn't happen */ - if (diff < 0) { - pr_err(BANNER "time running backwards\n"); - goto out; - } - - if (diff > sample) - sample = diff; /* only want highest value */ - - } while (total <= data.sample_width); - - ret = 0; - - /* If we exceed the threshold value, we have found a hardware latency */ - if (sample > data.threshold || outer_sample > data.threshold) { - struct sample s; - - ret = 1; - - data.count++; - s.seqnum = data.count; - s.duration = sample; - s.outer_duration = outer_sample; - s.timestamp = CURRENT_TIME; - __buffer_add_sample(&s); - - /* Keep a running maximum ever recorded hardware latency */ - if (sample > data.max_sample) - data.max_sample = sample; - } - -out: - return ret; -} - -/* - * kthread_fn - The CPU time sampling/hardware latency detection kernel thread - * @unused: A required part of the kthread API. - * - * Used to periodically sample the CPU TSC via a call to get_sample. We - * disable interrupts, which does (intentionally) introduce latency since we - * need to ensure nothing else might be running (and thus pre-empting). - * Obviously this should never be used in production environments. - * - * Currently this runs on which ever CPU it was scheduled on, but most - * real-worald hardware latency situations occur across several CPUs, - * but we might later generalize this if we find there are any actualy - * systems with alternate SMI delivery or other hardware latencies. - */ -static int kthread_fn(void *unused) -{ - int ret; - u64 interval; - - while (!kthread_should_stop()) { - - mutex_lock(&data.lock); - - local_irq_disable(); - ret = get_sample(); - local_irq_enable(); - - if (ret > 0) - wake_up(&data.wq); /* wake up reader(s) */ - - interval = data.sample_window - data.sample_width; - do_div(interval, USEC_PER_MSEC); /* modifies interval value */ - - mutex_unlock(&data.lock); - - if (msleep_interruptible(interval)) - break; - } - - return 0; -} - -/** - * start_kthread - Kick off the hardware latency sampling/detector kthread - * - * This starts a kernel thread that will sit and sample the CPU timestamp - * counter (TSC or similar) and look for potential hardware latencies. - */ -static int start_kthread(void) -{ - kthread = kthread_run(kthread_fn, NULL, - DRVNAME); - if (IS_ERR(kthread)) { - pr_err(BANNER "could not start sampling thread\n"); - enabled = 0; - return -ENOMEM; - } - - return 0; -} - -/** - * stop_kthread - Inform the hardware latency samping/detector kthread to stop - * - * This kicks the running hardware latency sampling/detector kernel thread and - * tells it to stop sampling now. Use this on unload and at system shutdown. - */ -static int stop_kthread(void) -{ - int ret; - - ret = kthread_stop(kthread); - - return ret; -} - -/** - * __reset_stats - Reset statistics for the hardware latency detector - * - * We use data to store various statistics and global state. We call this - * function in order to reset those when "enable" is toggled on or off, and - * also at initialization. Should be called with data.lock held. - */ -static void __reset_stats(void) -{ - data.count = 0; - data.max_sample = 0; - ring_buffer_reset(ring_buffer); /* flush out old sample entries */ -} - -/** - * init_stats - Setup global state statistics for the hardware latency detector - * - * We use data to store various statistics and global state. We also use - * a global ring buffer (ring_buffer) to keep raw samples of detected hardware - * induced system latencies. This function initializes these structures and - * allocates the global ring buffer also. - */ -static int init_stats(void) -{ - int ret = -ENOMEM; - - mutex_init(&data.lock); - init_waitqueue_head(&data.wq); - atomic_set(&data.sample_open, 0); - - ring_buffer = ring_buffer_alloc(buf_size, BUF_FLAGS); - - if (WARN(!ring_buffer, KERN_ERR BANNER - "failed to allocate ring buffer!\n")) - goto out; - - __reset_stats(); - data.threshold = threshold ?: DEFAULT_LAT_THRESHOLD; /* threshold us */ - data.sample_window = DEFAULT_SAMPLE_WINDOW; /* window us */ - data.sample_width = DEFAULT_SAMPLE_WIDTH; /* width us */ - - ret = 0; - -out: - return ret; - -} - -/* - * simple_data_read - Wrapper read function for global state debugfs entries - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The userspace provided buffer to read value into - * @cnt: The maximum number of bytes to read - * @ppos: The current "file" position - * @entry: The entry to read from - * - * This function provides a generic read implementation for the global state - * "data" structure debugfs filesystem entries. It would be nice to use - * simple_attr_read directly, but we need to make sure that the data.lock - * is held during the actual read. - */ -static ssize_t simple_data_read(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos, const u64 *entry) -{ - char buf[U64STR_SIZE]; - u64 val = 0; - int len = 0; - - memset(buf, 0, sizeof(buf)); - - if (!entry) - return -EFAULT; - - mutex_lock(&data.lock); - val = *entry; - mutex_unlock(&data.lock); - - len = snprintf(buf, sizeof(buf), "%llu\n", (unsigned long long)val); - - return simple_read_from_buffer(ubuf, cnt, ppos, buf, len); - -} - -/* - * simple_data_write - Wrapper write function for global state debugfs entries - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The userspace provided buffer to write value from - * @cnt: The maximum number of bytes to write - * @ppos: The current "file" position - * @entry: The entry to write to - * - * This function provides a generic write implementation for the global state - * "data" structure debugfs filesystem entries. It would be nice to use - * simple_attr_write directly, but we need to make sure that the data.lock - * is held during the actual write. - */ -static ssize_t simple_data_write(struct file *filp, const char __user *ubuf, - size_t cnt, loff_t *ppos, u64 *entry) -{ - char buf[U64STR_SIZE]; - int csize = min(cnt, sizeof(buf)); - u64 val = 0; - int err = 0; - - memset(buf, '\0', sizeof(buf)); - if (copy_from_user(buf, ubuf, csize)) - return -EFAULT; - - buf[U64STR_SIZE-1] = '\0'; /* just in case */ - err = kstrtoull(buf, 10, &val); - if (err) - return -EINVAL; - - mutex_lock(&data.lock); - *entry = val; - mutex_unlock(&data.lock); - - return csize; -} - -/** - * debug_count_fopen - Open function for "count" debugfs entry - * @inode: The in-kernel inode representation of the debugfs "file" - * @filp: The active open file structure for the debugfs "file" - * - * This function provides an open implementation for the "count" debugfs - * interface to the hardware latency detector. - */ -static int debug_count_fopen(struct inode *inode, struct file *filp) -{ - return 0; -} - -/** - * debug_count_fread - Read function for "count" debugfs entry - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The userspace provided buffer to read value into - * @cnt: The maximum number of bytes to read - * @ppos: The current "file" position - * - * This function provides a read implementation for the "count" debugfs - * interface to the hardware latency detector. Can be used to read the - * number of latency readings exceeding the configured threshold since - * the detector was last reset (e.g. by writing a zero into "count"). - */ -static ssize_t debug_count_fread(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos) -{ - return simple_data_read(filp, ubuf, cnt, ppos, &data.count); -} - -/** - * debug_count_fwrite - Write function for "count" debugfs entry - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The user buffer that contains the value to write - * @cnt: The maximum number of bytes to write to "file" - * @ppos: The current position in the debugfs "file" - * - * This function provides a write implementation for the "count" debugfs - * interface to the hardware latency detector. Can be used to write a - * desired value, especially to zero the total count. - */ -static ssize_t debug_count_fwrite(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) -{ - return simple_data_write(filp, ubuf, cnt, ppos, &data.count); -} - -/** - * debug_enable_fopen - Dummy open function for "enable" debugfs interface - * @inode: The in-kernel inode representation of the debugfs "file" - * @filp: The active open file structure for the debugfs "file" - * - * This function provides an open implementation for the "enable" debugfs - * interface to the hardware latency detector. - */ -static int debug_enable_fopen(struct inode *inode, struct file *filp) -{ - return 0; -} - -/** - * debug_enable_fread - Read function for "enable" debugfs interface - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The userspace provided buffer to read value into - * @cnt: The maximum number of bytes to read - * @ppos: The current "file" position - * - * This function provides a read implementation for the "enable" debugfs - * interface to the hardware latency detector. Can be used to determine - * whether the detector is currently enabled ("0\n" or "1\n" returned). - */ -static ssize_t debug_enable_fread(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos) -{ - char buf[4]; - - if ((cnt < sizeof(buf)) || (*ppos)) - return 0; - - buf[0] = enabled ? '1' : '0'; - buf[1] = '\n'; - buf[2] = '\0'; - if (copy_to_user(ubuf, buf, strlen(buf))) - return -EFAULT; - return *ppos = strlen(buf); -} - -/** - * debug_enable_fwrite - Write function for "enable" debugfs interface - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The user buffer that contains the value to write - * @cnt: The maximum number of bytes to write to "file" - * @ppos: The current position in the debugfs "file" - * - * This function provides a write implementation for the "enable" debugfs - * interface to the hardware latency detector. Can be used to enable or - * disable the detector, which will have the side-effect of possibly - * also resetting the global stats and kicking off the measuring - * kthread (on an enable) or the converse (upon a disable). - */ -static ssize_t debug_enable_fwrite(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) -{ - char buf[4]; - int csize = min(cnt, sizeof(buf)); - long val = 0; - int err = 0; - - memset(buf, '\0', sizeof(buf)); - if (copy_from_user(buf, ubuf, csize)) - return -EFAULT; - - buf[sizeof(buf)-1] = '\0'; /* just in case */ - err = kstrtoul(buf, 10, &val); - if (0 != err) - return -EINVAL; - - if (val) { - if (enabled) - goto unlock; - enabled = 1; - __reset_stats(); - if (start_kthread()) - return -EFAULT; - } else { - if (!enabled) - goto unlock; - enabled = 0; - err = stop_kthread(); - if (err) { - pr_err(BANNER "cannot stop kthread\n"); - return -EFAULT; - } - wake_up(&data.wq); /* reader(s) should return */ - } -unlock: - return csize; -} - -/** - * debug_max_fopen - Open function for "max" debugfs entry - * @inode: The in-kernel inode representation of the debugfs "file" - * @filp: The active open file structure for the debugfs "file" - * - * This function provides an open implementation for the "max" debugfs - * interface to the hardware latency detector. - */ -static int debug_max_fopen(struct inode *inode, struct file *filp) -{ - return 0; -} - -/** - * debug_max_fread - Read function for "max" debugfs entry - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The userspace provided buffer to read value into - * @cnt: The maximum number of bytes to read - * @ppos: The current "file" position - * - * This function provides a read implementation for the "max" debugfs - * interface to the hardware latency detector. Can be used to determine - * the maximum latency value observed since it was last reset. - */ -static ssize_t debug_max_fread(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos) -{ - return simple_data_read(filp, ubuf, cnt, ppos, &data.max_sample); -} - -/** - * debug_max_fwrite - Write function for "max" debugfs entry - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The user buffer that contains the value to write - * @cnt: The maximum number of bytes to write to "file" - * @ppos: The current position in the debugfs "file" - * - * This function provides a write implementation for the "max" debugfs - * interface to the hardware latency detector. Can be used to reset the - * maximum or set it to some other desired value - if, then, subsequent - * measurements exceed this value, the maximum will be updated. - */ -static ssize_t debug_max_fwrite(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) -{ - return simple_data_write(filp, ubuf, cnt, ppos, &data.max_sample); -} - - -/** - * debug_sample_fopen - An open function for "sample" debugfs interface - * @inode: The in-kernel inode representation of this debugfs "file" - * @filp: The active open file structure for the debugfs "file" - * - * This function handles opening the "sample" file within the hardware - * latency detector debugfs directory interface. This file is used to read - * raw samples from the global ring_buffer and allows the user to see a - * running latency history. Can be opened blocking or non-blocking, - * affecting whether it behaves as a buffer read pipe, or does not. - * Implements simple locking to prevent multiple simultaneous use. - */ -static int debug_sample_fopen(struct inode *inode, struct file *filp) -{ - if (!atomic_add_unless(&data.sample_open, 1, 1)) - return -EBUSY; - else - return 0; -} - -/** - * debug_sample_fread - A read function for "sample" debugfs interface - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The user buffer that will contain the samples read - * @cnt: The maximum bytes to read from the debugfs "file" - * @ppos: The current position in the debugfs "file" - * - * This function handles reading from the "sample" file within the hardware - * latency detector debugfs directory interface. This file is used to read - * raw samples from the global ring_buffer and allows the user to see a - * running latency history. By default this will block pending a new - * value written into the sample buffer, unless there are already a - * number of value(s) waiting in the buffer, or the sample file was - * previously opened in a non-blocking mode of operation. - */ -static ssize_t debug_sample_fread(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos) -{ - int len = 0; - char buf[64]; - struct sample *sample = NULL; - - if (!enabled) - return 0; - - sample = kzalloc(sizeof(struct sample), GFP_KERNEL); - if (!sample) - return -ENOMEM; - - while (!buffer_get_sample(sample)) { - - DEFINE_WAIT(wait); - - if (filp->f_flags & O_NONBLOCK) { - len = -EAGAIN; - goto out; - } - - prepare_to_wait(&data.wq, &wait, TASK_INTERRUPTIBLE); - schedule(); - finish_wait(&data.wq, &wait); - - if (signal_pending(current)) { - len = -EINTR; - goto out; - } - - if (!enabled) { /* enable was toggled */ - len = 0; - goto out; - } - } - - len = snprintf(buf, sizeof(buf), "%010lu.%010lu\t%llu\t%llu\n", - sample->timestamp.tv_sec, - sample->timestamp.tv_nsec, - sample->duration, - sample->outer_duration); - - - /* handling partial reads is more trouble than it's worth */ - if (len > cnt) - goto out; - - if (copy_to_user(ubuf, buf, len)) - len = -EFAULT; - -out: - kfree(sample); - return len; -} - -/** - * debug_sample_release - Release function for "sample" debugfs interface - * @inode: The in-kernel inode represenation of the debugfs "file" - * @filp: The active open file structure for the debugfs "file" - * - * This function completes the close of the debugfs interface "sample" file. - * Frees the sample_open "lock" so that other users may open the interface. - */ -static int debug_sample_release(struct inode *inode, struct file *filp) -{ - atomic_dec(&data.sample_open); - - return 0; -} - -/** - * debug_threshold_fopen - Open function for "threshold" debugfs entry - * @inode: The in-kernel inode representation of the debugfs "file" - * @filp: The active open file structure for the debugfs "file" - * - * This function provides an open implementation for the "threshold" debugfs - * interface to the hardware latency detector. - */ -static int debug_threshold_fopen(struct inode *inode, struct file *filp) -{ - return 0; -} - -/** - * debug_threshold_fread - Read function for "threshold" debugfs entry - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The userspace provided buffer to read value into - * @cnt: The maximum number of bytes to read - * @ppos: The current "file" position - * - * This function provides a read implementation for the "threshold" debugfs - * interface to the hardware latency detector. It can be used to determine - * the current threshold level at which a latency will be recorded in the - * global ring buffer, typically on the order of 10us. - */ -static ssize_t debug_threshold_fread(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos) -{ - return simple_data_read(filp, ubuf, cnt, ppos, &data.threshold); -} - -/** - * debug_threshold_fwrite - Write function for "threshold" debugfs entry - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The user buffer that contains the value to write - * @cnt: The maximum number of bytes to write to "file" - * @ppos: The current position in the debugfs "file" - * - * This function provides a write implementation for the "threshold" debugfs - * interface to the hardware latency detector. It can be used to configure - * the threshold level at which any subsequently detected latencies will - * be recorded into the global ring buffer. - */ -static ssize_t debug_threshold_fwrite(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) -{ - int ret; - - ret = simple_data_write(filp, ubuf, cnt, ppos, &data.threshold); - - if (enabled) - wake_up_process(kthread); - - return ret; -} - -/** - * debug_width_fopen - Open function for "width" debugfs entry - * @inode: The in-kernel inode representation of the debugfs "file" - * @filp: The active open file structure for the debugfs "file" - * - * This function provides an open implementation for the "width" debugfs - * interface to the hardware latency detector. - */ -static int debug_width_fopen(struct inode *inode, struct file *filp) -{ - return 0; -} - -/** - * debug_width_fread - Read function for "width" debugfs entry - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The userspace provided buffer to read value into - * @cnt: The maximum number of bytes to read - * @ppos: The current "file" position - * - * This function provides a read implementation for the "width" debugfs - * interface to the hardware latency detector. It can be used to determine - * for how many us of the total window us we will actively sample for any - * hardware-induced latecy periods. Obviously, it is not possible to - * sample constantly and have the system respond to a sample reader, or, - * worse, without having the system appear to have gone out to lunch. - */ -static ssize_t debug_width_fread(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos) -{ - return simple_data_read(filp, ubuf, cnt, ppos, &data.sample_width); -} - -/** - * debug_width_fwrite - Write function for "width" debugfs entry - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The user buffer that contains the value to write - * @cnt: The maximum number of bytes to write to "file" - * @ppos: The current position in the debugfs "file" - * - * This function provides a write implementation for the "width" debugfs - * interface to the hardware latency detector. It can be used to configure - * for how many us of the total window us we will actively sample for any - * hardware-induced latency periods. Obviously, it is not possible to - * sample constantly and have the system respond to a sample reader, or, - * worse, without having the system appear to have gone out to lunch. It - * is enforced that width is less that the total window size. - */ -static ssize_t debug_width_fwrite(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) -{ - char buf[U64STR_SIZE]; - int csize = min(cnt, sizeof(buf)); - u64 val = 0; - int err = 0; - - memset(buf, '\0', sizeof(buf)); - if (copy_from_user(buf, ubuf, csize)) - return -EFAULT; - - buf[U64STR_SIZE-1] = '\0'; /* just in case */ - err = kstrtoull(buf, 10, &val); - if (0 != err) - return -EINVAL; - - mutex_lock(&data.lock); - if (val < data.sample_window) - data.sample_width = val; - else { - mutex_unlock(&data.lock); - return -EINVAL; - } - mutex_unlock(&data.lock); - - if (enabled) - wake_up_process(kthread); - - return csize; -} - -/** - * debug_window_fopen - Open function for "window" debugfs entry - * @inode: The in-kernel inode representation of the debugfs "file" - * @filp: The active open file structure for the debugfs "file" - * - * This function provides an open implementation for the "window" debugfs - * interface to the hardware latency detector. The window is the total time - * in us that will be considered one sample period. Conceptually, windows - * occur back-to-back and contain a sample width period during which - * actual sampling occurs. - */ -static int debug_window_fopen(struct inode *inode, struct file *filp) -{ - return 0; -} - -/** - * debug_window_fread - Read function for "window" debugfs entry - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The userspace provided buffer to read value into - * @cnt: The maximum number of bytes to read - * @ppos: The current "file" position - * - * This function provides a read implementation for the "window" debugfs - * interface to the hardware latency detector. The window is the total time - * in us that will be considered one sample period. Conceptually, windows - * occur back-to-back and contain a sample width period during which - * actual sampling occurs. Can be used to read the total window size. - */ -static ssize_t debug_window_fread(struct file *filp, char __user *ubuf, - size_t cnt, loff_t *ppos) -{ - return simple_data_read(filp, ubuf, cnt, ppos, &data.sample_window); -} - -/** - * debug_window_fwrite - Write function for "window" debugfs entry - * @filp: The active open file structure for the debugfs "file" - * @ubuf: The user buffer that contains the value to write - * @cnt: The maximum number of bytes to write to "file" - * @ppos: The current position in the debugfs "file" - * - * This function provides a write implementation for the "window" debufds - * interface to the hardware latency detetector. The window is the total time - * in us that will be considered one sample period. Conceptually, windows - * occur back-to-back and contain a sample width period during which - * actual sampling occurs. Can be used to write a new total window size. It - * is enfoced that any value written must be greater than the sample width - * size, or an error results. - */ -static ssize_t debug_window_fwrite(struct file *filp, - const char __user *ubuf, - size_t cnt, - loff_t *ppos) -{ - char buf[U64STR_SIZE]; - int csize = min(cnt, sizeof(buf)); - u64 val = 0; - int err = 0; - - memset(buf, '\0', sizeof(buf)); - if (copy_from_user(buf, ubuf, csize)) - return -EFAULT; - - buf[U64STR_SIZE-1] = '\0'; /* just in case */ - err = kstrtoull(buf, 10, &val); - if (0 != err) - return -EINVAL; - - mutex_lock(&data.lock); - if (data.sample_width < val) - data.sample_window = val; - else { - mutex_unlock(&data.lock); - return -EINVAL; - } - mutex_unlock(&data.lock); - - return csize; -} - -/* - * Function pointers for the "count" debugfs file operations - */ -static const struct file_operations count_fops = { - .open = debug_count_fopen, - .read = debug_count_fread, - .write = debug_count_fwrite, - .owner = THIS_MODULE, -}; - -/* - * Function pointers for the "enable" debugfs file operations - */ -static const struct file_operations enable_fops = { - .open = debug_enable_fopen, - .read = debug_enable_fread, - .write = debug_enable_fwrite, - .owner = THIS_MODULE, -}; - -/* - * Function pointers for the "max" debugfs file operations - */ -static const struct file_operations max_fops = { - .open = debug_max_fopen, - .read = debug_max_fread, - .write = debug_max_fwrite, - .owner = THIS_MODULE, -}; - -/* - * Function pointers for the "sample" debugfs file operations - */ -static const struct file_operations sample_fops = { - .open = debug_sample_fopen, - .read = debug_sample_fread, - .release = debug_sample_release, - .owner = THIS_MODULE, -}; - -/* - * Function pointers for the "threshold" debugfs file operations - */ -static const struct file_operations threshold_fops = { - .open = debug_threshold_fopen, - .read = debug_threshold_fread, - .write = debug_threshold_fwrite, - .owner = THIS_MODULE, -}; - -/* - * Function pointers for the "width" debugfs file operations - */ -static const struct file_operations width_fops = { - .open = debug_width_fopen, - .read = debug_width_fread, - .write = debug_width_fwrite, - .owner = THIS_MODULE, -}; - -/* - * Function pointers for the "window" debugfs file operations - */ -static const struct file_operations window_fops = { - .open = debug_window_fopen, - .read = debug_window_fread, - .write = debug_window_fwrite, - .owner = THIS_MODULE, -}; - -/** - * init_debugfs - A function to initialize the debugfs interface files - * - * This function creates entries in debugfs for "hwlat_detector", including - * files to read values from the detector, current samples, and the - * maximum sample that has been captured since the hardware latency - * dectector was started. - */ -static int init_debugfs(void) -{ - int ret = -ENOMEM; - - debug_dir = debugfs_create_dir(DRVNAME, NULL); - if (!debug_dir) - goto err_debug_dir; - - debug_sample = debugfs_create_file("sample", 0444, - debug_dir, NULL, - &sample_fops); - if (!debug_sample) - goto err_sample; - - debug_count = debugfs_create_file("count", 0444, - debug_dir, NULL, - &count_fops); - if (!debug_count) - goto err_count; - - debug_max = debugfs_create_file("max", 0444, - debug_dir, NULL, - &max_fops); - if (!debug_max) - goto err_max; - - debug_sample_window = debugfs_create_file("window", 0644, - debug_dir, NULL, - &window_fops); - if (!debug_sample_window) - goto err_window; - - debug_sample_width = debugfs_create_file("width", 0644, - debug_dir, NULL, - &width_fops); - if (!debug_sample_width) - goto err_width; - - debug_threshold = debugfs_create_file("threshold", 0644, - debug_dir, NULL, - &threshold_fops); - if (!debug_threshold) - goto err_threshold; - - debug_enable = debugfs_create_file("enable", 0644, - debug_dir, &enabled, - &enable_fops); - if (!debug_enable) - goto err_enable; - - else { - ret = 0; - goto out; - } - -err_enable: - debugfs_remove(debug_threshold); -err_threshold: - debugfs_remove(debug_sample_width); -err_width: - debugfs_remove(debug_sample_window); -err_window: - debugfs_remove(debug_max); -err_max: - debugfs_remove(debug_count); -err_count: - debugfs_remove(debug_sample); -err_sample: - debugfs_remove(debug_dir); -err_debug_dir: -out: - return ret; -} - -/** - * free_debugfs - A function to cleanup the debugfs file interface - */ -static void free_debugfs(void) -{ - /* could also use a debugfs_remove_recursive */ - debugfs_remove(debug_enable); - debugfs_remove(debug_threshold); - debugfs_remove(debug_sample_width); - debugfs_remove(debug_sample_window); - debugfs_remove(debug_max); - debugfs_remove(debug_count); - debugfs_remove(debug_sample); - debugfs_remove(debug_dir); -} - -/** - * detector_init - Standard module initialization code - */ -static int detector_init(void) -{ - int ret = -ENOMEM; - - pr_info(BANNER "version %s\n", VERSION); - - ret = init_stats(); - if (0 != ret) - goto out; - - ret = init_debugfs(); - if (0 != ret) - goto err_stats; - - if (enabled) - ret = start_kthread(); - - goto out; - -err_stats: - ring_buffer_free(ring_buffer); -out: - return ret; - -} - -/** - * detector_exit - Standard module cleanup code - */ -static void detector_exit(void) -{ - int err; - - if (enabled) { - enabled = 0; - err = stop_kthread(); - if (err) - pr_err(BANNER "cannot stop kthread\n"); - } - - free_debugfs(); - ring_buffer_free(ring_buffer); /* free up the ring buffer */ - -} - -module_init(detector_init); -module_exit(detector_exit); diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index cd2033c..72b6823 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -71,7 +71,7 @@ static int mei_cl_device_probe(struct device *dev) dev_dbg(dev, "Device probe\n"); - strncpy(id.name, dev_name(dev), sizeof(id.name)); + strlcpy(id.name, dev_name(dev), sizeof(id.name)); return driver->probe(device, &id); } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index b66cec9..e9ea08d 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -74,23 +74,69 @@ int mei_me_cl_by_id(struct mei_device *dev, u8 client_id) /** - * mei_io_list_flush - removes list entry belonging to cl. + * mei_cl_cmp_id - tells if the clients are the same * - * @list: An instance of our list structure - * @cl: host client + * @cl1: host client 1 + * @cl2: host client 2 + * + * returns true - if the clients has same host and me ids + * false - otherwise + */ +static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, + const struct mei_cl *cl2) +{ + return cl1 && cl2 && + (cl1->host_client_id == cl2->host_client_id) && + (cl1->me_client_id == cl2->me_client_id); +} + +/** + * mei_io_list_flush - removes cbs belonging to cl. + * + * @list: an instance of our list structure + * @cl: host client, can be NULL for flushing the whole list + * @free: whether to free the cbs */ -void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) +static void __mei_io_list_flush(struct mei_cl_cb *list, + struct mei_cl *cl, bool free) { struct mei_cl_cb *cb; struct mei_cl_cb *next; + /* enable removing everything if no cl is specified */ list_for_each_entry_safe(cb, next, &list->list, list) { - if (cb->cl && mei_cl_cmp_id(cl, cb->cl)) + if (!cl || (cb->cl && mei_cl_cmp_id(cl, cb->cl))) { list_del(&cb->list); + if (free) + mei_io_cb_free(cb); + } } } /** + * mei_io_list_flush - removes list entry belonging to cl. + * + * @list: An instance of our list structure + * @cl: host client + */ +static inline void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) +{ + __mei_io_list_flush(list, cl, false); +} + + +/** + * mei_io_list_free - removes cb belonging to cl and free them + * + * @list: An instance of our list structure + * @cl: host client + */ +static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) +{ + __mei_io_list_flush(list, cl, true); +} + +/** * mei_io_cb_free - free mei_cb_private related memory * * @cb: mei callback struct @@ -192,8 +238,8 @@ int mei_cl_flush_queues(struct mei_cl *cl) dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n"); mei_io_list_flush(&cl->dev->read_list, cl); - mei_io_list_flush(&cl->dev->write_list, cl); - mei_io_list_flush(&cl->dev->write_waiting_list, cl); + mei_io_list_free(&cl->dev->write_list, cl); + mei_io_list_free(&cl->dev->write_waiting_list, cl); mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); @@ -405,6 +451,7 @@ int mei_cl_disconnect(struct mei_cl *cl) dev_err(&dev->pdev->dev, "failed to disconnect.\n"); goto free; } + cl->timer_count = MEI_CONNECT_TIMEOUT; mdelay(10); /* Wait for hardware disconnection ready */ list_add_tail(&cb->list, &dev->ctrl_rd_list.list); } else { @@ -916,20 +963,8 @@ void mei_cl_all_wakeup(struct mei_device *dev) */ void mei_cl_all_write_clear(struct mei_device *dev) { - struct mei_cl_cb *cb, *next; - struct list_head *list; - - list = &dev->write_list.list; - list_for_each_entry_safe(cb, next, list, list) { - list_del(&cb->list); - mei_io_cb_free(cb); - } - - list = &dev->write_waiting_list.list; - list_for_each_entry_safe(cb, next, list, list) { - list_del(&cb->list); - mei_io_cb_free(cb); - } + mei_io_list_free(&dev->write_list, NULL); + mei_io_list_free(&dev->write_waiting_list, NULL); } diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 892cc42..5d75ab5 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -45,8 +45,6 @@ static inline void mei_io_list_init(struct mei_cl_cb *list) { INIT_LIST_HEAD(&list->list); } -void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl); - /* * MEI Host Client Functions */ @@ -61,22 +59,6 @@ int mei_cl_unlink(struct mei_cl *cl); int mei_cl_flush_queues(struct mei_cl *cl); struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl); -/** - * mei_cl_cmp_id - tells if file private data have same id - * - * @fe1: private data of 1. file object - * @fe2: private data of 2. file object - * - * returns true - if ids are the same and not NULL - */ -static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, - const struct mei_cl *cl2) -{ - return cl1 && cl2 && - (cl1->host_client_id == cl2->host_client_id) && - (cl1->me_client_id == cl2->me_client_id); -} - int mei_cl_flow_ctrl_creds(struct mei_cl *cl); diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 66f411a..cabc043 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -115,6 +115,11 @@ #define MEI_DEV_ID_LPT_HR 0x8CBA /* Lynx Point H Refresh */ #define MEI_DEV_ID_WPT_LP 0x9CBA /* Wildcat Point LP */ + +/* Host Firmware Status Registers in PCI Config Space */ +#define PCI_CFG_HFS_1 0x40 +#define PCI_CFG_HFS_2 0x48 + /* * MEI HW Section */ diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 3412adc..e513354 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -164,6 +164,9 @@ static void mei_me_hw_reset_release(struct mei_device *dev) hcsr |= H_IG; hcsr &= ~H_RST; mei_hcsr_set(hw, hcsr); + + /* complete this write before we set host ready on another CPU */ + mmiowb(); } /** * mei_me_hw_reset - resets fw via mei csr register. @@ -183,9 +186,22 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) else hcsr &= ~H_IE; + dev->recvd_hw_ready = false; mei_me_reg_write(hw, H_CSR, hcsr); - if (dev->dev_state == MEI_DEV_POWER_DOWN) + /* + * Host reads the H_CSR once to ensure that the + * posted write to H_CSR completes. + */ + hcsr = mei_hcsr_read(hw); + + if ((hcsr & H_RST) == 0) + dev_warn(&dev->pdev->dev, "H_RST is not set = 0x%08X", hcsr); + + if ((hcsr & H_RDY) == H_RDY) + dev_warn(&dev->pdev->dev, "H_RDY is not cleared 0x%08X", hcsr); + + if (intr_enable == false) mei_me_hw_reset_release(dev); return 0; @@ -201,6 +217,7 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) static void mei_me_host_set_ready(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); + hw->host_hw_state = mei_hcsr_read(hw); hw->host_hw_state |= H_IE | H_IG | H_RDY; mei_hcsr_set(hw, hw->host_hw_state); } @@ -233,10 +250,7 @@ static bool mei_me_hw_is_ready(struct mei_device *dev) static int mei_me_hw_ready_wait(struct mei_device *dev) { int err; - if (mei_me_hw_is_ready(dev)) - return 0; - dev->recvd_hw_ready = false; mutex_unlock(&dev->device_lock); err = wait_event_interruptible_timeout(dev->wait_hw_ready, dev->recvd_hw_ready, @@ -496,19 +510,15 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) /* check if we need to start the dev */ if (!mei_host_is_ready(dev)) { if (mei_hw_is_ready(dev)) { + mei_me_hw_reset_release(dev); dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); dev->recvd_hw_ready = true; wake_up_interruptible(&dev->wait_hw_ready); - - mutex_unlock(&dev->device_lock); - return IRQ_HANDLED; } else { - dev_dbg(&dev->pdev->dev, "Reset Completed.\n"); - mei_me_hw_reset_release(dev); - mutex_unlock(&dev->device_lock); - return IRQ_HANDLED; + dev_dbg(&dev->pdev->dev, "Spurious Interrupt\n"); } + goto end; } /* check slots available for reading */ slots = mei_count_full_read_slots(dev); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 1b922e9..41b4b48 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -420,8 +420,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) cl->status = 0; list_del(&cb->list); - if (MEI_WRITING == cl->writing_state && - cb->fop_type == MEI_FOP_WRITE && + if (cb->fop_type == MEI_FOP_WRITE && cl != &dev->iamthif_cl) { dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n"); cl->writing_state = MEI_WRITE_COMPLETE; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index cabeddd..9558bef 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -648,8 +648,7 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) goto out; } - if (MEI_WRITE_COMPLETE == cl->writing_state) - mask |= (POLLIN | POLLRDNORM); + mask |= (POLLIN | POLLRDNORM); out: mutex_unlock(&dev->device_lock); diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index 994ca4a..4b7ea3f 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -342,9 +342,10 @@ static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length) ndev = (struct mei_nfc_dev *) cldev->priv_data; dev = ndev->cl->dev; + err = -ENOMEM; mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL); if (!mei_buf) - return -ENOMEM; + goto out; hdr = (struct mei_nfc_hci_hdr *) mei_buf; hdr->cmd = MEI_NFC_CMD_HCI_SEND; @@ -354,12 +355,9 @@ static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length) hdr->data_size = length; memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); - err = __mei_cl_send(ndev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE); if (err < 0) - return err; - - kfree(mei_buf); + goto out; if (!wait_event_interruptible_timeout(ndev->send_wq, ndev->recv_req_id == ndev->req_id, HZ)) { @@ -368,7 +366,8 @@ static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length) } else { ndev->req_id++; } - +out: + kfree(mei_buf); return err; } diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index e637318..20fb058 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -100,15 +100,31 @@ static bool mei_me_quirk_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { u32 reg; - if (ent->device == MEI_DEV_ID_PBG_1) { - pci_read_config_dword(pdev, 0x48, ®); - /* make sure that bit 9 is up and bit 10 is down */ - if ((reg & 0x600) == 0x200) { - dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n"); - return false; - } + /* Cougar Point || Patsburg */ + if (ent->device == MEI_DEV_ID_CPT_1 || + ent->device == MEI_DEV_ID_PBG_1) { + pci_read_config_dword(pdev, PCI_CFG_HFS_2, ®); + /* make sure that bit 9 (NM) is up and bit 10 (DM) is down */ + if ((reg & 0x600) == 0x200) + goto no_mei; } + + /* Lynx Point */ + if (ent->device == MEI_DEV_ID_LPT_H || + ent->device == MEI_DEV_ID_LPT_W || + ent->device == MEI_DEV_ID_LPT_HR) { + /* Read ME FW Status check for SPS Firmware */ + pci_read_config_dword(pdev, PCI_CFG_HFS_1, ®); + /* if bits [19:16] = 15, running SPS Firmware */ + if ((reg & 0xf0000) == 0xf0000) + goto no_mei; + } + return true; + +no_mei: + dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n"); + return false; } /** * mei_probe - Device Initialization Routine |