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:
Linus Torvalds 2014-04-01 16:13:21 -07:00
commit 675c354a95
167 changed files with 8865 additions and 1722 deletions

View file

@ -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

View file

@ -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>

View file

@ -22,7 +22,6 @@
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/err.h>

View file

@ -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"

View file

@ -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>

View file

@ -32,7 +32,6 @@
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/list.h>

View file

@ -10,7 +10,6 @@
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>

View file

@ -17,7 +17,6 @@
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>

View file

@ -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>

View file

@ -27,7 +27,6 @@
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>

View file

@ -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 */},
};

View file

@ -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>

View file

@ -22,7 +22,6 @@
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/err.h>

View file

@ -26,7 +26,6 @@
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>

View file

@ -23,7 +23,6 @@
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/err.h>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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) {

View file

@ -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();

View file

@ -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);

View file

@ -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);

View 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

File diff suppressed because it is too large Load diff

74
drivers/misc/mei/hw-txe.h Normal file
View 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_ */

View file

@ -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
};
/*

View file

@ -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);

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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
View 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");

View file

@ -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)

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -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>

View file

@ -20,7 +20,6 @@
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spi/spi.h>
#include <linux/of.h>

View file

@ -20,7 +20,6 @@
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>

View file

@ -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;
}