mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
Char/Misc driver patches for 3.15-rc1
Here's the big char/misc driver updates for 3.15-rc1. Lots of various things here, including the new mcb driver subsystem. All of these have been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <[email protected]> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iEYEABECAAYFAlM7ArIACgkQMUfUDdst+ylS+gCfcJr0Zo2v5aWnqD7rFtFETmFI LhcAoNTQ4cvlVdxnI0driWCWFYxLj6at =aj+L -----END PGP SIGNATURE----- Merge tag 'char-misc-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc driver patches from Greg KH: "Here's the big char/misc driver updates for 3.15-rc1. Lots of various things here, including the new mcb driver subsystem. All of these have been in linux-next for a while" * tag 'char-misc-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (118 commits) extcon: Move OF helper function to extcon core and change function name extcon: of: Remove unnecessary function call by using the name of device_node extcon: gpio: Use SIMPLE_DEV_PM_OPS macro extcon: palmas: Use SIMPLE_DEV_PM_OPS macro mei: don't use deprecated DEFINE_PCI_DEVICE_TABLE macro mei: amthif: fix checkpatch error mei: client.h fix checkpatch errors mei: use cl_dbg where appropriate mei: fix Unnecessary space after function pointer name mei: report consistently copy_from/to_user failures mei: drop pr_fmt macros mei: make me hw headers private to me hw. mei: fix memory leak of pending write cb objects mei: me: do not reset when less than expected data is received drivers: mcb: Fix build error discovered by 0-day bot cs5535-mfgpt: Simplify dependencies spmi: pm: drop bus-level PM suspend/resume routines spmi: pmic_arb: make selectable on ARCH_QCOM Drivers: hv: vmbus: Increase the limit on the number of pfns we can handle pch_phub: Report error writing MAC back to user ...
This commit is contained in:
commit
675c354a95
167 changed files with 8865 additions and 1722 deletions
|
|
@ -235,7 +235,7 @@ config SGI_XP
|
|||
|
||||
config CS5535_MFGPT
|
||||
tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
|
||||
depends on PCI && X86 && MFD_CS5535
|
||||
depends on MFD_CS5535
|
||||
default n
|
||||
help
|
||||
This driver provides access to MFGPT functionality for other
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include "bmp085.h"
|
||||
|
|
|
|||
|
|
@ -101,7 +101,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/io.h>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/list.h>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kobject.h>
|
||||
|
|
@ -96,7 +95,7 @@ static int sunxi_sid_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct of_device_id sunxi_sid_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-sid", .data = (void *)16},
|
||||
{ .compatible = "allwinner,sun4i-a10-sid", .data = (void *)16},
|
||||
{ .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},
|
||||
{/* sentinel */},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
*
|
||||
* See Documentation/fault-injection/provoke-crashes.txt for instructions
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
|
|
@ -45,6 +46,7 @@
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mman.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#ifdef CONFIG_IDE
|
||||
#include <linux/ide.h>
|
||||
|
|
@ -101,6 +103,7 @@ enum ctype {
|
|||
CT_EXEC_USERSPACE,
|
||||
CT_ACCESS_USERSPACE,
|
||||
CT_WRITE_RO,
|
||||
CT_WRITE_KERN,
|
||||
};
|
||||
|
||||
static char* cp_name[] = {
|
||||
|
|
@ -137,6 +140,7 @@ static char* cp_type[] = {
|
|||
"EXEC_USERSPACE",
|
||||
"ACCESS_USERSPACE",
|
||||
"WRITE_RO",
|
||||
"WRITE_KERN",
|
||||
};
|
||||
|
||||
static struct jprobe lkdtm;
|
||||
|
|
@ -316,6 +320,13 @@ static void do_nothing(void)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Must immediately follow do_nothing for size calculuations to work out. */
|
||||
static void do_overwritten(void)
|
||||
{
|
||||
pr_info("do_overwritten wasn't overwritten!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static noinline void corrupt_stack(void)
|
||||
{
|
||||
/* Use default char array length that triggers stack protection. */
|
||||
|
|
@ -328,7 +339,12 @@ static void execute_location(void *dst)
|
|||
{
|
||||
void (*func)(void) = dst;
|
||||
|
||||
pr_info("attempting ok execution at %p\n", do_nothing);
|
||||
do_nothing();
|
||||
|
||||
memcpy(dst, do_nothing, EXEC_SIZE);
|
||||
flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE);
|
||||
pr_info("attempting bad execution at %p\n", func);
|
||||
func();
|
||||
}
|
||||
|
||||
|
|
@ -337,8 +353,13 @@ static void execute_user_location(void *dst)
|
|||
/* Intentionally crossing kernel/user memory boundary. */
|
||||
void (*func)(void) = dst;
|
||||
|
||||
pr_info("attempting ok execution at %p\n", do_nothing);
|
||||
do_nothing();
|
||||
|
||||
if (copy_to_user((void __user *)dst, do_nothing, EXEC_SIZE))
|
||||
return;
|
||||
flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE);
|
||||
pr_info("attempting bad execution at %p\n", func);
|
||||
func();
|
||||
}
|
||||
|
||||
|
|
@ -463,8 +484,12 @@ static void lkdtm_do_action(enum ctype which)
|
|||
}
|
||||
|
||||
ptr = (unsigned long *)user_addr;
|
||||
|
||||
pr_info("attempting bad read at %p\n", ptr);
|
||||
tmp = *ptr;
|
||||
tmp += 0xc0dec0de;
|
||||
|
||||
pr_info("attempting bad write at %p\n", ptr);
|
||||
*ptr = tmp;
|
||||
|
||||
vm_munmap(user_addr, PAGE_SIZE);
|
||||
|
|
@ -475,10 +500,28 @@ static void lkdtm_do_action(enum ctype which)
|
|||
unsigned long *ptr;
|
||||
|
||||
ptr = (unsigned long *)&rodata;
|
||||
|
||||
pr_info("attempting bad write at %p\n", ptr);
|
||||
*ptr ^= 0xabcd1234;
|
||||
|
||||
break;
|
||||
}
|
||||
case CT_WRITE_KERN: {
|
||||
size_t size;
|
||||
unsigned char *ptr;
|
||||
|
||||
size = (unsigned long)do_overwritten -
|
||||
(unsigned long)do_nothing;
|
||||
ptr = (unsigned char *)do_overwritten;
|
||||
|
||||
pr_info("attempting bad %zu byte write at %p\n", size, ptr);
|
||||
memcpy(ptr, (unsigned char *)do_nothing, size);
|
||||
flush_icache_range((unsigned long)ptr,
|
||||
(unsigned long)(ptr + size));
|
||||
|
||||
do_overwritten();
|
||||
break;
|
||||
}
|
||||
case CT_NONE:
|
||||
default:
|
||||
break;
|
||||
|
|
@ -493,8 +536,8 @@ static void lkdtm_handler(void)
|
|||
|
||||
spin_lock_irqsave(&count_lock, flags);
|
||||
count--;
|
||||
printk(KERN_INFO "lkdtm: Crash point %s of type %s hit, trigger in %d rounds\n",
|
||||
cp_name_to_str(cpoint), cp_type_to_str(cptype), count);
|
||||
pr_info("Crash point %s of type %s hit, trigger in %d rounds\n",
|
||||
cp_name_to_str(cpoint), cp_type_to_str(cptype), count);
|
||||
|
||||
if (count == 0) {
|
||||
do_it = true;
|
||||
|
|
@ -551,18 +594,18 @@ static int lkdtm_register_cpoint(enum cname which)
|
|||
lkdtm.kp.symbol_name = "generic_ide_ioctl";
|
||||
lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
|
||||
#else
|
||||
printk(KERN_INFO "lkdtm: Crash point not available\n");
|
||||
pr_info("Crash point not available\n");
|
||||
return -EINVAL;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
printk(KERN_INFO "lkdtm: Invalid Crash Point\n");
|
||||
pr_info("Invalid Crash Point\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cpoint = which;
|
||||
if ((ret = register_jprobe(&lkdtm)) < 0) {
|
||||
printk(KERN_INFO "lkdtm: Couldn't register jprobe\n");
|
||||
pr_info("Couldn't register jprobe\n");
|
||||
cpoint = CN_INVALID;
|
||||
}
|
||||
|
||||
|
|
@ -709,8 +752,7 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf,
|
|||
if (type == CT_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
printk(KERN_INFO "lkdtm: Performing direct entry %s\n",
|
||||
cp_type_to_str(type));
|
||||
pr_info("Performing direct entry %s\n", cp_type_to_str(type));
|
||||
lkdtm_do_action(type);
|
||||
*off += count;
|
||||
|
||||
|
|
@ -772,7 +814,7 @@ static int __init lkdtm_module_init(void)
|
|||
/* Register debugfs interface */
|
||||
lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL);
|
||||
if (!lkdtm_debugfs_root) {
|
||||
printk(KERN_ERR "lkdtm: creating root dir failed\n");
|
||||
pr_err("creating root dir failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
|
@ -787,28 +829,26 @@ static int __init lkdtm_module_init(void)
|
|||
de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root,
|
||||
NULL, &cur->fops);
|
||||
if (de == NULL) {
|
||||
printk(KERN_ERR "lkdtm: could not create %s\n",
|
||||
cur->name);
|
||||
pr_err("could not create %s\n", cur->name);
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
if (lkdtm_parse_commandline() == -EINVAL) {
|
||||
printk(KERN_INFO "lkdtm: Invalid command\n");
|
||||
pr_info("Invalid command\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (cpoint != CN_INVALID && cptype != CT_NONE) {
|
||||
ret = lkdtm_register_cpoint(cpoint);
|
||||
if (ret < 0) {
|
||||
printk(KERN_INFO "lkdtm: Invalid crash point %d\n",
|
||||
cpoint);
|
||||
pr_info("Invalid crash point %d\n", cpoint);
|
||||
goto out_err;
|
||||
}
|
||||
printk(KERN_INFO "lkdtm: Crash point %s of type %s registered\n",
|
||||
cpoint_name, cpoint_type);
|
||||
pr_info("Crash point %s of type %s registered\n",
|
||||
cpoint_name, cpoint_type);
|
||||
} else {
|
||||
printk(KERN_INFO "lkdtm: No crash points registered, enable through debugfs\n");
|
||||
pr_info("No crash points registered, enable through debugfs\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -823,7 +863,7 @@ static void __exit lkdtm_module_exit(void)
|
|||
debugfs_remove_recursive(lkdtm_debugfs_root);
|
||||
|
||||
unregister_jprobe(&lkdtm);
|
||||
printk(KERN_INFO "lkdtm: Crash point unregistered\n");
|
||||
pr_info("Crash point unregistered\n");
|
||||
}
|
||||
|
||||
module_init(lkdtm_module_init);
|
||||
|
|
|
|||
|
|
@ -34,3 +34,12 @@ config INTEL_MEI_ME
|
|||
82Q33 Express
|
||||
82X38/X48 Express
|
||||
|
||||
config INTEL_MEI_TXE
|
||||
tristate "Intel Trusted Execution Environment with ME Interface"
|
||||
select INTEL_MEI
|
||||
depends on X86 && PCI && WATCHDOG_CORE
|
||||
help
|
||||
MEI Support for Trusted Execution Environment device on Intel SoCs
|
||||
|
||||
Supported SoCs:
|
||||
Intel Bay Trail
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#
|
||||
# Makefile - Intel Management Engine Interface (Intel MEI) Linux driver
|
||||
# Copyright (c) 2010-2011, Intel Corporation.
|
||||
# Copyright (c) 2010-2014, Intel Corporation.
|
||||
#
|
||||
obj-$(CONFIG_INTEL_MEI) += mei.o
|
||||
mei-objs := init.o
|
||||
|
|
@ -17,3 +17,7 @@ mei-$(CONFIG_DEBUG_FS) += debugfs.o
|
|||
obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o
|
||||
mei-me-objs := pci-me.o
|
||||
mei-me-objs += hw-me.o
|
||||
|
||||
obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o
|
||||
mei-txe-objs := pci-txe.o
|
||||
mei-txe-objs += hw-txe.o
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/fcntl.h>
|
||||
#include <linux/aio.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/list.h>
|
||||
|
|
@ -35,7 +34,6 @@
|
|||
|
||||
#include "mei_dev.h"
|
||||
#include "hbm.h"
|
||||
#include "hw-me.h"
|
||||
#include "client.h"
|
||||
|
||||
const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,
|
||||
|
|
@ -79,10 +77,9 @@ int mei_amthif_host_init(struct mei_device *dev)
|
|||
|
||||
i = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
|
||||
if (i < 0) {
|
||||
ret = i;
|
||||
dev_info(&dev->pdev->dev,
|
||||
"amthif: failed to find the client %d\n", ret);
|
||||
return ret;
|
||||
"amthif: failed to find the client %d\n", i);
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
cl->me_client_id = dev->me_clients[i].client_id;
|
||||
|
|
@ -116,14 +113,11 @@ int mei_amthif_host_init(struct mei_device *dev)
|
|||
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
|
||||
if (mei_hbm_cl_connect_req(dev, cl)) {
|
||||
dev_dbg(&dev->pdev->dev, "amthif: Failed to connect to ME client\n");
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->host_client_id = 0;
|
||||
} else {
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
}
|
||||
return 0;
|
||||
ret = mei_cl_connect(cl, NULL);
|
||||
|
||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -137,14 +131,12 @@ int mei_amthif_host_init(struct mei_device *dev)
|
|||
struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
|
||||
struct file *file)
|
||||
{
|
||||
struct mei_cl_cb *pos = NULL;
|
||||
struct mei_cl_cb *next = NULL;
|
||||
struct mei_cl_cb *cb;
|
||||
|
||||
list_for_each_entry_safe(pos, next,
|
||||
&dev->amthif_rd_complete_list.list, list) {
|
||||
if (pos->cl && pos->cl == &dev->iamthif_cl &&
|
||||
pos->file_object == file)
|
||||
return pos;
|
||||
list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) {
|
||||
if (cb->cl && cb->cl == &dev->iamthif_cl &&
|
||||
cb->file_object == file)
|
||||
return cb;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -180,14 +172,13 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
|
|||
/* Only possible if we are in timeout */
|
||||
if (!cl || cl != &dev->iamthif_cl) {
|
||||
dev_dbg(&dev->pdev->dev, "bad file ext.\n");
|
||||
return -ETIMEDOUT;
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id);
|
||||
|
||||
if (i < 0) {
|
||||
dev_dbg(&dev->pdev->dev, "amthif client not found.\n");
|
||||
return -ENODEV;
|
||||
return -ENOTTY;
|
||||
}
|
||||
dev_dbg(&dev->pdev->dev, "checking amthif data\n");
|
||||
cb = mei_amthif_find_read_list_entry(dev, file);
|
||||
|
|
@ -228,7 +219,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
|
|||
dev_dbg(&dev->pdev->dev, "amthif Time out\n");
|
||||
/* 15 sec for the message has expired */
|
||||
list_del(&cb->list);
|
||||
rets = -ETIMEDOUT;
|
||||
rets = -ETIME;
|
||||
goto free;
|
||||
}
|
||||
}
|
||||
|
|
@ -253,9 +244,10 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
|
|||
* the buf_idx may point beyond */
|
||||
length = min_t(size_t, length, (cb->buf_idx - *offset));
|
||||
|
||||
if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length))
|
||||
if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) {
|
||||
dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n");
|
||||
rets = -EFAULT;
|
||||
else {
|
||||
} else {
|
||||
rets = length;
|
||||
if ((*offset + length) < cb->buf_idx) {
|
||||
*offset += length;
|
||||
|
|
@ -302,9 +294,8 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret && dev->hbuf_is_ready) {
|
||||
if (ret && mei_hbuf_acquire(dev)) {
|
||||
ret = 0;
|
||||
dev->hbuf_is_ready = false;
|
||||
if (cb->request_buffer.size > mei_hbuf_max_len(dev)) {
|
||||
mei_hdr.length = mei_hbuf_max_len(dev);
|
||||
mei_hdr.msg_complete = 0;
|
||||
|
|
@ -336,10 +327,6 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb)
|
|||
list_add_tail(&cb->list, &dev->write_list.list);
|
||||
}
|
||||
} else {
|
||||
if (!dev->hbuf_is_ready)
|
||||
dev_dbg(&dev->pdev->dev, "host buffer is not empty");
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "No flow control credentials, so add iamthif cb to write list.\n");
|
||||
list_add_tail(&cb->list, &dev->write_list.list);
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -365,7 +352,7 @@ int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
cb->fop_type = MEI_FOP_IOCTL;
|
||||
cb->fop_type = MEI_FOP_WRITE;
|
||||
|
||||
if (!list_empty(&dev->amthif_cmd_list.list) ||
|
||||
dev->iamthif_state != MEI_IAMTHIF_IDLE) {
|
||||
|
|
@ -447,23 +434,23 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
|
|||
|
||||
|
||||
/**
|
||||
* mei_amthif_irq_write_completed - processes completed iamthif operation.
|
||||
* mei_amthif_irq_write - write iamthif command in irq thread context.
|
||||
*
|
||||
* @dev: the device structure.
|
||||
* @slots: free slots.
|
||||
* @cb_pos: callback block.
|
||||
* @cl: private data of the file object.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise, error.
|
||||
*/
|
||||
int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list)
|
||||
int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
struct mei_msg_hdr mei_hdr;
|
||||
size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index;
|
||||
u32 msg_slots = mei_data2slots(len);
|
||||
int slots;
|
||||
int rets;
|
||||
|
||||
rets = mei_cl_flow_ctrl_creds(cl);
|
||||
|
|
@ -480,13 +467,15 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
mei_hdr.reserved = 0;
|
||||
mei_hdr.internal = 0;
|
||||
|
||||
if (*slots >= msg_slots) {
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
|
||||
if (slots >= msg_slots) {
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 1;
|
||||
/* Split the message only if we can write the whole host buffer */
|
||||
} else if (*slots == dev->hbuf_depth) {
|
||||
msg_slots = *slots;
|
||||
len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
|
||||
} else if (slots == dev->hbuf_depth) {
|
||||
msg_slots = slots;
|
||||
len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 0;
|
||||
} else {
|
||||
|
|
@ -496,7 +485,6 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
|
||||
dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr));
|
||||
|
||||
*slots -= msg_slots;
|
||||
rets = mei_write_message(dev, &mei_hdr,
|
||||
dev->iamthif_msg_buf + dev->iamthif_msg_buf_index);
|
||||
if (rets) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
#include <linux/mei_cl_bus.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
#include "hw-me.h"
|
||||
#include "client.h"
|
||||
|
||||
#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver)
|
||||
|
|
@ -145,9 +144,9 @@ static struct device_type mei_cl_device_type = {
|
|||
static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev,
|
||||
uuid_le uuid)
|
||||
{
|
||||
struct mei_cl *cl, *next;
|
||||
struct mei_cl *cl;
|
||||
|
||||
list_for_each_entry_safe(cl, next, &dev->device_list, device_link) {
|
||||
list_for_each_entry(cl, &dev->device_list, device_link) {
|
||||
if (!uuid_le_cmp(uuid, cl->device_uuid))
|
||||
return cl;
|
||||
}
|
||||
|
|
@ -524,6 +523,22 @@ void mei_cl_bus_rx_event(struct mei_cl *cl)
|
|||
schedule_work(&device->event_work);
|
||||
}
|
||||
|
||||
void mei_cl_bus_remove_devices(struct mei_device *dev)
|
||||
{
|
||||
struct mei_cl *cl, *next;
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
list_for_each_entry_safe(cl, next, &dev->device_list, device_link) {
|
||||
if (cl->device)
|
||||
mei_cl_remove_device(cl->device);
|
||||
|
||||
list_del(&cl->device_link);
|
||||
mei_cl_unlink(cl);
|
||||
kfree(cl);
|
||||
}
|
||||
mutex_unlock(&dev->device_lock);
|
||||
}
|
||||
|
||||
int __init mei_cl_bus_init(void)
|
||||
{
|
||||
return bus_register(&mei_cl_bus_type);
|
||||
|
|
|
|||
|
|
@ -29,20 +29,21 @@
|
|||
* mei_me_cl_by_uuid - locate index of me client
|
||||
*
|
||||
* @dev: mei device
|
||||
*
|
||||
* Locking: called under "dev->device_lock" lock
|
||||
*
|
||||
* returns me client index or -ENOENT if not found
|
||||
*/
|
||||
int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid)
|
||||
{
|
||||
int i, res = -ENOENT;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->me_clients_num; ++i)
|
||||
if (uuid_le_cmp(*uuid,
|
||||
dev->me_clients[i].props.protocol_name) == 0) {
|
||||
res = i;
|
||||
break;
|
||||
}
|
||||
dev->me_clients[i].props.protocol_name) == 0)
|
||||
return i;
|
||||
|
||||
return res;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -60,34 +61,76 @@ int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid)
|
|||
int mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->me_clients_num; i++)
|
||||
if (dev->me_clients[i].client_id == client_id)
|
||||
break;
|
||||
if (WARN_ON(dev->me_clients[i].client_id != client_id))
|
||||
return -ENOENT;
|
||||
return i;
|
||||
|
||||
if (i == dev->me_clients_num)
|
||||
return -ENOENT;
|
||||
|
||||
return i;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_cmp_id - tells if the clients are the same
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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 (!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
|
||||
*/
|
||||
void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
|
||||
static inline void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
|
||||
{
|
||||
struct mei_cl_cb *cb;
|
||||
struct mei_cl_cb *next;
|
||||
__mei_io_list_flush(list, cl, false);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(cb, next, &list->list, list) {
|
||||
if (cb->cl && mei_cl_cmp_id(cl, cb->cl))
|
||||
list_del(&cb->list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -196,8 +239,8 @@ int mei_cl_flush_queues(struct mei_cl *cl)
|
|||
|
||||
cl_dbg(dev, cl, "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);
|
||||
|
|
@ -254,10 +297,9 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev)
|
|||
struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
struct mei_cl_cb *cb = NULL;
|
||||
struct mei_cl_cb *next = NULL;
|
||||
struct mei_cl_cb *cb;
|
||||
|
||||
list_for_each_entry_safe(cb, next, &dev->read_list.list, list)
|
||||
list_for_each_entry(cb, &dev->read_list.list, list)
|
||||
if (mei_cl_cmp_id(cl, cb->cl))
|
||||
return cb;
|
||||
return NULL;
|
||||
|
|
@ -375,6 +417,23 @@ void mei_host_client_init(struct work_struct *work)
|
|||
mutex_unlock(&dev->device_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_hbuf_acquire: try to acquire host buffer
|
||||
*
|
||||
* @dev: the device structure
|
||||
* returns true if host buffer was acquired
|
||||
*/
|
||||
bool mei_hbuf_acquire(struct mei_device *dev)
|
||||
{
|
||||
if (!dev->hbuf_is_ready) {
|
||||
dev_dbg(&dev->pdev->dev, "hbuf is not ready\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
dev->hbuf_is_ready = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_disconnect - disconnect host client from the me one
|
||||
|
|
@ -406,8 +465,7 @@ int mei_cl_disconnect(struct mei_cl *cl)
|
|||
return -ENOMEM;
|
||||
|
||||
cb->fop_type = MEI_FOP_CLOSE;
|
||||
if (dev->hbuf_is_ready) {
|
||||
dev->hbuf_is_ready = false;
|
||||
if (mei_hbuf_acquire(dev)) {
|
||||
if (mei_hbm_cl_disconnect_req(dev, cl)) {
|
||||
rets = -ENODEV;
|
||||
cl_err(dev, cl, "failed to disconnect.\n");
|
||||
|
|
@ -461,17 +519,17 @@ free:
|
|||
bool mei_cl_is_other_connecting(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl *pos;
|
||||
struct mei_cl *next;
|
||||
struct mei_cl *ocl; /* the other client */
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return false;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
list_for_each_entry_safe(pos, next, &dev->file_list, link) {
|
||||
if ((pos->state == MEI_FILE_CONNECTING) &&
|
||||
(pos != cl) && cl->me_client_id == pos->me_client_id)
|
||||
list_for_each_entry(ocl, &dev->file_list, link) {
|
||||
if (ocl->state == MEI_FILE_CONNECTING &&
|
||||
ocl != cl &&
|
||||
cl->me_client_id == ocl->me_client_id)
|
||||
return true;
|
||||
|
||||
}
|
||||
|
|
@ -505,11 +563,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
|||
goto out;
|
||||
}
|
||||
|
||||
cb->fop_type = MEI_FOP_IOCTL;
|
||||
|
||||
if (dev->hbuf_is_ready && !mei_cl_is_other_connecting(cl)) {
|
||||
dev->hbuf_is_ready = false;
|
||||
cb->fop_type = MEI_FOP_CONNECT;
|
||||
|
||||
/* run hbuf acquire last so we don't have to undo */
|
||||
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
|
||||
if (mei_hbm_cl_connect_req(dev, cl)) {
|
||||
rets = -ENODEV;
|
||||
goto out;
|
||||
|
|
@ -521,18 +578,19 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
|
|||
}
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
rets = wait_event_timeout(dev->wait_recvd_msg,
|
||||
(cl->state == MEI_FILE_CONNECTED ||
|
||||
cl->state == MEI_FILE_DISCONNECTED),
|
||||
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
|
||||
wait_event_timeout(dev->wait_recvd_msg,
|
||||
(cl->state == MEI_FILE_CONNECTED ||
|
||||
cl->state == MEI_FILE_DISCONNECTED),
|
||||
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (cl->state != MEI_FILE_CONNECTED) {
|
||||
rets = -EFAULT;
|
||||
/* something went really wrong */
|
||||
if (!cl->status)
|
||||
cl->status = -EFAULT;
|
||||
|
||||
mei_io_list_flush(&dev->ctrl_rd_list, cl);
|
||||
mei_io_list_flush(&dev->ctrl_wr_list, cl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rets = cl->status;
|
||||
|
|
@ -554,7 +612,8 @@ out:
|
|||
int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
int i;
|
||||
struct mei_me_client *me_cl;
|
||||
int id;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -EINVAL;
|
||||
|
|
@ -567,19 +626,19 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
|
|||
if (cl->mei_flow_ctrl_creds > 0)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < dev->me_clients_num; i++) {
|
||||
struct mei_me_client *me_cl = &dev->me_clients[i];
|
||||
if (me_cl->client_id == cl->me_client_id) {
|
||||
if (me_cl->mei_flow_ctrl_creds) {
|
||||
if (WARN_ON(me_cl->props.single_recv_buf == 0))
|
||||
return -EINVAL;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
id = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
if (id < 0) {
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
return id;
|
||||
}
|
||||
return -ENOENT;
|
||||
|
||||
me_cl = &dev->me_clients[id];
|
||||
if (me_cl->mei_flow_ctrl_creds) {
|
||||
if (WARN_ON(me_cl->props.single_recv_buf == 0))
|
||||
return -EINVAL;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -595,32 +654,31 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
|
|||
int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
int i;
|
||||
struct mei_me_client *me_cl;
|
||||
int id;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -EINVAL;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
if (!dev->me_clients_num)
|
||||
return -ENOENT;
|
||||
|
||||
for (i = 0; i < dev->me_clients_num; i++) {
|
||||
struct mei_me_client *me_cl = &dev->me_clients[i];
|
||||
if (me_cl->client_id == cl->me_client_id) {
|
||||
if (me_cl->props.single_recv_buf != 0) {
|
||||
if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
dev->me_clients[i].mei_flow_ctrl_creds--;
|
||||
} else {
|
||||
if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
cl->mei_flow_ctrl_creds--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
id = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
if (id < 0) {
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
return id;
|
||||
}
|
||||
return -ENOENT;
|
||||
|
||||
me_cl = &dev->me_clients[id];
|
||||
if (me_cl->props.single_recv_buf != 0) {
|
||||
if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
me_cl->mei_flow_ctrl_creds--;
|
||||
} else {
|
||||
if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
|
||||
return -EINVAL;
|
||||
cl->mei_flow_ctrl_creds--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -652,7 +710,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
|
|||
i = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
if (i < 0) {
|
||||
cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
|
||||
return -ENODEV;
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
cb = mei_io_cb_init(cl, NULL);
|
||||
|
|
@ -666,8 +724,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
|
|||
goto err;
|
||||
|
||||
cb->fop_type = MEI_FOP_READ;
|
||||
if (dev->hbuf_is_ready) {
|
||||
dev->hbuf_is_ready = false;
|
||||
if (mei_hbuf_acquire(dev)) {
|
||||
if (mei_hbm_cl_flow_control_req(dev, cl)) {
|
||||
cl_err(dev, cl, "flow control send failed\n");
|
||||
rets = -ENODEV;
|
||||
|
|
@ -687,27 +744,26 @@ err:
|
|||
}
|
||||
|
||||
/**
|
||||
* mei_cl_irq_write_complete - write a message to device
|
||||
* mei_cl_irq_write - write a message to device
|
||||
* from the interrupt thread context
|
||||
*
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @slots: free slots.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise error.
|
||||
*/
|
||||
int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list)
|
||||
int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_msg_data *buf;
|
||||
struct mei_msg_hdr mei_hdr;
|
||||
size_t len;
|
||||
u32 msg_slots;
|
||||
int slots;
|
||||
int rets;
|
||||
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
|
||||
|
|
@ -724,6 +780,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
len = buf->size - cb->buf_idx;
|
||||
msg_slots = mei_data2slots(len);
|
||||
|
||||
|
|
@ -732,13 +789,13 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
mei_hdr.reserved = 0;
|
||||
mei_hdr.internal = cb->internal;
|
||||
|
||||
if (*slots >= msg_slots) {
|
||||
if (slots >= msg_slots) {
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 1;
|
||||
/* Split the message only if we can write the whole host buffer */
|
||||
} else if (*slots == dev->hbuf_depth) {
|
||||
msg_slots = *slots;
|
||||
len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
|
||||
} else if (slots == dev->hbuf_depth) {
|
||||
msg_slots = slots;
|
||||
len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
|
||||
mei_hdr.length = len;
|
||||
mei_hdr.msg_complete = 0;
|
||||
} else {
|
||||
|
|
@ -749,7 +806,6 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
cl_dbg(dev, cl, "buf: size = %d idx = %lu\n",
|
||||
cb->request_buffer.size, cb->buf_idx);
|
||||
|
||||
*slots -= msg_slots;
|
||||
rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
|
||||
if (rets) {
|
||||
cl->status = rets;
|
||||
|
|
@ -802,21 +858,29 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
|||
|
||||
|
||||
cb->fop_type = MEI_FOP_WRITE;
|
||||
cb->buf_idx = 0;
|
||||
cl->writing_state = MEI_IDLE;
|
||||
|
||||
mei_hdr.host_addr = cl->host_client_id;
|
||||
mei_hdr.me_addr = cl->me_client_id;
|
||||
mei_hdr.reserved = 0;
|
||||
mei_hdr.msg_complete = 0;
|
||||
mei_hdr.internal = cb->internal;
|
||||
|
||||
rets = mei_cl_flow_ctrl_creds(cl);
|
||||
if (rets < 0)
|
||||
goto err;
|
||||
|
||||
/* Host buffer is not ready, we queue the request */
|
||||
if (rets == 0 || !dev->hbuf_is_ready) {
|
||||
cb->buf_idx = 0;
|
||||
/* unseting complete will enqueue the cb for write */
|
||||
mei_hdr.msg_complete = 0;
|
||||
if (rets == 0) {
|
||||
cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
|
||||
rets = buf->size;
|
||||
goto out;
|
||||
}
|
||||
if (!mei_hbuf_acquire(dev)) {
|
||||
cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
|
||||
rets = buf->size;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->hbuf_is_ready = false;
|
||||
|
||||
/* Check for a maximum length */
|
||||
if (buf->size > mei_hbuf_max_len(dev)) {
|
||||
|
|
@ -827,12 +891,6 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
|||
mei_hdr.msg_complete = 1;
|
||||
}
|
||||
|
||||
mei_hdr.host_addr = cl->host_client_id;
|
||||
mei_hdr.me_addr = cl->me_client_id;
|
||||
mei_hdr.reserved = 0;
|
||||
mei_hdr.internal = cb->internal;
|
||||
|
||||
|
||||
rets = mei_write_message(dev, &mei_hdr, buf->data);
|
||||
if (rets)
|
||||
goto err;
|
||||
|
|
@ -840,13 +898,12 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
|||
cl->writing_state = MEI_WRITING;
|
||||
cb->buf_idx = mei_hdr.length;
|
||||
|
||||
rets = buf->size;
|
||||
out:
|
||||
if (mei_hdr.msg_complete) {
|
||||
if (mei_cl_flow_ctrl_reduce(cl)) {
|
||||
rets = -ENODEV;
|
||||
rets = mei_cl_flow_ctrl_reduce(cl);
|
||||
if (rets < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
list_add_tail(&cb->list, &dev->write_waiting_list.list);
|
||||
} else {
|
||||
list_add_tail(&cb->list, &dev->write_list.list);
|
||||
|
|
@ -856,15 +913,18 @@ out:
|
|||
if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
if (wait_event_interruptible(cl->tx_wait,
|
||||
cl->writing_state == MEI_WRITE_COMPLETE)) {
|
||||
if (signal_pending(current))
|
||||
rets = -EINTR;
|
||||
else
|
||||
rets = -ERESTARTSYS;
|
||||
}
|
||||
rets = wait_event_interruptible(cl->tx_wait,
|
||||
cl->writing_state == MEI_WRITE_COMPLETE);
|
||||
mutex_lock(&dev->device_lock);
|
||||
/* wait_event_interruptible returns -ERESTARTSYS */
|
||||
if (rets) {
|
||||
if (signal_pending(current))
|
||||
rets = -EINTR;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
rets = buf->size;
|
||||
err:
|
||||
return rets;
|
||||
}
|
||||
|
|
@ -905,9 +965,9 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
|
|||
|
||||
void mei_cl_all_disconnect(struct mei_device *dev)
|
||||
{
|
||||
struct mei_cl *cl, *next;
|
||||
struct mei_cl *cl;
|
||||
|
||||
list_for_each_entry_safe(cl, next, &dev->file_list, link) {
|
||||
list_for_each_entry(cl, &dev->file_list, link) {
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->mei_flow_ctrl_creds = 0;
|
||||
cl->timer_count = 0;
|
||||
|
|
@ -922,8 +982,8 @@ void mei_cl_all_disconnect(struct mei_device *dev)
|
|||
*/
|
||||
void mei_cl_all_wakeup(struct mei_device *dev)
|
||||
{
|
||||
struct mei_cl *cl, *next;
|
||||
list_for_each_entry_safe(cl, next, &dev->file_list, link) {
|
||||
struct mei_cl *cl;
|
||||
list_for_each_entry(cl, &dev->file_list, link) {
|
||||
if (waitqueue_active(&cl->rx_wait)) {
|
||||
cl_dbg(dev, cl, "Waking up reading client!\n");
|
||||
wake_up_interruptible(&cl->rx_wait);
|
||||
|
|
@ -942,20 +1002,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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
@ -86,15 +68,15 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl);
|
|||
*/
|
||||
static inline bool mei_cl_is_connected(struct mei_cl *cl)
|
||||
{
|
||||
return (cl->dev &&
|
||||
return cl->dev &&
|
||||
cl->dev->dev_state == MEI_DEV_ENABLED &&
|
||||
cl->state == MEI_FILE_CONNECTED);
|
||||
cl->state == MEI_FILE_CONNECTED;
|
||||
}
|
||||
static inline bool mei_cl_is_transitioning(struct mei_cl *cl)
|
||||
{
|
||||
return (MEI_FILE_INITIALIZING == cl->state ||
|
||||
return MEI_FILE_INITIALIZING == cl->state ||
|
||||
MEI_FILE_DISCONNECTED == cl->state ||
|
||||
MEI_FILE_DISCONNECTING == cl->state);
|
||||
MEI_FILE_DISCONNECTING == cl->state;
|
||||
}
|
||||
|
||||
bool mei_cl_is_other_connecting(struct mei_cl *cl);
|
||||
|
|
@ -102,8 +84,8 @@ int mei_cl_disconnect(struct mei_cl *cl);
|
|||
int mei_cl_connect(struct mei_cl *cl, struct file *file);
|
||||
int mei_cl_read_start(struct mei_cl *cl, size_t length);
|
||||
int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking);
|
||||
int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list);
|
||||
int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list);
|
||||
|
||||
void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb);
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,54 @@ static const struct file_operations mei_dbgfs_fops_meclients = {
|
|||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct mei_device *dev = fp->private_data;
|
||||
struct mei_cl *cl;
|
||||
const size_t bufsz = 1024;
|
||||
char *buf;
|
||||
int i = 0;
|
||||
int pos = 0;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
buf = kzalloc(bufsz, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
" |me|host|state|rd|wr|\n");
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
/* if the driver is not enabled the list won't b consitent */
|
||||
if (dev->dev_state != MEI_DEV_ENABLED)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(cl, &dev->file_list, link) {
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"%2d|%2d|%4d|%5d|%2d|%2d|\n",
|
||||
i, cl->me_client_id, cl->host_client_id, cl->state,
|
||||
cl->reading_state, cl->writing_state);
|
||||
i++;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&dev->device_lock);
|
||||
ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos);
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations mei_dbgfs_fops_active = {
|
||||
.open = simple_open,
|
||||
.read = mei_dbgfs_read_active,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
|
|
@ -128,6 +176,12 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name)
|
|||
dev_err(&dev->pdev->dev, "meclients: registration failed\n");
|
||||
goto err;
|
||||
}
|
||||
f = debugfs_create_file("active", S_IRUSR, dir,
|
||||
dev, &mei_dbgfs_fops_active);
|
||||
if (!f) {
|
||||
dev_err(&dev->pdev->dev, "meclients: registration failed\n");
|
||||
goto err;
|
||||
}
|
||||
f = debugfs_create_file("devstate", S_IRUSR, dir,
|
||||
dev, &mei_dbgfs_fops_devstate);
|
||||
if (!f) {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,41 @@
|
|||
|
||||
#include "mei_dev.h"
|
||||
#include "hbm.h"
|
||||
#include "hw-me.h"
|
||||
#include "client.h"
|
||||
|
||||
static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status)
|
||||
{
|
||||
#define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status
|
||||
switch (status) {
|
||||
MEI_CL_CS(SUCCESS);
|
||||
MEI_CL_CS(NOT_FOUND);
|
||||
MEI_CL_CS(ALREADY_STARTED);
|
||||
MEI_CL_CS(OUT_OF_RESOURCES);
|
||||
MEI_CL_CS(MESSAGE_SMALL);
|
||||
default: return "unknown";
|
||||
}
|
||||
#undef MEI_CL_CCS
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_conn_status_to_errno - convert client connect response
|
||||
* status to error code
|
||||
*
|
||||
* @status: client connect response status
|
||||
*
|
||||
* returns corresponding error code
|
||||
*/
|
||||
static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
|
||||
{
|
||||
switch (status) {
|
||||
case MEI_CL_CONN_SUCCESS: return 0;
|
||||
case MEI_CL_CONN_NOT_FOUND: return -ENOTTY;
|
||||
case MEI_CL_CONN_ALREADY_STARTED: return -EBUSY;
|
||||
case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY;
|
||||
case MEI_CL_CONN_MESSAGE_SMALL: return -EINVAL;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_hbm_me_cl_allocate - allocates storage for me clients
|
||||
|
|
@ -99,33 +133,6 @@ bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* is_treat_specially_client - checks if the message belongs
|
||||
* to the file private data.
|
||||
*
|
||||
* @cl: private data of the file object
|
||||
* @rs: connect response bus message
|
||||
*
|
||||
*/
|
||||
static bool is_treat_specially_client(struct mei_cl *cl,
|
||||
struct hbm_client_connect_response *rs)
|
||||
{
|
||||
if (mei_hbm_cl_addr_equal(cl, rs)) {
|
||||
if (!rs->status) {
|
||||
cl->state = MEI_FILE_CONNECTED;
|
||||
cl->status = 0;
|
||||
|
||||
} else {
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->status = -ENODEV;
|
||||
}
|
||||
cl->timer_count = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_hbm_idle - set hbm to idle state
|
||||
*
|
||||
|
|
@ -147,13 +154,13 @@ int mei_hbm_start_wait(struct mei_device *dev)
|
|||
ret = wait_event_interruptible_timeout(dev->wait_recvd_msg,
|
||||
dev->hbm_state == MEI_HBM_IDLE ||
|
||||
dev->hbm_state >= MEI_HBM_STARTED,
|
||||
mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT));
|
||||
mei_secs_to_jiffies(MEI_HBM_TIMEOUT));
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) {
|
||||
dev->hbm_state = MEI_HBM_IDLE;
|
||||
dev_err(&dev->pdev->dev, "waiting for mei start failed\n");
|
||||
return -ETIMEDOUT;
|
||||
return -ETIME;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -283,17 +290,18 @@ static int mei_hbm_prop_req(struct mei_device *dev)
|
|||
}
|
||||
|
||||
/**
|
||||
* mei_hbm_stop_req_prepare - prepare stop request message
|
||||
* mei_hbm_stop_req - send stop request message
|
||||
*
|
||||
* @dev - mei device
|
||||
* @mei_hdr - mei message header
|
||||
* @data - hbm message body buffer
|
||||
* @cl: client info
|
||||
*
|
||||
* This function returns -EIO on write failure
|
||||
*/
|
||||
static void mei_hbm_stop_req_prepare(struct mei_device *dev,
|
||||
struct mei_msg_hdr *mei_hdr, unsigned char *data)
|
||||
static int mei_hbm_stop_req(struct mei_device *dev)
|
||||
{
|
||||
struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
|
||||
struct hbm_host_stop_request *req =
|
||||
(struct hbm_host_stop_request *)data;
|
||||
(struct hbm_host_stop_request *)dev->wr_msg.data;
|
||||
const size_t len = sizeof(struct hbm_host_stop_request);
|
||||
|
||||
mei_hbm_hdr(mei_hdr, len);
|
||||
|
|
@ -301,6 +309,8 @@ static void mei_hbm_stop_req_prepare(struct mei_device *dev,
|
|||
memset(req, 0, len);
|
||||
req->hbm_cmd = HOST_STOP_REQ_CMD;
|
||||
req->reason = DRIVER_STOP_REQUEST;
|
||||
|
||||
return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -319,8 +329,7 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
|
|||
mei_hbm_hdr(mei_hdr, len);
|
||||
mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len);
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n",
|
||||
cl->host_client_id, cl->me_client_id);
|
||||
cl_dbg(dev, cl, "sending flow control\n");
|
||||
|
||||
return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
|
||||
}
|
||||
|
|
@ -330,27 +339,34 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
|
|||
*
|
||||
* @dev: the device structure
|
||||
* @flow: flow control.
|
||||
*
|
||||
* return 0 on success, < 0 otherwise
|
||||
*/
|
||||
static void mei_hbm_add_single_flow_creds(struct mei_device *dev,
|
||||
static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
|
||||
struct hbm_flow_control *flow)
|
||||
{
|
||||
struct mei_me_client *client;
|
||||
int i;
|
||||
struct mei_me_client *me_cl;
|
||||
int id;
|
||||
|
||||
for (i = 0; i < dev->me_clients_num; i++) {
|
||||
client = &dev->me_clients[i];
|
||||
if (client && flow->me_addr == client->client_id) {
|
||||
if (client->props.single_recv_buf) {
|
||||
client->mei_flow_ctrl_creds++;
|
||||
dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
|
||||
flow->me_addr);
|
||||
dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
|
||||
client->mei_flow_ctrl_creds);
|
||||
} else {
|
||||
BUG(); /* error in flow control */
|
||||
}
|
||||
}
|
||||
id = mei_me_cl_by_id(dev, flow->me_addr);
|
||||
if (id < 0) {
|
||||
dev_err(&dev->pdev->dev, "no such me client %d\n",
|
||||
flow->me_addr);
|
||||
return id;
|
||||
}
|
||||
|
||||
me_cl = &dev->me_clients[id];
|
||||
if (me_cl->props.single_recv_buf) {
|
||||
me_cl->mei_flow_ctrl_creds++;
|
||||
dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
|
||||
flow->me_addr);
|
||||
dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
|
||||
me_cl->mei_flow_ctrl_creds);
|
||||
} else {
|
||||
BUG(); /* error in flow control */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -362,8 +378,7 @@ static void mei_hbm_add_single_flow_creds(struct mei_device *dev,
|
|||
static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
|
||||
struct hbm_flow_control *flow_control)
|
||||
{
|
||||
struct mei_cl *cl = NULL;
|
||||
struct mei_cl *next = NULL;
|
||||
struct mei_cl *cl;
|
||||
|
||||
if (!flow_control->host_addr) {
|
||||
/* single receive buffer */
|
||||
|
|
@ -372,7 +387,7 @@ static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
|
|||
}
|
||||
|
||||
/* normal connection */
|
||||
list_for_each_entry_safe(cl, next, &dev->file_list, link) {
|
||||
list_for_each_entry(cl, &dev->file_list, link) {
|
||||
if (mei_hbm_cl_addr_equal(cl, flow_control)) {
|
||||
cl->mei_flow_ctrl_creds++;
|
||||
dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
|
||||
|
|
@ -404,6 +419,25 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
|
|||
return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
|
||||
*
|
||||
* @dev: the device structure
|
||||
* @cl: a client to disconnect from
|
||||
*
|
||||
* This function returns -EIO on write failure
|
||||
*/
|
||||
int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl)
|
||||
{
|
||||
struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
|
||||
const size_t len = sizeof(struct hbm_client_connect_response);
|
||||
|
||||
mei_hbm_hdr(mei_hdr, len);
|
||||
mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, dev->wr_msg.data, len);
|
||||
|
||||
return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_hbm_cl_disconnect_res - disconnect response from ME
|
||||
*
|
||||
|
|
@ -414,29 +448,23 @@ static void mei_hbm_cl_disconnect_res(struct mei_device *dev,
|
|||
struct hbm_client_connect_response *rs)
|
||||
{
|
||||
struct mei_cl *cl;
|
||||
struct mei_cl_cb *pos = NULL, *next = NULL;
|
||||
struct mei_cl_cb *cb, *next;
|
||||
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
"disconnect_response:\n"
|
||||
"ME Client = %d\n"
|
||||
"Host Client = %d\n"
|
||||
"Status = %d\n",
|
||||
rs->me_addr,
|
||||
rs->host_addr,
|
||||
rs->status);
|
||||
dev_dbg(&dev->pdev->dev, "hbm: disconnect response cl:host=%02d me=%02d status=%d\n",
|
||||
rs->me_addr, rs->host_addr, rs->status);
|
||||
|
||||
list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
|
||||
cl = pos->cl;
|
||||
list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
|
||||
cl = cb->cl;
|
||||
|
||||
if (!cl) {
|
||||
list_del(&pos->list);
|
||||
/* this should not happen */
|
||||
if (WARN_ON(!cl)) {
|
||||
list_del(&cb->list);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
|
||||
if (mei_hbm_cl_addr_equal(cl, rs)) {
|
||||
list_del(&pos->list);
|
||||
if (!rs->status)
|
||||
list_del(&cb->list);
|
||||
if (rs->status == MEI_CL_DISCONN_SUCCESS)
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
|
||||
cl->status = 0;
|
||||
|
|
@ -476,46 +504,41 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev,
|
|||
{
|
||||
|
||||
struct mei_cl *cl;
|
||||
struct mei_cl_cb *pos = NULL, *next = NULL;
|
||||
struct mei_cl_cb *cb, *next;
|
||||
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
"connect_response:\n"
|
||||
"ME Client = %d\n"
|
||||
"Host Client = %d\n"
|
||||
"Status = %d\n",
|
||||
rs->me_addr,
|
||||
rs->host_addr,
|
||||
rs->status);
|
||||
dev_dbg(&dev->pdev->dev, "hbm: connect response cl:host=%02d me=%02d status=%s\n",
|
||||
rs->me_addr, rs->host_addr,
|
||||
mei_cl_conn_status_str(rs->status));
|
||||
|
||||
/* if WD or iamthif client treat specially */
|
||||
cl = NULL;
|
||||
|
||||
if (is_treat_specially_client(&dev->wd_cl, rs)) {
|
||||
dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
|
||||
mei_watchdog_register(dev);
|
||||
list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_treat_specially_client(&dev->iamthif_cl, rs)) {
|
||||
dev->iamthif_state = MEI_IAMTHIF_IDLE;
|
||||
return;
|
||||
}
|
||||
list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
|
||||
|
||||
cl = pos->cl;
|
||||
if (!cl) {
|
||||
list_del(&pos->list);
|
||||
return;
|
||||
cl = cb->cl;
|
||||
/* this should not happen */
|
||||
if (WARN_ON(!cl)) {
|
||||
list_del_init(&cb->list);
|
||||
continue;
|
||||
}
|
||||
if (pos->fop_type == MEI_FOP_IOCTL) {
|
||||
if (is_treat_specially_client(cl, rs)) {
|
||||
list_del(&pos->list);
|
||||
cl->status = 0;
|
||||
cl->timer_count = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cb->fop_type != MEI_FOP_CONNECT)
|
||||
continue;
|
||||
|
||||
if (mei_hbm_cl_addr_equal(cl, rs)) {
|
||||
list_del(&cb->list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cl)
|
||||
return;
|
||||
|
||||
cl->timer_count = 0;
|
||||
if (rs->status == MEI_CL_CONN_SUCCESS)
|
||||
cl->state = MEI_FILE_CONNECTED;
|
||||
else
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->status = mei_cl_conn_status_to_errno(rs->status);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -525,32 +548,34 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev,
|
|||
*
|
||||
* @dev: the device structure.
|
||||
* @disconnect_req: disconnect request bus message from the me
|
||||
*
|
||||
* returns -ENOMEM on allocation failure
|
||||
*/
|
||||
static void mei_hbm_fw_disconnect_req(struct mei_device *dev,
|
||||
static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
|
||||
struct hbm_client_connect_request *disconnect_req)
|
||||
{
|
||||
struct mei_cl *cl, *next;
|
||||
const size_t len = sizeof(struct hbm_client_connect_response);
|
||||
struct mei_cl *cl;
|
||||
struct mei_cl_cb *cb;
|
||||
|
||||
list_for_each_entry_safe(cl, next, &dev->file_list, link) {
|
||||
list_for_each_entry(cl, &dev->file_list, link) {
|
||||
if (mei_hbm_cl_addr_equal(cl, disconnect_req)) {
|
||||
dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
|
||||
disconnect_req->host_addr,
|
||||
disconnect_req->me_addr);
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->timer_count = 0;
|
||||
if (cl == &dev->wd_cl)
|
||||
dev->wd_pending = false;
|
||||
else if (cl == &dev->iamthif_cl)
|
||||
dev->iamthif_timer = 0;
|
||||
|
||||
/* prepare disconnect response */
|
||||
mei_hbm_hdr(&dev->wr_ext_msg.hdr, len);
|
||||
mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD,
|
||||
dev->wr_ext_msg.data, len);
|
||||
cb = mei_io_cb_init(cl, NULL);
|
||||
if (!cb)
|
||||
return -ENOMEM;
|
||||
cb->fop_type = MEI_FOP_DISCONNECT_RSP;
|
||||
cl_dbg(dev, cl, "add disconnect response as first\n");
|
||||
list_add(&cb->list, &dev->ctrl_wr_list.list);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -629,10 +654,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
|||
dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n");
|
||||
|
||||
dev->hbm_state = MEI_HBM_STOPPED;
|
||||
mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr,
|
||||
dev->wr_msg.data);
|
||||
if (mei_write_message(dev, &dev->wr_msg.hdr,
|
||||
dev->wr_msg.data)) {
|
||||
if (mei_hbm_stop_req(dev)) {
|
||||
dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
|
@ -778,10 +800,11 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
|||
|
||||
case ME_STOP_REQ_CMD:
|
||||
dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n");
|
||||
|
||||
dev->hbm_state = MEI_HBM_STOPPED;
|
||||
mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr,
|
||||
dev->wr_ext_msg.data);
|
||||
if (mei_hbm_stop_req(dev)) {
|
||||
dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ int mei_hbm_start_req(struct mei_device *dev);
|
|||
int mei_hbm_start_wait(struct mei_device *dev);
|
||||
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl);
|
||||
int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl);
|
||||
int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl);
|
||||
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl);
|
||||
bool mei_hbm_version_is_supported(struct mei_device *dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@
|
|||
#include <linux/interrupt.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
#include "hw-me.h"
|
||||
|
||||
#include "hbm.h"
|
||||
|
||||
#include "hw-me.h"
|
||||
#include "hw-me-regs.h"
|
||||
|
||||
/**
|
||||
* mei_me_reg_read - Reads 32bit data from the mei device
|
||||
|
|
@ -240,11 +240,11 @@ static int mei_me_hw_ready_wait(struct mei_device *dev)
|
|||
mutex_unlock(&dev->device_lock);
|
||||
err = wait_event_interruptible_timeout(dev->wait_hw_ready,
|
||||
dev->recvd_hw_ready,
|
||||
mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT));
|
||||
mei_secs_to_jiffies(MEI_HW_READY_TIMEOUT));
|
||||
mutex_lock(&dev->device_lock);
|
||||
if (!err && !dev->recvd_hw_ready) {
|
||||
if (!err)
|
||||
err = -ETIMEDOUT;
|
||||
err = -ETIME;
|
||||
dev_err(&dev->pdev->dev,
|
||||
"wait hw ready failed. status = %d\n", err);
|
||||
return err;
|
||||
|
|
@ -303,7 +303,7 @@ static bool mei_me_hbuf_is_empty(struct mei_device *dev)
|
|||
*
|
||||
* @dev: the device structure
|
||||
*
|
||||
* returns -1(ESLOTS_OVERFLOW) if overflow, otherwise empty slots count
|
||||
* returns -EOVERFLOW if overflow, otherwise empty slots count
|
||||
*/
|
||||
static int mei_me_hbuf_empty_slots(struct mei_device *dev)
|
||||
{
|
||||
|
|
@ -326,7 +326,7 @@ static size_t mei_me_hbuf_max_len(const struct mei_device *dev)
|
|||
|
||||
|
||||
/**
|
||||
* mei_write_message - writes a message to mei device.
|
||||
* mei_me_write_message - writes a message to mei device.
|
||||
*
|
||||
* @dev: the device structure
|
||||
* @header: mei HECI header of message
|
||||
|
|
@ -354,7 +354,7 @@ static int mei_me_write_message(struct mei_device *dev,
|
|||
|
||||
dw_cnt = mei_data2slots(length);
|
||||
if (empty_slots < 0 || dw_cnt > empty_slots)
|
||||
return -EIO;
|
||||
return -EMSGSIZE;
|
||||
|
||||
mei_me_reg_write(hw, H_CB_WW, *((u32 *) header));
|
||||
|
||||
|
|
@ -381,7 +381,7 @@ static int mei_me_write_message(struct mei_device *dev,
|
|||
*
|
||||
* @dev: the device structure
|
||||
*
|
||||
* returns -1(ESLOTS_OVERFLOW) if overflow, otherwise filled slots count
|
||||
* returns -EOVERFLOW if overflow, otherwise filled slots count
|
||||
*/
|
||||
static int mei_me_count_full_read_slots(struct mei_device *dev)
|
||||
{
|
||||
|
|
@ -505,17 +505,25 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
|||
/* check slots available for reading */
|
||||
slots = mei_count_full_read_slots(dev);
|
||||
while (slots > 0) {
|
||||
/* we have urgent data to send so break the read */
|
||||
if (dev->wr_ext_msg.hdr.length)
|
||||
break;
|
||||
dev_dbg(&dev->pdev->dev, "slots to read = %08x\n", slots);
|
||||
rets = mei_irq_read_handler(dev, &complete_list, &slots);
|
||||
/* There is a race between ME write and interrupt delivery:
|
||||
* Not all data is always available immediately after the
|
||||
* interrupt, so try to read again on the next interrupt.
|
||||
*/
|
||||
if (rets == -ENODATA)
|
||||
break;
|
||||
|
||||
if (rets && dev->dev_state != MEI_DEV_RESETTING) {
|
||||
dev_err(&dev->pdev->dev, "mei_irq_read_handler ret = %d.\n",
|
||||
rets);
|
||||
schedule_work(&dev->reset_work);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
|
||||
|
||||
rets = mei_irq_write_handler(dev, &complete_list);
|
||||
|
||||
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
|
||||
|
|
|
|||
294
drivers/misc/mei/hw-txe-regs.h
Normal file
294
drivers/misc/mei/hw-txe-regs.h
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
/******************************************************************************
|
||||
* Intel Management Engine Interface (Intel MEI) Linux driver
|
||||
* Intel MEI Interface Header
|
||||
*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called COPYING
|
||||
*
|
||||
* Contact Information:
|
||||
* Intel Corporation.
|
||||
* linux-mei@linux.intel.com
|
||||
* http://www.intel.com
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
#ifndef _MEI_HW_TXE_REGS_H_
|
||||
#define _MEI_HW_TXE_REGS_H_
|
||||
|
||||
#include "hw.h"
|
||||
|
||||
#define SEC_ALIVENESS_TIMER_TIMEOUT (5 * MSEC_PER_SEC)
|
||||
#define SEC_ALIVENESS_WAIT_TIMEOUT (1 * MSEC_PER_SEC)
|
||||
#define SEC_RESET_WAIT_TIMEOUT (1 * MSEC_PER_SEC)
|
||||
#define SEC_READY_WAIT_TIMEOUT (5 * MSEC_PER_SEC)
|
||||
#define START_MESSAGE_RESPONSE_WAIT_TIMEOUT (5 * MSEC_PER_SEC)
|
||||
#define RESET_CANCEL_WAIT_TIMEOUT (1 * MSEC_PER_SEC)
|
||||
|
||||
enum {
|
||||
SEC_BAR,
|
||||
BRIDGE_BAR,
|
||||
|
||||
NUM_OF_MEM_BARS
|
||||
};
|
||||
|
||||
/* SeC FW Status Register
|
||||
*
|
||||
* FW uses this register in order to report its status to host.
|
||||
* This register resides in PCI-E config space.
|
||||
*/
|
||||
#define PCI_CFG_TXE_FW_STS0 0x40
|
||||
# define PCI_CFG_TXE_FW_STS0_WRK_ST_MSK 0x0000000F
|
||||
# define PCI_CFG_TXE_FW_STS0_OP_ST_MSK 0x000001C0
|
||||
# define PCI_CFG_TXE_FW_STS0_FW_INIT_CMPLT 0x00000200
|
||||
# define PCI_CFG_TXE_FW_STS0_ERR_CODE_MSK 0x0000F000
|
||||
# define PCI_CFG_TXE_FW_STS0_OP_MODE_MSK 0x000F0000
|
||||
# define PCI_CFG_TXE_FW_STS0_RST_CNT_MSK 0x00F00000
|
||||
|
||||
|
||||
#define IPC_BASE_ADDR 0x80400 /* SeC IPC Base Address */
|
||||
|
||||
/* IPC Input Doorbell Register */
|
||||
#define SEC_IPC_INPUT_DOORBELL_REG (0x0000 + IPC_BASE_ADDR)
|
||||
|
||||
/* IPC Input Status Register
|
||||
* This register indicates whether or not processing of
|
||||
* the most recent command has been completed by the SEC
|
||||
* New commands and payloads should not be written by the Host
|
||||
* until this indicates that the previous command has been processed.
|
||||
*/
|
||||
#define SEC_IPC_INPUT_STATUS_REG (0x0008 + IPC_BASE_ADDR)
|
||||
# define SEC_IPC_INPUT_STATUS_RDY BIT(0)
|
||||
|
||||
/* IPC Host Interrupt Status Register */
|
||||
#define SEC_IPC_HOST_INT_STATUS_REG (0x0010 + IPC_BASE_ADDR)
|
||||
#define SEC_IPC_HOST_INT_STATUS_OUT_DB BIT(0)
|
||||
#define SEC_IPC_HOST_INT_STATUS_IN_RDY BIT(1)
|
||||
#define SEC_IPC_HOST_INT_STATUS_HDCP_M0_RCVD BIT(5)
|
||||
#define SEC_IPC_HOST_INT_STATUS_ILL_MEM_ACCESS BIT(17)
|
||||
#define SEC_IPC_HOST_INT_STATUS_AES_HKEY_ERR BIT(18)
|
||||
#define SEC_IPC_HOST_INT_STATUS_DES_HKEY_ERR BIT(19)
|
||||
#define SEC_IPC_HOST_INT_STATUS_TMRMTB_OVERFLOW BIT(21)
|
||||
|
||||
/* Convenient mask for pending interrupts */
|
||||
#define SEC_IPC_HOST_INT_STATUS_PENDING \
|
||||
(SEC_IPC_HOST_INT_STATUS_OUT_DB| \
|
||||
SEC_IPC_HOST_INT_STATUS_IN_RDY)
|
||||
|
||||
/* IPC Host Interrupt Mask Register */
|
||||
#define SEC_IPC_HOST_INT_MASK_REG (0x0014 + IPC_BASE_ADDR)
|
||||
|
||||
# define SEC_IPC_HOST_INT_MASK_OUT_DB BIT(0) /* Output Doorbell Int Mask */
|
||||
# define SEC_IPC_HOST_INT_MASK_IN_RDY BIT(1) /* Input Ready Int Mask */
|
||||
|
||||
/* IPC Input Payload RAM */
|
||||
#define SEC_IPC_INPUT_PAYLOAD_REG (0x0100 + IPC_BASE_ADDR)
|
||||
/* IPC Shared Payload RAM */
|
||||
#define IPC_SHARED_PAYLOAD_REG (0x0200 + IPC_BASE_ADDR)
|
||||
|
||||
/* SeC Address Translation Table Entry 2 - Ctrl
|
||||
*
|
||||
* This register resides also in SeC's PCI-E Memory space.
|
||||
*/
|
||||
#define SATT2_CTRL_REG 0x1040
|
||||
# define SATT2_CTRL_VALID_MSK BIT(0)
|
||||
# define SATT2_CTRL_BR_BASE_ADDR_REG_SHIFT 8
|
||||
# define SATT2_CTRL_BRIDGE_HOST_EN_MSK BIT(12)
|
||||
|
||||
/* SATT Table Entry 2 SAP Base Address Register */
|
||||
#define SATT2_SAP_BA_REG 0x1044
|
||||
/* SATT Table Entry 2 SAP Size Register. */
|
||||
#define SATT2_SAP_SIZE_REG 0x1048
|
||||
/* SATT Table Entry 2 SAP Bridge Address - LSB Register */
|
||||
#define SATT2_BRG_BA_LSB_REG 0x104C
|
||||
|
||||
/* Host High-level Interrupt Status Register */
|
||||
#define HHISR_REG 0x2020
|
||||
/* Host High-level Interrupt Enable Register
|
||||
*
|
||||
* Resides in PCI memory space. This is the top hierarchy for
|
||||
* interrupts from SeC to host, aggregating both interrupts that
|
||||
* arrive through HICR registers as well as interrupts
|
||||
* that arrive via IPC.
|
||||
*/
|
||||
#define HHIER_REG 0x2024
|
||||
#define IPC_HHIER_SEC BIT(0)
|
||||
#define IPC_HHIER_BRIDGE BIT(1)
|
||||
#define IPC_HHIER_MSK (IPC_HHIER_SEC | IPC_HHIER_BRIDGE)
|
||||
|
||||
/* Host High-level Interrupt Mask Register.
|
||||
*
|
||||
* Resides in PCI memory space.
|
||||
* This is the top hierarchy for masking interrupts from SeC to host.
|
||||
*/
|
||||
#define HHIMR_REG 0x2028
|
||||
#define IPC_HHIMR_SEC BIT(0)
|
||||
#define IPC_HHIMR_BRIDGE BIT(1)
|
||||
|
||||
/* Host High-level IRQ Status Register */
|
||||
#define HHIRQSR_REG 0x202C
|
||||
|
||||
/* Host Interrupt Cause Register 0 - SeC IPC Readiness
|
||||
*
|
||||
* This register is both an ICR to Host from PCI Memory Space
|
||||
* and it is also exposed in the SeC memory space.
|
||||
* This register is used by SeC's IPC driver in order
|
||||
* to synchronize with host about IPC interface state.
|
||||
*/
|
||||
#define HICR_SEC_IPC_READINESS_REG 0x2040
|
||||
#define HICR_SEC_IPC_READINESS_HOST_RDY BIT(0)
|
||||
#define HICR_SEC_IPC_READINESS_SEC_RDY BIT(1)
|
||||
#define HICR_SEC_IPC_READINESS_SYS_RDY \
|
||||
(HICR_SEC_IPC_READINESS_HOST_RDY | \
|
||||
HICR_SEC_IPC_READINESS_SEC_RDY)
|
||||
#define HICR_SEC_IPC_READINESS_RDY_CLR BIT(2)
|
||||
|
||||
/* Host Interrupt Cause Register 1 - Aliveness Response */
|
||||
/* This register is both an ICR to Host from PCI Memory Space
|
||||
* and it is also exposed in the SeC memory space.
|
||||
* The register may be used by SeC to ACK a host request for aliveness.
|
||||
*/
|
||||
#define HICR_HOST_ALIVENESS_RESP_REG 0x2044
|
||||
#define HICR_HOST_ALIVENESS_RESP_ACK BIT(0)
|
||||
|
||||
/* Host Interrupt Cause Register 2 - SeC IPC Output Doorbell */
|
||||
#define HICR_SEC_IPC_OUTPUT_DOORBELL_REG 0x2048
|
||||
|
||||
/* Host Interrupt Status Register.
|
||||
*
|
||||
* Resides in PCI memory space.
|
||||
* This is the main register involved in generating interrupts
|
||||
* from SeC to host via HICRs.
|
||||
* The interrupt generation rules are as follows:
|
||||
* An interrupt will be generated whenever for any i,
|
||||
* there is a transition from a state where at least one of
|
||||
* the following conditions did not hold, to a state where
|
||||
* ALL the following conditions hold:
|
||||
* A) HISR.INT[i]_STS == 1.
|
||||
* B) HIER.INT[i]_EN == 1.
|
||||
*/
|
||||
#define HISR_REG 0x2060
|
||||
#define HISR_INT_0_STS BIT(0)
|
||||
#define HISR_INT_1_STS BIT(1)
|
||||
#define HISR_INT_2_STS BIT(2)
|
||||
#define HISR_INT_3_STS BIT(3)
|
||||
#define HISR_INT_4_STS BIT(4)
|
||||
#define HISR_INT_5_STS BIT(5)
|
||||
#define HISR_INT_6_STS BIT(6)
|
||||
#define HISR_INT_7_STS BIT(7)
|
||||
#define HISR_INT_STS_MSK \
|
||||
(HISR_INT_0_STS | HISR_INT_1_STS | HISR_INT_2_STS)
|
||||
|
||||
/* Host Interrupt Enable Register. Resides in PCI memory space. */
|
||||
#define HIER_REG 0x2064
|
||||
#define HIER_INT_0_EN BIT(0)
|
||||
#define HIER_INT_1_EN BIT(1)
|
||||
#define HIER_INT_2_EN BIT(2)
|
||||
#define HIER_INT_3_EN BIT(3)
|
||||
#define HIER_INT_4_EN BIT(4)
|
||||
#define HIER_INT_5_EN BIT(5)
|
||||
#define HIER_INT_6_EN BIT(6)
|
||||
#define HIER_INT_7_EN BIT(7)
|
||||
|
||||
#define HIER_INT_EN_MSK \
|
||||
(HIER_INT_0_EN | HIER_INT_1_EN | HIER_INT_2_EN)
|
||||
|
||||
|
||||
/* SEC Memory Space IPC output payload.
|
||||
*
|
||||
* This register is part of the output payload which SEC provides to host.
|
||||
*/
|
||||
#define BRIDGE_IPC_OUTPUT_PAYLOAD_REG 0x20C0
|
||||
|
||||
/* SeC Interrupt Cause Register - Host Aliveness Request
|
||||
* This register is both an ICR to SeC and it is also exposed
|
||||
* in the host-visible PCI memory space.
|
||||
* The register is used by host to request SeC aliveness.
|
||||
*/
|
||||
#define SICR_HOST_ALIVENESS_REQ_REG 0x214C
|
||||
#define SICR_HOST_ALIVENESS_REQ_REQUESTED BIT(0)
|
||||
|
||||
|
||||
/* SeC Interrupt Cause Register - Host IPC Readiness
|
||||
*
|
||||
* This register is both an ICR to SeC and it is also exposed
|
||||
* in the host-visible PCI memory space.
|
||||
* This register is used by the host's SeC driver uses in order
|
||||
* to synchronize with SeC about IPC interface state.
|
||||
*/
|
||||
#define SICR_HOST_IPC_READINESS_REQ_REG 0x2150
|
||||
|
||||
|
||||
#define SICR_HOST_IPC_READINESS_HOST_RDY BIT(0)
|
||||
#define SICR_HOST_IPC_READINESS_SEC_RDY BIT(1)
|
||||
#define SICR_HOST_IPC_READINESS_SYS_RDY \
|
||||
(SICR_HOST_IPC_READINESS_HOST_RDY | \
|
||||
SICR_HOST_IPC_READINESS_SEC_RDY)
|
||||
#define SICR_HOST_IPC_READINESS_RDY_CLR BIT(2)
|
||||
|
||||
/* SeC Interrupt Cause Register - SeC IPC Output Status
|
||||
*
|
||||
* This register indicates whether or not processing of the most recent
|
||||
* command has been completed by the Host.
|
||||
* New commands and payloads should not be written by SeC until this
|
||||
* register indicates that the previous command has been processed.
|
||||
*/
|
||||
#define SICR_SEC_IPC_OUTPUT_STATUS_REG 0x2154
|
||||
# define SEC_IPC_OUTPUT_STATUS_RDY BIT(0)
|
||||
|
||||
|
||||
|
||||
/* MEI IPC Message payload size 64 bytes */
|
||||
#define PAYLOAD_SIZE 64
|
||||
|
||||
/* MAX size for SATT range 32MB */
|
||||
#define SATT_RANGE_MAX (32 << 20)
|
||||
|
||||
|
||||
#endif /* _MEI_HW_TXE_REGS_H_ */
|
||||
|
||||
1107
drivers/misc/mei/hw-txe.c
Normal file
1107
drivers/misc/mei/hw-txe.c
Normal file
File diff suppressed because it is too large
Load diff
74
drivers/misc/mei/hw-txe.h
Normal file
74
drivers/misc/mei/hw-txe.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
*
|
||||
* Intel Management Engine Interface (Intel MEI) Linux driver
|
||||
* Copyright (c) 2013-2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _MEI_HW_TXE_H_
|
||||
#define _MEI_HW_TXE_H_
|
||||
|
||||
#include <linux/irqreturn.h>
|
||||
|
||||
#include "hw.h"
|
||||
#include "hw-txe-regs.h"
|
||||
|
||||
/* Flatten Hierarchy interrupt cause */
|
||||
#define TXE_INTR_READINESS_BIT 0 /* HISR_INT_0_STS */
|
||||
#define TXE_INTR_READINESS HISR_INT_0_STS
|
||||
#define TXE_INTR_ALIVENESS_BIT 1 /* HISR_INT_1_STS */
|
||||
#define TXE_INTR_ALIVENESS HISR_INT_1_STS
|
||||
#define TXE_INTR_OUT_DB_BIT 2 /* HISR_INT_2_STS */
|
||||
#define TXE_INTR_OUT_DB HISR_INT_2_STS
|
||||
#define TXE_INTR_IN_READY_BIT 8 /* beyond HISR */
|
||||
#define TXE_INTR_IN_READY BIT(8)
|
||||
|
||||
/**
|
||||
* struct mei_txe_hw - txe hardware specifics
|
||||
*
|
||||
* @mem_addr: SeC and BRIDGE bars
|
||||
* @aliveness: aliveness (power gating) state of the hardware
|
||||
* @readiness: readiness state of the hardware
|
||||
* @wait_aliveness: aliveness wait queue
|
||||
* @recvd_aliveness: aliveness interrupt was recived
|
||||
* @intr_cause: translated interrupt cause
|
||||
*/
|
||||
struct mei_txe_hw {
|
||||
void __iomem *mem_addr[NUM_OF_MEM_BARS];
|
||||
u32 aliveness;
|
||||
u32 readiness;
|
||||
u32 slots;
|
||||
|
||||
wait_queue_head_t wait_aliveness;
|
||||
bool recvd_aliveness;
|
||||
|
||||
unsigned long intr_cause;
|
||||
};
|
||||
|
||||
#define to_txe_hw(dev) (struct mei_txe_hw *)((dev)->hw)
|
||||
|
||||
static inline struct mei_device *hw_txe_to_mei(struct mei_txe_hw *hw)
|
||||
{
|
||||
return container_of((void *)hw, struct mei_device, hw);
|
||||
}
|
||||
|
||||
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev);
|
||||
|
||||
irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id);
|
||||
irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id);
|
||||
|
||||
int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req);
|
||||
|
||||
int mei_txe_setup_satt2(struct mei_device *dev, phys_addr_t addr, u32 range);
|
||||
|
||||
|
||||
#endif /* _MEI_HW_TXE_H_ */
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
/*
|
||||
* Timeouts in Seconds
|
||||
*/
|
||||
#define MEI_INTEROP_TIMEOUT 7 /* Timeout on ready message */
|
||||
#define MEI_HW_READY_TIMEOUT 2 /* Timeout on ready message */
|
||||
#define MEI_CONNECT_TIMEOUT 3 /* HPS: at least 2 seconds */
|
||||
|
||||
#define MEI_CL_CONNECT_TIMEOUT 15 /* HPS: Client Connect Timeout */
|
||||
|
|
@ -31,13 +31,13 @@
|
|||
#define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */
|
||||
#define MEI_IAMTHIF_READ_TIMER 10 /* HPS */
|
||||
|
||||
#define MEI_HBM_TIMEOUT 1 /* 1 second */
|
||||
|
||||
/*
|
||||
* MEI Version
|
||||
*/
|
||||
#define HBM_MINOR_VERSION 0
|
||||
#define HBM_MAJOR_VERSION 1
|
||||
#define HBM_TIMEOUT 1 /* 1 second */
|
||||
|
||||
/* Host bus message command opcode */
|
||||
#define MEI_HBM_CMD_OP_MSK 0x7f
|
||||
|
|
@ -89,19 +89,19 @@ enum mei_stop_reason_types {
|
|||
* Client Connect Status
|
||||
* used by hbm_client_connect_response.status
|
||||
*/
|
||||
enum client_connect_status_types {
|
||||
CCS_SUCCESS = 0x00,
|
||||
CCS_NOT_FOUND = 0x01,
|
||||
CCS_ALREADY_STARTED = 0x02,
|
||||
CCS_OUT_OF_RESOURCES = 0x03,
|
||||
CCS_MESSAGE_SMALL = 0x04
|
||||
enum mei_cl_connect_status {
|
||||
MEI_CL_CONN_SUCCESS = 0x00,
|
||||
MEI_CL_CONN_NOT_FOUND = 0x01,
|
||||
MEI_CL_CONN_ALREADY_STARTED = 0x02,
|
||||
MEI_CL_CONN_OUT_OF_RESOURCES = 0x03,
|
||||
MEI_CL_CONN_MESSAGE_SMALL = 0x04
|
||||
};
|
||||
|
||||
/*
|
||||
* Client Disconnect Status
|
||||
*/
|
||||
enum client_disconnect_status_types {
|
||||
CDS_SUCCESS = 0x00
|
||||
enum mei_cl_disconnect_status {
|
||||
MEI_CL_DISCONN_SUCCESS = 0x00
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@ int mei_reset(struct mei_device *dev)
|
|||
mei_cl_unlink(&dev->wd_cl);
|
||||
mei_cl_unlink(&dev->iamthif_cl);
|
||||
mei_amthif_reset_params(dev);
|
||||
memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -126,7 +125,6 @@ int mei_reset(struct mei_device *dev)
|
|||
|
||||
if (ret) {
|
||||
dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret);
|
||||
dev->dev_state = MEI_DEV_DISABLED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +137,6 @@ int mei_reset(struct mei_device *dev)
|
|||
ret = mei_hw_start(dev);
|
||||
if (ret) {
|
||||
dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret);
|
||||
dev->dev_state = MEI_DEV_DISABLED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +146,7 @@ int mei_reset(struct mei_device *dev)
|
|||
ret = mei_hbm_start_req(dev);
|
||||
if (ret) {
|
||||
dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret);
|
||||
dev->dev_state = MEI_DEV_DISABLED;
|
||||
dev->dev_state = MEI_DEV_RESETTING;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -166,6 +163,7 @@ EXPORT_SYMBOL_GPL(mei_reset);
|
|||
*/
|
||||
int mei_start(struct mei_device *dev)
|
||||
{
|
||||
int ret;
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
/* acknowledge interrupt and stop interrupts */
|
||||
|
|
@ -175,10 +173,18 @@ int mei_start(struct mei_device *dev)
|
|||
|
||||
dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");
|
||||
|
||||
dev->dev_state = MEI_DEV_INITIALIZING;
|
||||
dev->reset_count = 0;
|
||||
mei_reset(dev);
|
||||
do {
|
||||
dev->dev_state = MEI_DEV_INITIALIZING;
|
||||
ret = mei_reset(dev);
|
||||
|
||||
if (ret == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) {
|
||||
dev_err(&dev->pdev->dev, "reset failed ret = %d", ret);
|
||||
goto err;
|
||||
}
|
||||
} while (ret);
|
||||
|
||||
/* we cannot start the device w/o hbm start message completed */
|
||||
if (dev->dev_state == MEI_DEV_DISABLED) {
|
||||
dev_err(&dev->pdev->dev, "reset failed");
|
||||
goto err;
|
||||
|
|
@ -238,27 +244,40 @@ int mei_restart(struct mei_device *dev)
|
|||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
if (err || dev->dev_state == MEI_DEV_DISABLED)
|
||||
if (err == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) {
|
||||
dev_err(&dev->pdev->dev, "device disabled = %d\n", err);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* try to start again */
|
||||
if (err)
|
||||
schedule_work(&dev->reset_work);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_restart);
|
||||
|
||||
|
||||
static void mei_reset_work(struct work_struct *work)
|
||||
{
|
||||
struct mei_device *dev =
|
||||
container_of(work, struct mei_device, reset_work);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
mei_reset(dev);
|
||||
ret = mei_reset(dev);
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
if (dev->dev_state == MEI_DEV_DISABLED)
|
||||
dev_err(&dev->pdev->dev, "reset failed");
|
||||
if (dev->dev_state == MEI_DEV_DISABLED) {
|
||||
dev_err(&dev->pdev->dev, "device disabled = %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* retry reset in case of failure */
|
||||
if (ret)
|
||||
schedule_work(&dev->reset_work);
|
||||
}
|
||||
|
||||
void mei_stop(struct mei_device *dev)
|
||||
|
|
@ -269,6 +288,8 @@ void mei_stop(struct mei_device *dev)
|
|||
|
||||
mei_nfc_host_exit(dev);
|
||||
|
||||
mei_cl_bus_remove_devices(dev);
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
mei_wd_stop(dev);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
#include "mei_dev.h"
|
||||
#include "hbm.h"
|
||||
#include "hw-me.h"
|
||||
#include "client.h"
|
||||
|
||||
|
||||
|
|
@ -160,30 +159,64 @@ static int mei_cl_irq_read_msg(struct mei_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_irq_disconnect_rsp - send disconnection response message
|
||||
*
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise, error.
|
||||
*/
|
||||
static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
u32 msg_slots;
|
||||
int slots;
|
||||
int ret;
|
||||
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_response));
|
||||
|
||||
if (slots < msg_slots)
|
||||
return -EMSGSIZE;
|
||||
|
||||
ret = mei_hbm_cl_disconnect_rsp(dev, cl);
|
||||
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->status = 0;
|
||||
list_del(&cb->list);
|
||||
mei_io_cb_free(cb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_irq_close - processes close related operation from
|
||||
* interrupt thread context - send disconnect request
|
||||
*
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @slots: free slots.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise, error.
|
||||
*/
|
||||
static int mei_cl_irq_close(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list)
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
u32 msg_slots;
|
||||
int slots;
|
||||
|
||||
u32 msg_slots =
|
||||
mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
|
||||
if (*slots < msg_slots)
|
||||
if (slots < msg_slots)
|
||||
return -EMSGSIZE;
|
||||
|
||||
*slots -= msg_slots;
|
||||
|
||||
if (mei_hbm_cl_disconnect_req(dev, cl)) {
|
||||
cl->status = 0;
|
||||
cb->buf_idx = 0;
|
||||
|
|
@ -207,27 +240,23 @@ static int mei_cl_irq_close(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
*
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @slots: free slots.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise, error.
|
||||
*/
|
||||
static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list)
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
|
||||
|
||||
u32 msg_slots;
|
||||
int slots;
|
||||
int ret;
|
||||
|
||||
msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
|
||||
if (*slots < msg_slots) {
|
||||
/* return the cancel routine */
|
||||
list_del(&cb->list);
|
||||
if (slots < msg_slots)
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
*slots -= msg_slots;
|
||||
|
||||
ret = mei_hbm_cl_flow_control_req(dev, cl);
|
||||
if (ret) {
|
||||
|
|
@ -244,32 +273,30 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
|
|||
|
||||
|
||||
/**
|
||||
* mei_cl_irq_ioctl - processes client ioctl related operation from the
|
||||
* interrupt thread context - send connection request
|
||||
* mei_cl_irq_connect - send connect request in irq_thread context
|
||||
*
|
||||
* @cl: client
|
||||
* @cb: callback block.
|
||||
* @slots: free slots.
|
||||
* @cmpl_list: complete list.
|
||||
*
|
||||
* returns 0, OK; otherwise, error.
|
||||
*/
|
||||
static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list)
|
||||
static int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
struct mei_device *dev = cl->dev;
|
||||
u32 msg_slots;
|
||||
int slots;
|
||||
int ret;
|
||||
|
||||
u32 msg_slots =
|
||||
mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
|
||||
if (*slots < msg_slots) {
|
||||
/* return the cancel routine */
|
||||
list_del(&cb->list);
|
||||
if (mei_cl_is_other_connecting(cl))
|
||||
return 0;
|
||||
|
||||
if (slots < msg_slots)
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
*slots -= msg_slots;
|
||||
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
|
||||
|
|
@ -323,7 +350,7 @@ int mei_irq_read_handler(struct mei_device *dev,
|
|||
dev_err(&dev->pdev->dev, "less data available than length=%08x.\n",
|
||||
*slots);
|
||||
/* we can't read the message */
|
||||
ret = -ERANGE;
|
||||
ret = -ENODATA;
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
|
@ -409,10 +436,10 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
|
|||
s32 slots;
|
||||
int ret;
|
||||
|
||||
if (!mei_hbuf_is_ready(dev)) {
|
||||
dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
|
||||
|
||||
if (!mei_hbuf_acquire(dev))
|
||||
return 0;
|
||||
}
|
||||
|
||||
slots = mei_hbuf_empty_slots(dev);
|
||||
if (slots <= 0)
|
||||
return -EMSGSIZE;
|
||||
|
|
@ -447,29 +474,16 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
|
|||
|
||||
if (dev->wd_state == MEI_WD_STOPPING) {
|
||||
dev->wd_state = MEI_WD_IDLE;
|
||||
wake_up_interruptible(&dev->wait_stop_wd);
|
||||
wake_up(&dev->wait_stop_wd);
|
||||
}
|
||||
|
||||
if (dev->wr_ext_msg.hdr.length) {
|
||||
mei_write_message(dev, &dev->wr_ext_msg.hdr,
|
||||
dev->wr_ext_msg.data);
|
||||
slots -= mei_data2slots(dev->wr_ext_msg.hdr.length);
|
||||
dev->wr_ext_msg.hdr.length = 0;
|
||||
}
|
||||
if (dev->dev_state == MEI_DEV_ENABLED) {
|
||||
if (mei_cl_is_connected(&dev->wd_cl)) {
|
||||
if (dev->wd_pending &&
|
||||
mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) {
|
||||
if (mei_wd_send(dev))
|
||||
dev_dbg(&dev->pdev->dev, "wd send failed.\n");
|
||||
else if (mei_cl_flow_ctrl_reduce(&dev->wd_cl))
|
||||
return -ENODEV;
|
||||
|
||||
ret = mei_wd_send(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev->wd_pending = false;
|
||||
|
||||
if (dev->wd_state == MEI_WD_RUNNING)
|
||||
slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
|
||||
else
|
||||
slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -484,28 +498,31 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
|
|||
switch (cb->fop_type) {
|
||||
case MEI_FOP_CLOSE:
|
||||
/* send disconnect message */
|
||||
ret = mei_cl_irq_close(cl, cb, &slots, cmpl_list);
|
||||
ret = mei_cl_irq_close(cl, cb, cmpl_list);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
case MEI_FOP_READ:
|
||||
/* send flow control message */
|
||||
ret = mei_cl_irq_read(cl, cb, &slots, cmpl_list);
|
||||
ret = mei_cl_irq_read(cl, cb, cmpl_list);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
case MEI_FOP_IOCTL:
|
||||
case MEI_FOP_CONNECT:
|
||||
/* connect message */
|
||||
if (mei_cl_is_other_connecting(cl))
|
||||
continue;
|
||||
ret = mei_cl_irq_ioctl(cl, cb, &slots, cmpl_list);
|
||||
ret = mei_cl_irq_connect(cl, cb, cmpl_list);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
|
||||
case MEI_FOP_DISCONNECT_RSP:
|
||||
/* send disconnect resp */
|
||||
ret = mei_cl_irq_disconnect_rsp(cl, cb, cmpl_list);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
|
@ -518,11 +535,9 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
|
|||
if (cl == NULL)
|
||||
continue;
|
||||
if (cl == &dev->iamthif_cl)
|
||||
ret = mei_amthif_irq_write_complete(cl, cb,
|
||||
&slots, cmpl_list);
|
||||
ret = mei_amthif_irq_write(cl, cb, cmpl_list);
|
||||
else
|
||||
ret = mei_cl_irq_write_complete(cl, cb,
|
||||
&slots, cmpl_list);
|
||||
ret = mei_cl_irq_write(cl, cb, cmpl_list);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -541,8 +556,7 @@ EXPORT_SYMBOL_GPL(mei_irq_write_handler);
|
|||
void mei_timer(struct work_struct *work)
|
||||
{
|
||||
unsigned long timeout;
|
||||
struct mei_cl *cl_pos = NULL;
|
||||
struct mei_cl *cl_next = NULL;
|
||||
struct mei_cl *cl;
|
||||
struct mei_cl_cb *cb_pos = NULL;
|
||||
struct mei_cl_cb *cb_next = NULL;
|
||||
|
||||
|
|
@ -570,9 +584,9 @@ void mei_timer(struct work_struct *work)
|
|||
goto out;
|
||||
|
||||
/*** connect/disconnect timeouts ***/
|
||||
list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
|
||||
if (cl_pos->timer_count) {
|
||||
if (--cl_pos->timer_count == 0) {
|
||||
list_for_each_entry(cl, &dev->file_list, link) {
|
||||
if (cl->timer_count) {
|
||||
if (--cl->timer_count == 0) {
|
||||
dev_err(&dev->pdev->dev, "timer: connect/disconnect timeout.\n");
|
||||
mei_reset(dev);
|
||||
goto out;
|
||||
|
|
@ -580,6 +594,9 @@ void mei_timer(struct work_struct *work)
|
|||
}
|
||||
}
|
||||
|
||||
if (!mei_cl_is_connected(&dev->iamthif_cl))
|
||||
goto out;
|
||||
|
||||
if (dev->iamthif_stall_timer) {
|
||||
if (--dev->iamthif_stall_timer == 0) {
|
||||
dev_err(&dev->pdev->dev, "timer: amthif hanged.\n");
|
||||
|
|
@ -619,10 +636,10 @@ void mei_timer(struct work_struct *work)
|
|||
list_for_each_entry_safe(cb_pos, cb_next,
|
||||
&dev->amthif_rd_complete_list.list, list) {
|
||||
|
||||
cl_pos = cb_pos->file_object->private_data;
|
||||
cl = cb_pos->file_object->private_data;
|
||||
|
||||
/* Finding the AMTHI entry. */
|
||||
if (cl_pos == &dev->iamthif_cl)
|
||||
if (cl == &dev->iamthif_cl)
|
||||
list_del(&cb_pos->list);
|
||||
}
|
||||
mei_io_cb_free(dev->iamthif_current_cb);
|
||||
|
|
|
|||
|
|
@ -13,9 +13,6 @@
|
|||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
|
|
@ -40,7 +37,6 @@
|
|||
#include <linux/mei.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
#include "hw-me.h"
|
||||
#include "client.h"
|
||||
|
||||
/**
|
||||
|
|
@ -129,17 +125,11 @@ static int mei_release(struct inode *inode, struct file *file)
|
|||
}
|
||||
if (cl->state == MEI_FILE_CONNECTED) {
|
||||
cl->state = MEI_FILE_DISCONNECTING;
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
"disconnecting client host client = %d, "
|
||||
"ME client = %d\n",
|
||||
cl->host_client_id,
|
||||
cl->me_client_id);
|
||||
cl_dbg(dev, cl, "disconnecting\n");
|
||||
rets = mei_cl_disconnect(cl);
|
||||
}
|
||||
mei_cl_flush_queues(cl);
|
||||
dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n",
|
||||
cl->host_client_id,
|
||||
cl->me_client_id);
|
||||
cl_dbg(dev, cl, "removing\n");
|
||||
|
||||
mei_cl_unlink(cl);
|
||||
|
||||
|
|
@ -284,6 +274,7 @@ copy_buffer:
|
|||
length = min_t(size_t, length, cb->buf_idx - *offset);
|
||||
|
||||
if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) {
|
||||
dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n");
|
||||
rets = -EFAULT;
|
||||
goto free;
|
||||
}
|
||||
|
|
@ -340,7 +331,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
|||
|
||||
id = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
if (id < 0) {
|
||||
rets = -ENODEV;
|
||||
rets = -ENOTTY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -404,7 +395,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
|||
|
||||
rets = copy_from_user(write_cb->request_buffer.data, ubuf, length);
|
||||
if (rets) {
|
||||
dev_err(&dev->pdev->dev, "failed to copy data from userland\n");
|
||||
dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n");
|
||||
rets = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -471,7 +462,7 @@ static int mei_ioctl_connect_client(struct file *file,
|
|||
if (i < 0 || dev->me_clients[i].props.fixed_address) {
|
||||
dev_dbg(&dev->pdev->dev, "Cannot connect to FW Client UUID = %pUl\n",
|
||||
&data->in_client_uuid);
|
||||
rets = -ENODEV;
|
||||
rets = -ENOTTY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
|
@ -569,7 +560,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
|
|||
dev_dbg(&dev->pdev->dev, "copy connect data from user\n");
|
||||
if (copy_from_user(connect_data, (char __user *)data,
|
||||
sizeof(struct mei_connect_client_data))) {
|
||||
dev_err(&dev->pdev->dev, "failed to copy data from userland\n");
|
||||
dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n");
|
||||
rets = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
#include <linux/mei_cl_bus.h>
|
||||
|
||||
#include "hw.h"
|
||||
#include "hw-me-regs.h"
|
||||
#include "hbm.h"
|
||||
|
||||
/*
|
||||
|
|
@ -130,16 +129,18 @@ enum mei_wd_states {
|
|||
|
||||
/**
|
||||
* enum mei_cb_file_ops - file operation associated with the callback
|
||||
* @MEI_FOP_READ - read
|
||||
* @MEI_FOP_WRITE - write
|
||||
* @MEI_FOP_IOCTL - ioctl
|
||||
* @MEI_FOP_OPEN - open
|
||||
* @MEI_FOP_CLOSE - close
|
||||
* @MEI_FOP_READ - read
|
||||
* @MEI_FOP_WRITE - write
|
||||
* @MEI_FOP_CONNECT - connect
|
||||
* @MEI_FOP_DISCONNECT_RSP - disconnect response
|
||||
* @MEI_FOP_OPEN - open
|
||||
* @MEI_FOP_CLOSE - close
|
||||
*/
|
||||
enum mei_cb_file_ops {
|
||||
MEI_FOP_READ = 0,
|
||||
MEI_FOP_WRITE,
|
||||
MEI_FOP_IOCTL,
|
||||
MEI_FOP_CONNECT,
|
||||
MEI_FOP_DISCONNECT_RSP,
|
||||
MEI_FOP_OPEN,
|
||||
MEI_FOP_CLOSE
|
||||
};
|
||||
|
|
@ -236,20 +237,20 @@ struct mei_cl {
|
|||
*/
|
||||
struct mei_hw_ops {
|
||||
|
||||
bool (*host_is_ready) (struct mei_device *dev);
|
||||
bool (*host_is_ready)(struct mei_device *dev);
|
||||
|
||||
bool (*hw_is_ready) (struct mei_device *dev);
|
||||
int (*hw_reset) (struct mei_device *dev, bool enable);
|
||||
int (*hw_start) (struct mei_device *dev);
|
||||
void (*hw_config) (struct mei_device *dev);
|
||||
bool (*hw_is_ready)(struct mei_device *dev);
|
||||
int (*hw_reset)(struct mei_device *dev, bool enable);
|
||||
int (*hw_start)(struct mei_device *dev);
|
||||
void (*hw_config)(struct mei_device *dev);
|
||||
|
||||
void (*intr_clear) (struct mei_device *dev);
|
||||
void (*intr_enable) (struct mei_device *dev);
|
||||
void (*intr_disable) (struct mei_device *dev);
|
||||
void (*intr_clear)(struct mei_device *dev);
|
||||
void (*intr_enable)(struct mei_device *dev);
|
||||
void (*intr_disable)(struct mei_device *dev);
|
||||
|
||||
int (*hbuf_free_slots) (struct mei_device *dev);
|
||||
bool (*hbuf_is_ready) (struct mei_device *dev);
|
||||
size_t (*hbuf_max_len) (const struct mei_device *dev);
|
||||
int (*hbuf_free_slots)(struct mei_device *dev);
|
||||
bool (*hbuf_is_ready)(struct mei_device *dev);
|
||||
size_t (*hbuf_max_len)(const struct mei_device *dev);
|
||||
|
||||
int (*write)(struct mei_device *dev,
|
||||
struct mei_msg_hdr *hdr,
|
||||
|
|
@ -258,7 +259,7 @@ struct mei_hw_ops {
|
|||
int (*rdbuf_full_slots)(struct mei_device *dev);
|
||||
|
||||
u32 (*read_hdr)(const struct mei_device *dev);
|
||||
int (*read) (struct mei_device *dev,
|
||||
int (*read)(struct mei_device *dev,
|
||||
unsigned char *buf, unsigned long len);
|
||||
};
|
||||
|
||||
|
|
@ -294,6 +295,7 @@ int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length);
|
|||
int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
void mei_cl_bus_rx_event(struct mei_cl *cl);
|
||||
void mei_cl_bus_remove_devices(struct mei_device *dev);
|
||||
int mei_cl_bus_init(void);
|
||||
void mei_cl_bus_exit(void);
|
||||
|
||||
|
|
@ -339,7 +341,6 @@ struct mei_cl_device {
|
|||
* @hbuf_depth - depth of hardware host/write buffer is slots
|
||||
* @hbuf_is_ready - query if the host host/write buffer is ready
|
||||
* @wr_msg - the buffer for hbm control messages
|
||||
* @wr_ext_msg - the buffer for hbm control responses (set in read cycle)
|
||||
*/
|
||||
struct mei_device {
|
||||
struct pci_dev *pdev; /* pointer to pci device struct */
|
||||
|
|
@ -394,11 +395,6 @@ struct mei_device {
|
|||
unsigned char data[128];
|
||||
} wr_msg;
|
||||
|
||||
struct {
|
||||
struct mei_msg_hdr hdr;
|
||||
unsigned char data[4]; /* All HBM messages are 4 bytes */
|
||||
} wr_ext_msg; /* for control responses */
|
||||
|
||||
struct hbm_version version;
|
||||
|
||||
struct mei_me_client *me_clients; /* Note: memory has to be allocated */
|
||||
|
|
@ -518,8 +514,8 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
|
|||
|
||||
void mei_amthif_run_next_cmd(struct mei_device *dev);
|
||||
|
||||
int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
s32 *slots, struct mei_cl_cb *cmpl_list);
|
||||
int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
|
||||
struct mei_cl_cb *cmpl_list);
|
||||
|
||||
void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb);
|
||||
int mei_amthif_irq_read_msg(struct mei_device *dev,
|
||||
|
|
@ -546,7 +542,7 @@ int mei_wd_host_init(struct mei_device *dev);
|
|||
* once we got connection to the WD Client
|
||||
* @dev - mei device
|
||||
*/
|
||||
void mei_watchdog_register(struct mei_device *dev);
|
||||
int mei_watchdog_register(struct mei_device *dev);
|
||||
/*
|
||||
* mei_watchdog_unregister - Unregistering watchdog interface
|
||||
* @dev - mei device
|
||||
|
|
@ -633,6 +629,8 @@ static inline int mei_count_full_read_slots(struct mei_device *dev)
|
|||
return dev->ops->rdbuf_full_slots(dev);
|
||||
}
|
||||
|
||||
bool mei_hbuf_acquire(struct mei_device *dev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
int mei_dbgfs_register(struct mei_device *dev, const char *name);
|
||||
void mei_dbgfs_deregister(struct mei_device *dev);
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length)
|
|||
if (!wait_event_interruptible_timeout(ndev->send_wq,
|
||||
ndev->recv_req_id == ndev->req_id, HZ)) {
|
||||
dev_err(&dev->pdev->dev, "NFC MEI command timeout\n");
|
||||
err = -ETIMEDOUT;
|
||||
err = -ETIME;
|
||||
} else {
|
||||
ndev->req_id++;
|
||||
}
|
||||
|
|
@ -502,7 +502,7 @@ int mei_nfc_host_init(struct mei_device *dev)
|
|||
i = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid);
|
||||
if (i < 0) {
|
||||
dev_info(&dev->pdev->dev, "nfc: failed to find the client\n");
|
||||
ret = -ENOENT;
|
||||
ret = -ENOTTY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
|
@ -520,7 +520,7 @@ int mei_nfc_host_init(struct mei_device *dev)
|
|||
i = mei_me_cl_by_uuid(dev, &mei_nfc_guid);
|
||||
if (i < 0) {
|
||||
dev_info(&dev->pdev->dev, "nfc: failed to find the client\n");
|
||||
ret = -ENOENT;
|
||||
ret = -ENOTTY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
|
@ -552,13 +552,7 @@ err:
|
|||
void mei_nfc_host_exit(struct mei_device *dev)
|
||||
{
|
||||
struct mei_nfc_dev *ndev = &nfc_dev;
|
||||
|
||||
cancel_work_sync(&ndev->init_work);
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
if (ndev->cl && ndev->cl->device)
|
||||
mei_cl_remove_device(ndev->cl->device);
|
||||
|
||||
mei_nfc_free(ndev);
|
||||
mutex_unlock(&dev->device_lock);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,6 @@
|
|||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
|
|
@ -27,7 +24,6 @@
|
|||
#include <linux/aio.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/sched.h>
|
||||
|
|
@ -40,11 +36,12 @@
|
|||
#include <linux/mei.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
#include "hw-me.h"
|
||||
#include "client.h"
|
||||
#include "hw-me-regs.h"
|
||||
#include "hw-me.h"
|
||||
|
||||
/* mei_pci_tbl - PCI Device ID Table */
|
||||
static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = {
|
||||
static const struct pci_device_id mei_me_pci_tbl[] = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)},
|
||||
|
|
@ -270,7 +267,7 @@ static void mei_me_remove(struct pci_dev *pdev)
|
|||
|
||||
|
||||
}
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mei_me_pci_suspend(struct device *device)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(device);
|
||||
|
|
@ -330,11 +327,12 @@ static int mei_me_pci_resume(struct device *device)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume);
|
||||
#define MEI_ME_PM_OPS (&mei_me_pm_ops)
|
||||
#else
|
||||
#define MEI_ME_PM_OPS NULL
|
||||
#endif /* CONFIG_PM */
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
/*
|
||||
* PCI driver structure
|
||||
*/
|
||||
|
|
|
|||
293
drivers/misc/mei/pci-txe.c
Normal file
293
drivers/misc/mei/pci-txe.c
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
*
|
||||
* Intel Management Engine Interface (Intel MEI) Linux driver
|
||||
* Copyright (c) 2013-2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/mei.h>
|
||||
|
||||
|
||||
#include "mei_dev.h"
|
||||
#include "hw-txe.h"
|
||||
|
||||
static const struct pci_device_id mei_txe_pci_tbl[] = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F18)}, /* Baytrail */
|
||||
{0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl);
|
||||
|
||||
|
||||
static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
|
||||
{
|
||||
int i;
|
||||
for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) {
|
||||
if (hw->mem_addr[i]) {
|
||||
pci_iounmap(pdev, hw->mem_addr[i]);
|
||||
hw->mem_addr[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* mei_probe - Device Initialization Routine
|
||||
*
|
||||
* @pdev: PCI device structure
|
||||
* @ent: entry in mei_txe_pci_tbl
|
||||
*
|
||||
* returns 0 on success, <0 on failure.
|
||||
*/
|
||||
static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_txe_hw *hw;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
/* enable pci dev */
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to enable pci device.\n");
|
||||
goto end;
|
||||
}
|
||||
/* set PCI host mastering */
|
||||
pci_set_master(pdev);
|
||||
/* pci request regions for mei driver */
|
||||
err = pci_request_regions(pdev, KBUILD_MODNAME);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to get pci regions.\n");
|
||||
goto disable_device;
|
||||
}
|
||||
|
||||
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
|
||||
if (err) {
|
||||
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "No suitable DMA available.\n");
|
||||
goto release_regions;
|
||||
}
|
||||
}
|
||||
|
||||
/* allocates and initializes the mei dev structure */
|
||||
dev = mei_txe_dev_init(pdev);
|
||||
if (!dev) {
|
||||
err = -ENOMEM;
|
||||
goto release_regions;
|
||||
}
|
||||
hw = to_txe_hw(dev);
|
||||
|
||||
/* mapping IO device memory */
|
||||
for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) {
|
||||
hw->mem_addr[i] = pci_iomap(pdev, i, 0);
|
||||
if (!hw->mem_addr[i]) {
|
||||
dev_err(&pdev->dev, "mapping I/O device memory failure.\n");
|
||||
err = -ENOMEM;
|
||||
goto free_device;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pci_enable_msi(pdev);
|
||||
|
||||
/* clear spurious interrupts */
|
||||
mei_clear_interrupts(dev);
|
||||
|
||||
/* request and enable interrupt */
|
||||
if (pci_dev_msi_enabled(pdev))
|
||||
err = request_threaded_irq(pdev->irq,
|
||||
NULL,
|
||||
mei_txe_irq_thread_handler,
|
||||
IRQF_ONESHOT, KBUILD_MODNAME, dev);
|
||||
else
|
||||
err = request_threaded_irq(pdev->irq,
|
||||
mei_txe_irq_quick_handler,
|
||||
mei_txe_irq_thread_handler,
|
||||
IRQF_SHARED, KBUILD_MODNAME, dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "mei: request_threaded_irq failure. irq = %d\n",
|
||||
pdev->irq);
|
||||
goto free_device;
|
||||
}
|
||||
|
||||
if (mei_start(dev)) {
|
||||
dev_err(&pdev->dev, "init hw failure.\n");
|
||||
err = -ENODEV;
|
||||
goto release_irq;
|
||||
}
|
||||
|
||||
err = mei_register(dev);
|
||||
if (err)
|
||||
goto release_irq;
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
return 0;
|
||||
|
||||
release_irq:
|
||||
|
||||
mei_cancel_work(dev);
|
||||
|
||||
/* disable interrupts */
|
||||
mei_disable_interrupts(dev);
|
||||
|
||||
free_irq(pdev->irq, dev);
|
||||
pci_disable_msi(pdev);
|
||||
|
||||
free_device:
|
||||
mei_txe_pci_iounmap(pdev, hw);
|
||||
|
||||
kfree(dev);
|
||||
release_regions:
|
||||
pci_release_regions(pdev);
|
||||
disable_device:
|
||||
pci_disable_device(pdev);
|
||||
end:
|
||||
dev_err(&pdev->dev, "initialization failed.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_remove - Device Removal Routine
|
||||
*
|
||||
* @pdev: PCI device structure
|
||||
*
|
||||
* mei_remove is called by the PCI subsystem to alert the driver
|
||||
* that it should release a PCI device.
|
||||
*/
|
||||
static void mei_txe_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_txe_hw *hw;
|
||||
|
||||
dev = pci_get_drvdata(pdev);
|
||||
if (!dev) {
|
||||
dev_err(&pdev->dev, "mei: dev =NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hw = to_txe_hw(dev);
|
||||
|
||||
mei_stop(dev);
|
||||
|
||||
/* disable interrupts */
|
||||
mei_disable_interrupts(dev);
|
||||
free_irq(pdev->irq, dev);
|
||||
pci_disable_msi(pdev);
|
||||
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
|
||||
mei_txe_pci_iounmap(pdev, hw);
|
||||
|
||||
mei_deregister(dev);
|
||||
|
||||
kfree(dev);
|
||||
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mei_txe_pci_suspend(struct device *device)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(device);
|
||||
struct mei_device *dev = pci_get_drvdata(pdev);
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
dev_dbg(&pdev->dev, "suspend\n");
|
||||
|
||||
mei_stop(dev);
|
||||
|
||||
mei_disable_interrupts(dev);
|
||||
|
||||
free_irq(pdev->irq, dev);
|
||||
pci_disable_msi(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mei_txe_pci_resume(struct device *device)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(device);
|
||||
struct mei_device *dev;
|
||||
int err;
|
||||
|
||||
dev = pci_get_drvdata(pdev);
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
pci_enable_msi(pdev);
|
||||
|
||||
mei_clear_interrupts(dev);
|
||||
|
||||
/* request and enable interrupt */
|
||||
if (pci_dev_msi_enabled(pdev))
|
||||
err = request_threaded_irq(pdev->irq,
|
||||
NULL,
|
||||
mei_txe_irq_thread_handler,
|
||||
IRQF_ONESHOT, KBUILD_MODNAME, dev);
|
||||
else
|
||||
err = request_threaded_irq(pdev->irq,
|
||||
mei_txe_irq_quick_handler,
|
||||
mei_txe_irq_thread_handler,
|
||||
IRQF_SHARED, KBUILD_MODNAME, dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n",
|
||||
pdev->irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mei_restart(dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mei_txe_pm_ops,
|
||||
mei_txe_pci_suspend,
|
||||
mei_txe_pci_resume);
|
||||
|
||||
#define MEI_TXE_PM_OPS (&mei_txe_pm_ops)
|
||||
#else
|
||||
#define MEI_TXE_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
/*
|
||||
* PCI driver structure
|
||||
*/
|
||||
static struct pci_driver mei_txe_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = mei_txe_pci_tbl,
|
||||
.probe = mei_txe_probe,
|
||||
.remove = mei_txe_remove,
|
||||
.shutdown = mei_txe_remove,
|
||||
.driver.pm = MEI_TXE_PM_OPS,
|
||||
};
|
||||
|
||||
module_pci_driver(mei_txe_driver);
|
||||
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_DESCRIPTION("Intel(R) Trusted Execution Environment Interface");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
#include "mei_dev.h"
|
||||
#include "hbm.h"
|
||||
#include "hw-me.h"
|
||||
#include "client.h"
|
||||
|
||||
static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
|
||||
|
|
@ -53,7 +52,7 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)
|
|||
*
|
||||
* @dev: the device structure
|
||||
*
|
||||
* returns -ENENT if wd client cannot be found
|
||||
* returns -ENOTTY if wd client cannot be found
|
||||
* -EIO if write has failed
|
||||
* 0 on success
|
||||
*/
|
||||
|
|
@ -73,7 +72,7 @@ int mei_wd_host_init(struct mei_device *dev)
|
|||
id = mei_me_cl_by_uuid(dev, &mei_wd_guid);
|
||||
if (id < 0) {
|
||||
dev_info(&dev->pdev->dev, "wd: failed to find the client\n");
|
||||
return id;
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
cl->me_client_id = dev->me_clients[id].client_id;
|
||||
|
|
@ -87,15 +86,20 @@ int mei_wd_host_init(struct mei_device *dev)
|
|||
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
|
||||
if (mei_hbm_cl_connect_req(dev, cl)) {
|
||||
dev_err(&dev->pdev->dev, "wd: failed to connect to the client\n");
|
||||
cl->state = MEI_FILE_DISCONNECTED;
|
||||
cl->host_client_id = 0;
|
||||
return -EIO;
|
||||
}
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
ret = mei_cl_connect(cl, NULL);
|
||||
|
||||
return 0;
|
||||
if (ret) {
|
||||
dev_err(&dev->pdev->dev, "wd: failed to connect = %d\n", ret);
|
||||
mei_cl_unlink(cl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mei_watchdog_register(dev);
|
||||
if (ret) {
|
||||
mei_cl_disconnect(cl);
|
||||
mei_cl_unlink(cl);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -106,13 +110,16 @@ int mei_wd_host_init(struct mei_device *dev)
|
|||
* returns 0 if success,
|
||||
* -EIO when message send fails
|
||||
* -EINVAL when invalid message is to be sent
|
||||
* -ENODEV on flow control failure
|
||||
*/
|
||||
int mei_wd_send(struct mei_device *dev)
|
||||
{
|
||||
struct mei_cl *cl = &dev->wd_cl;
|
||||
struct mei_msg_hdr hdr;
|
||||
int ret;
|
||||
|
||||
hdr.host_addr = dev->wd_cl.host_client_id;
|
||||
hdr.me_addr = dev->wd_cl.me_client_id;
|
||||
hdr.host_addr = cl->host_client_id;
|
||||
hdr.me_addr = cl->me_client_id;
|
||||
hdr.msg_complete = 1;
|
||||
hdr.reserved = 0;
|
||||
hdr.internal = 0;
|
||||
|
|
@ -121,10 +128,24 @@ int mei_wd_send(struct mei_device *dev)
|
|||
hdr.length = MEI_WD_START_MSG_SIZE;
|
||||
else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE))
|
||||
hdr.length = MEI_WD_STOP_MSG_SIZE;
|
||||
else
|
||||
else {
|
||||
dev_err(&dev->pdev->dev, "wd: invalid message is to be sent, aborting\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return mei_write_message(dev, &hdr, dev->wd_data);
|
||||
ret = mei_write_message(dev, &hdr, dev->wd_data);
|
||||
if (ret) {
|
||||
dev_err(&dev->pdev->dev, "wd: write message failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mei_cl_flow_ctrl_reduce(cl);
|
||||
if (ret) {
|
||||
dev_err(&dev->pdev->dev, "wd: flow_ctrl_reduce failed.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -133,9 +154,11 @@ int mei_wd_send(struct mei_device *dev)
|
|||
* @dev: the device structure
|
||||
* @preserve: indicate if to keep the timeout value
|
||||
*
|
||||
* returns 0 if success,
|
||||
* -EIO when message send fails
|
||||
* returns 0 if success
|
||||
* on error:
|
||||
* -EIO when message send fails
|
||||
* -EINVAL when invalid message is to be sent
|
||||
* -ETIME on message timeout
|
||||
*/
|
||||
int mei_wd_stop(struct mei_device *dev)
|
||||
{
|
||||
|
|
@ -151,20 +174,12 @@ int mei_wd_stop(struct mei_device *dev)
|
|||
|
||||
ret = mei_cl_flow_ctrl_creds(&dev->wd_cl);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (ret && dev->hbuf_is_ready) {
|
||||
ret = 0;
|
||||
dev->hbuf_is_ready = false;
|
||||
|
||||
if (!mei_wd_send(dev)) {
|
||||
ret = mei_cl_flow_ctrl_reduce(&dev->wd_cl);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else {
|
||||
dev_err(&dev->pdev->dev, "wd: send stop failed\n");
|
||||
}
|
||||
goto err;
|
||||
|
||||
if (ret && mei_hbuf_acquire(dev)) {
|
||||
ret = mei_wd_send(dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
dev->wd_pending = false;
|
||||
} else {
|
||||
dev->wd_pending = true;
|
||||
|
|
@ -172,21 +187,21 @@ int mei_wd_stop(struct mei_device *dev)
|
|||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
ret = wait_event_interruptible_timeout(dev->wait_stop_wd,
|
||||
dev->wd_state == MEI_WD_IDLE,
|
||||
msecs_to_jiffies(MEI_WD_STOP_TIMEOUT));
|
||||
ret = wait_event_timeout(dev->wait_stop_wd,
|
||||
dev->wd_state == MEI_WD_IDLE,
|
||||
msecs_to_jiffies(MEI_WD_STOP_TIMEOUT));
|
||||
mutex_lock(&dev->device_lock);
|
||||
if (dev->wd_state == MEI_WD_IDLE) {
|
||||
dev_dbg(&dev->pdev->dev, "wd: stop completed ret=%d.\n", ret);
|
||||
ret = 0;
|
||||
} else {
|
||||
if (!ret)
|
||||
ret = -ETIMEDOUT;
|
||||
if (dev->wd_state != MEI_WD_IDLE) {
|
||||
/* timeout */
|
||||
ret = -ETIME;
|
||||
dev_warn(&dev->pdev->dev,
|
||||
"wd: stop failed to complete ret=%d.\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
out:
|
||||
dev_dbg(&dev->pdev->dev, "wd: stop completed after %u msec\n",
|
||||
MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret));
|
||||
return 0;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -260,8 +275,8 @@ static int mei_wd_ops_stop(struct watchdog_device *wd_dev)
|
|||
*/
|
||||
static int mei_wd_ops_ping(struct watchdog_device *wd_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mei_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = watchdog_get_drvdata(wd_dev);
|
||||
if (!dev)
|
||||
|
|
@ -277,25 +292,18 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev)
|
|||
|
||||
dev->wd_state = MEI_WD_RUNNING;
|
||||
|
||||
ret = mei_cl_flow_ctrl_creds(&dev->wd_cl);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
/* Check if we can send the ping to HW*/
|
||||
if (dev->hbuf_is_ready && mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) {
|
||||
if (ret && mei_hbuf_acquire(dev)) {
|
||||
|
||||
dev->hbuf_is_ready = false;
|
||||
dev_dbg(&dev->pdev->dev, "wd: sending ping\n");
|
||||
|
||||
if (mei_wd_send(dev)) {
|
||||
dev_err(&dev->pdev->dev, "wd: send failed.\n");
|
||||
ret = -EIO;
|
||||
ret = mei_wd_send(dev);
|
||||
if (ret)
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (mei_cl_flow_ctrl_reduce(&dev->wd_cl)) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"wd: mei_cl_flow_ctrl_reduce() failed.\n");
|
||||
ret = -EIO;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dev->wd_pending = false;
|
||||
} else {
|
||||
dev->wd_pending = true;
|
||||
}
|
||||
|
|
@ -363,17 +371,25 @@ static struct watchdog_device amt_wd_dev = {
|
|||
};
|
||||
|
||||
|
||||
void mei_watchdog_register(struct mei_device *dev)
|
||||
int mei_watchdog_register(struct mei_device *dev)
|
||||
{
|
||||
if (watchdog_register_device(&amt_wd_dev)) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"wd: unable to register watchdog device.\n");
|
||||
return;
|
||||
|
||||
int ret;
|
||||
|
||||
/* unlock to perserve correct locking order */
|
||||
mutex_unlock(&dev->device_lock);
|
||||
ret = watchdog_register_device(&amt_wd_dev);
|
||||
mutex_lock(&dev->device_lock);
|
||||
if (ret) {
|
||||
dev_err(&dev->pdev->dev, "wd: unable to register watchdog device = %d.\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
"wd: successfully register watchdog interface.\n");
|
||||
watchdog_set_drvdata(&amt_wd_dev, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mei_watchdog_unregister(struct mei_device *dev)
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev)
|
|||
for (i = 0; i < MIC_MIN_MSIX; i++)
|
||||
mdev->irq_info.msix_entries[i].entry = i;
|
||||
|
||||
rc = pci_enable_msix(pdev, mdev->irq_info.msix_entries,
|
||||
rc = pci_enable_msix_exact(pdev, mdev->irq_info.msix_entries,
|
||||
MIC_MIN_MSIX);
|
||||
if (rc) {
|
||||
dev_dbg(&pdev->dev, "Error enabling MSIx. rc = %d\n", rc);
|
||||
|
|
|
|||
|
|
@ -636,6 +636,7 @@ static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr,
|
|||
u8 mac[ETH_ALEN];
|
||||
ssize_t rom_size;
|
||||
struct pch_phub_reg *chip = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (!mac_pton(buf, mac))
|
||||
return -EINVAL;
|
||||
|
|
@ -644,8 +645,10 @@ static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr,
|
|||
if (!chip->pch_phub_extrom_base_address)
|
||||
return -ENOMEM;
|
||||
|
||||
pch_phub_write_gbe_mac_addr(chip, mac);
|
||||
ret = pch_phub_write_gbe_mac_addr(chip, mac);
|
||||
pci_unmap_rom(chip->pdev, chip->pch_phub_extrom_base_address);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/list_sort.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
|
@ -36,14 +39,35 @@ struct sram_dev {
|
|||
struct clk *clk;
|
||||
};
|
||||
|
||||
struct sram_reserve {
|
||||
struct list_head list;
|
||||
u32 start;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
static int sram_reserve_cmp(void *priv, struct list_head *a,
|
||||
struct list_head *b)
|
||||
{
|
||||
struct sram_reserve *ra = list_entry(a, struct sram_reserve, list);
|
||||
struct sram_reserve *rb = list_entry(b, struct sram_reserve, list);
|
||||
|
||||
return ra->start - rb->start;
|
||||
}
|
||||
|
||||
static int sram_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *virt_base;
|
||||
struct sram_dev *sram;
|
||||
struct resource *res;
|
||||
unsigned long size;
|
||||
struct device_node *np = pdev->dev.of_node, *child;
|
||||
unsigned long size, cur_start, cur_size;
|
||||
struct sram_reserve *rblocks, *block;
|
||||
struct list_head reserve_list;
|
||||
unsigned int nblocks;
|
||||
int ret;
|
||||
|
||||
INIT_LIST_HEAD(&reserve_list);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
virt_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(virt_base))
|
||||
|
|
@ -65,19 +89,106 @@ static int sram_probe(struct platform_device *pdev)
|
|||
if (!sram->pool)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = gen_pool_add_virt(sram->pool, (unsigned long)virt_base,
|
||||
res->start, size, -1);
|
||||
if (ret < 0) {
|
||||
if (sram->clk)
|
||||
clk_disable_unprepare(sram->clk);
|
||||
return ret;
|
||||
/*
|
||||
* We need an additional block to mark the end of the memory region
|
||||
* after the reserved blocks from the dt are processed.
|
||||
*/
|
||||
nblocks = (np) ? of_get_available_child_count(np) + 1 : 1;
|
||||
rblocks = kmalloc((nblocks) * sizeof(*rblocks), GFP_KERNEL);
|
||||
if (!rblocks) {
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
block = &rblocks[0];
|
||||
for_each_available_child_of_node(np, child) {
|
||||
struct resource child_res;
|
||||
|
||||
ret = of_address_to_resource(child, 0, &child_res);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"could not get address for node %s\n",
|
||||
child->full_name);
|
||||
goto err_chunks;
|
||||
}
|
||||
|
||||
if (child_res.start < res->start || child_res.end > res->end) {
|
||||
dev_err(&pdev->dev,
|
||||
"reserved block %s outside the sram area\n",
|
||||
child->full_name);
|
||||
ret = -EINVAL;
|
||||
goto err_chunks;
|
||||
}
|
||||
|
||||
block->start = child_res.start - res->start;
|
||||
block->size = resource_size(&child_res);
|
||||
list_add_tail(&block->list, &reserve_list);
|
||||
|
||||
dev_dbg(&pdev->dev, "found reserved block 0x%x-0x%x\n",
|
||||
block->start,
|
||||
block->start + block->size);
|
||||
|
||||
block++;
|
||||
}
|
||||
|
||||
/* the last chunk marks the end of the region */
|
||||
rblocks[nblocks - 1].start = size;
|
||||
rblocks[nblocks - 1].size = 0;
|
||||
list_add_tail(&rblocks[nblocks - 1].list, &reserve_list);
|
||||
|
||||
list_sort(NULL, &reserve_list, sram_reserve_cmp);
|
||||
|
||||
cur_start = 0;
|
||||
|
||||
list_for_each_entry(block, &reserve_list, list) {
|
||||
/* can only happen if sections overlap */
|
||||
if (block->start < cur_start) {
|
||||
dev_err(&pdev->dev,
|
||||
"block at 0x%x starts after current offset 0x%lx\n",
|
||||
block->start, cur_start);
|
||||
ret = -EINVAL;
|
||||
goto err_chunks;
|
||||
}
|
||||
|
||||
/* current start is in a reserved block, so continue after it */
|
||||
if (block->start == cur_start) {
|
||||
cur_start = block->start + block->size;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate the space between the current starting
|
||||
* address and the following reserved block, or the
|
||||
* end of the region.
|
||||
*/
|
||||
cur_size = block->start - cur_start;
|
||||
|
||||
dev_dbg(&pdev->dev, "adding chunk 0x%lx-0x%lx\n",
|
||||
cur_start, cur_start + cur_size);
|
||||
ret = gen_pool_add_virt(sram->pool,
|
||||
(unsigned long)virt_base + cur_start,
|
||||
res->start + cur_start, cur_size, -1);
|
||||
if (ret < 0)
|
||||
goto err_chunks;
|
||||
|
||||
/* next allocation after this reserved block */
|
||||
cur_start = block->start + block->size;
|
||||
}
|
||||
|
||||
kfree(rblocks);
|
||||
|
||||
platform_set_drvdata(pdev, sram);
|
||||
|
||||
dev_dbg(&pdev->dev, "SRAM pool: %ld KiB @ 0x%p\n", size / 1024, virt_base);
|
||||
|
||||
return 0;
|
||||
|
||||
err_chunks:
|
||||
kfree(rblocks);
|
||||
err_alloc:
|
||||
if (sram->clk)
|
||||
clk_disable_unprepare(sram->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sram_remove(struct platform_device *pdev)
|
||||
|
|
@ -87,8 +198,6 @@ static int sram_remove(struct platform_device *pdev)
|
|||
if (gen_pool_avail(sram->pool) < gen_pool_size(sram->pool))
|
||||
dev_dbg(&pdev->dev, "removed while SRAM allocated\n");
|
||||
|
||||
gen_pool_destroy(sram->pool);
|
||||
|
||||
if (sram->clk)
|
||||
clk_disable_unprepare(sram->clk);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#define pr_fmt(fmt) "(stc): " fmt
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/tty.h>
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
|
|
|
|||
|
|
@ -383,11 +383,12 @@ static int vmci_enable_msix(struct pci_dev *pdev,
|
|||
vmci_dev->msix_entries[i].vector = i;
|
||||
}
|
||||
|
||||
result = pci_enable_msix(pdev, vmci_dev->msix_entries, VMCI_MAX_INTRS);
|
||||
result = pci_enable_msix_exact(pdev,
|
||||
vmci_dev->msix_entries, VMCI_MAX_INTRS);
|
||||
if (result == 0)
|
||||
vmci_dev->exclusive_vectors = true;
|
||||
else if (result > 0)
|
||||
result = pci_enable_msix(pdev, vmci_dev->msix_entries, 1);
|
||||
else if (result == -ENOSPC)
|
||||
result = pci_enable_msix_exact(pdev, vmci_dev->msix_entries, 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue