summaryrefslogtreecommitdiff
path: root/drivers/misc/mei
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/mei')
-rw-r--r--drivers/misc/mei/Kconfig6
-rw-r--r--drivers/misc/mei/Makefile1
-rw-r--r--drivers/misc/mei/amthif.c134
-rw-r--r--drivers/misc/mei/bus-fixup.c41
-rw-r--r--drivers/misc/mei/bus.c63
-rw-r--r--drivers/misc/mei/client.c193
-rw-r--r--drivers/misc/mei/client.h27
-rw-r--r--drivers/misc/mei/debugfs.c65
-rw-r--r--drivers/misc/mei/hbm.c27
-rw-r--r--drivers/misc/mei/hw-me-regs.h4
-rw-r--r--drivers/misc/mei/hw-me.c10
-rw-r--r--drivers/misc/mei/hw-txe.c10
-rw-r--r--drivers/misc/mei/hw.h32
-rw-r--r--drivers/misc/mei/init.c20
-rw-r--r--drivers/misc/mei/interrupt.c100
-rw-r--r--drivers/misc/mei/main.c106
-rw-r--r--drivers/misc/mei/mei-trace.c2
-rw-r--r--drivers/misc/mei/mei-trace.h40
-rw-r--r--drivers/misc/mei/mei_dev.h120
-rw-r--r--drivers/misc/mei/pci-me.c7
-rw-r--r--drivers/misc/mei/pci-txe.c4
-rw-r--r--drivers/misc/mei/wd.c391
22 files changed, 521 insertions, 882 deletions
diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig
index d23384dde..c49e1d226 100644
--- a/drivers/misc/mei/Kconfig
+++ b/drivers/misc/mei/Kconfig
@@ -1,6 +1,6 @@
config INTEL_MEI
tristate "Intel Management Engine Interface"
- depends on X86 && PCI && WATCHDOG_CORE
+ depends on X86 && PCI
help
The Intel Management Engine (Intel ME) provides Manageability,
Security and Media services for system containing Intel chipsets.
@@ -12,7 +12,7 @@ config INTEL_MEI
config INTEL_MEI_ME
tristate "ME Enabled Intel Chipsets"
select INTEL_MEI
- depends on X86 && PCI && WATCHDOG_CORE
+ depends on X86 && PCI
help
MEI support for ME Enabled Intel chipsets.
@@ -37,7 +37,7 @@ config INTEL_MEI_ME
config INTEL_MEI_TXE
tristate "Intel Trusted Execution Environment with ME Interface"
select INTEL_MEI
- depends on X86 && PCI && WATCHDOG_CORE
+ depends on X86 && PCI
help
MEI Support for Trusted Execution Environment device on Intel SoCs
diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile
index 01447ca21..59e6b0aed 100644
--- a/drivers/misc/mei/Makefile
+++ b/drivers/misc/mei/Makefile
@@ -9,7 +9,6 @@ mei-objs += interrupt.o
mei-objs += client.o
mei-objs += main.o
mei-objs += amthif.o
-mei-objs += wd.o
mei-objs += bus.o
mei-objs += bus-fixup.o
mei-$(CONFIG_DEBUG_FS) += debugfs.o
diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c
index cd0403f09..a039a5df6 100644
--- a/drivers/misc/mei/amthif.c
+++ b/drivers/misc/mei/amthif.c
@@ -50,7 +50,6 @@ void mei_amthif_reset_params(struct mei_device *dev)
dev->iamthif_current_cb = NULL;
dev->iamthif_canceled = false;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
- dev->iamthif_timer = 0;
dev->iamthif_stall_timer = 0;
dev->iamthif_open_count = 0;
}
@@ -68,11 +67,14 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
struct mei_cl *cl = &dev->iamthif_cl;
int ret;
+ if (mei_cl_is_connected(cl))
+ return 0;
+
dev->iamthif_state = MEI_IAMTHIF_IDLE;
mei_cl_init(cl, dev);
- ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID);
+ ret = mei_cl_link(cl);
if (ret < 0) {
dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
return ret;
@@ -80,32 +82,10 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
ret = mei_cl_connect(cl, me_cl, NULL);
- dev->iamthif_state = MEI_IAMTHIF_IDLE;
-
return ret;
}
/**
- * mei_amthif_find_read_list_entry - finds a amthilist entry for current file
- *
- * @dev: the device structure
- * @file: pointer to file object
- *
- * Return: returned a list entry on success, NULL on failure.
- */
-struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
- struct file *file)
-{
- struct mei_cl_cb *cb;
-
- list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list)
- if (cb->file_object == file)
- return cb;
- return NULL;
-}
-
-
-/**
* mei_amthif_read - read data from AMTHIF client
*
* @dev: the device structure
@@ -126,18 +106,11 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
{
struct mei_cl *cl = file->private_data;
struct mei_cl_cb *cb;
- unsigned long timeout;
int rets;
int wait_ret;
- /* Only possible if we are in timeout */
- if (!cl) {
- dev_err(dev->dev, "bad file ext.\n");
- return -ETIME;
- }
-
dev_dbg(dev->dev, "checking amthif data\n");
- cb = mei_amthif_find_read_list_entry(dev, file);
+ cb = mei_cl_read_cb(cl, file);
/* Check for if we can block or not*/
if (cb == NULL && file->f_flags & O_NONBLOCK)
@@ -149,8 +122,9 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
/* unlock the Mutex */
mutex_unlock(&dev->device_lock);
- wait_ret = wait_event_interruptible(dev->iamthif_cl.wait,
- (cb = mei_amthif_find_read_list_entry(dev, file)));
+ wait_ret = wait_event_interruptible(cl->rx_wait,
+ !list_empty(&cl->rd_completed) ||
+ !mei_cl_is_connected(cl));
/* Locking again the Mutex */
mutex_lock(&dev->device_lock);
@@ -158,7 +132,12 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
if (wait_ret)
return -ERESTARTSYS;
- dev_dbg(dev->dev, "woke up from sleep\n");
+ if (!mei_cl_is_connected(cl)) {
+ rets = -EBUSY;
+ goto out;
+ }
+
+ cb = mei_cl_read_cb(cl, file);
}
if (cb->status) {
@@ -168,24 +147,10 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
}
dev_dbg(dev->dev, "Got amthif data\n");
- dev->iamthif_timer = 0;
-
- timeout = cb->read_time +
- mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
- dev_dbg(dev->dev, "amthif timeout = %lud\n",
- timeout);
-
- if (time_after(jiffies, timeout)) {
- dev_dbg(dev->dev, "amthif Time out\n");
- /* 15 sec for the message has expired */
- list_del_init(&cb->list);
- rets = -ETIME;
- goto free;
- }
/* if the whole message will fit remove it from the list */
if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset))
list_del_init(&cb->list);
- else if (cb->buf_idx > 0 && cb->buf_idx <= *offset) {
+ else if (cb->buf_idx <= *offset) {
/* end of the message has been reached */
list_del_init(&cb->list);
rets = 0;
@@ -195,9 +160,8 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
* remove message from deletion list
*/
- dev_dbg(dev->dev, "amthif cb->buf size - %d\n",
- cb->buf.size);
- dev_dbg(dev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx);
+ dev_dbg(dev->dev, "amthif cb->buf.size - %zu cb->buf_idx - %zu\n",
+ cb->buf.size, cb->buf_idx);
/* length is being truncated to PAGE_SIZE, however,
* the buf_idx may point beyond */
@@ -229,7 +193,7 @@ out:
*
* Return: 0 on success, <0 on failure.
*/
-static int mei_amthif_read_start(struct mei_cl *cl, struct file *file)
+static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file)
{
struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb;
@@ -248,7 +212,7 @@ static int mei_amthif_read_start(struct mei_cl *cl, struct file *file)
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
dev->iamthif_state = MEI_IAMTHIF_READING;
- dev->iamthif_file_object = cb->file_object;
+ dev->iamthif_fp = cb->fp;
dev->iamthif_current_cb = cb;
return 0;
@@ -277,7 +241,7 @@ static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb)
dev->iamthif_state = MEI_IAMTHIF_WRITING;
dev->iamthif_current_cb = cb;
- dev->iamthif_file_object = cb->file_object;
+ dev->iamthif_fp = cb->fp;
dev->iamthif_canceled = false;
ret = mei_cl_write(cl, cb, false);
@@ -285,7 +249,7 @@ static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb)
return ret;
if (cb->completed)
- cb->status = mei_amthif_read_start(cl, cb->file_object);
+ cb->status = mei_amthif_read_start(cl, cb->fp);
return 0;
}
@@ -304,8 +268,7 @@ int mei_amthif_run_next_cmd(struct mei_device *dev)
dev->iamthif_canceled = false;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
- dev->iamthif_timer = 0;
- dev->iamthif_file_object = NULL;
+ dev->iamthif_fp = NULL;
dev_dbg(dev->dev, "complete amthif cmd_list cb.\n");
@@ -329,17 +292,17 @@ int mei_amthif_run_next_cmd(struct mei_device *dev)
int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
{
- struct mei_device *dev;
-
- if (WARN_ON(!cl || !cl->dev))
- return -ENODEV;
+ struct mei_device *dev = cl->dev;
- if (WARN_ON(!cb))
- return -EINVAL;
+ list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
- dev = cl->dev;
+ /*
+ * The previous request is still in processing, queue this one.
+ */
+ if (dev->iamthif_state > MEI_IAMTHIF_IDLE &&
+ dev->iamthif_state < MEI_IAMTHIF_READ_COMPLETE)
+ return 0;
- list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
return mei_amthif_run_next_cmd(dev);
}
@@ -360,10 +323,10 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
{
unsigned int mask = 0;
- poll_wait(file, &dev->iamthif_cl.wait, wait);
+ poll_wait(file, &dev->iamthif_cl.rx_wait, wait);
if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
- dev->iamthif_file_object == file) {
+ dev->iamthif_fp == file) {
mask |= POLLIN | POLLRDNORM;
mei_amthif_run_next_cmd(dev);
@@ -393,7 +356,7 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
return ret;
if (cb->completed)
- cb->status = mei_amthif_read_start(cl, cb->file_object);
+ cb->status = mei_amthif_read_start(cl, cb->fp);
return 0;
}
@@ -417,8 +380,10 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl,
dev = cl->dev;
- if (dev->iamthif_state != MEI_IAMTHIF_READING)
+ if (dev->iamthif_state != MEI_IAMTHIF_READING) {
+ mei_irq_discard_msg(dev, mei_hdr);
return 0;
+ }
ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
if (ret)
@@ -437,11 +402,12 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl,
/**
* mei_amthif_complete - complete amthif callback.
*
- * @dev: the device structure.
+ * @cl: host client
* @cb: callback block.
*/
-void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb)
+void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
{
+ struct mei_device *dev = cl->dev;
if (cb->fop_type == MEI_FOP_WRITE) {
if (!cb->status) {
@@ -453,25 +419,22 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb)
* in case of error enqueue the write cb to complete read list
* so it can be propagated to the reader
*/
- list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list);
- wake_up_interruptible(&dev->iamthif_cl.wait);
+ list_add_tail(&cb->list, &cl->rd_completed);
+ wake_up_interruptible(&cl->rx_wait);
return;
}
if (!dev->iamthif_canceled) {
dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
dev->iamthif_stall_timer = 0;
- list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list);
+ list_add_tail(&cb->list, &cl->rd_completed);
dev_dbg(dev->dev, "amthif read completed\n");
- dev->iamthif_timer = jiffies;
- dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n",
- dev->iamthif_timer);
} else {
mei_amthif_run_next_cmd(dev);
}
dev_dbg(dev->dev, "completing amthif call back.\n");
- wake_up_interruptible(&dev->iamthif_cl.wait);
+ wake_up_interruptible(&cl->rx_wait);
}
/**
@@ -497,7 +460,7 @@ static bool mei_clear_list(struct mei_device *dev,
/* list all list member */
list_for_each_entry_safe(cb, next, mei_cb_list, list) {
/* check if list member associated with a file */
- if (file == cb->file_object) {
+ if (file == cb->fp) {
/* check if cb equal to current iamthif cb */
if (dev->iamthif_current_cb == cb) {
dev->iamthif_current_cb = NULL;
@@ -523,13 +486,14 @@ static bool mei_clear_list(struct mei_device *dev,
*
* Return: true if callback removed from the list, false otherwise
*/
-static bool mei_clear_lists(struct mei_device *dev, struct file *file)
+static bool mei_clear_lists(struct mei_device *dev, const struct file *file)
{
bool removed = false;
+ struct mei_cl *cl = &dev->iamthif_cl;
/* remove callbacks associated with a file */
mei_clear_list(dev, file, &dev->amthif_cmd_list.list);
- if (mei_clear_list(dev, file, &dev->amthif_rd_complete_list.list))
+ if (mei_clear_list(dev, file, &cl->rd_completed))
removed = true;
mei_clear_list(dev, file, &dev->ctrl_rd_list.list);
@@ -546,7 +510,7 @@ static bool mei_clear_lists(struct mei_device *dev, struct file *file)
/* check if iamthif_current_cb not NULL */
if (dev->iamthif_current_cb && !removed) {
/* check file and iamthif current cb association */
- if (dev->iamthif_current_cb->file_object == file) {
+ if (dev->iamthif_current_cb->fp == file) {
/* remove cb */
mei_io_cb_free(dev->iamthif_current_cb);
dev->iamthif_current_cb = NULL;
@@ -569,7 +533,7 @@ int mei_amthif_release(struct mei_device *dev, struct file *file)
if (dev->iamthif_open_count > 0)
dev->iamthif_open_count--;
- if (dev->iamthif_file_object == file &&
+ if (dev->iamthif_fp == file &&
dev->iamthif_state != MEI_IAMTHIF_IDLE) {
dev_dbg(dev->dev, "amthif canceled iamthif state %d\n",
diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c
index 020de5919..e9e6ea3ab 100644
--- a/drivers/misc/mei/bus-fixup.c
+++ b/drivers/misc/mei/bus-fixup.c
@@ -35,6 +35,9 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO;
#define MEI_UUID_NFC_HCI UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \
0x94, 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c)
+#define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \
+ 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB)
+
#define MEI_UUID_ANY NULL_UUID_LE
/**
@@ -48,8 +51,7 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO;
*/
static void number_of_connections(struct mei_cl_device *cldev)
{
- dev_dbg(&cldev->dev, "running hook %s on %pUl\n",
- __func__, mei_me_cl_uuid(cldev->me_cl));
+ dev_dbg(&cldev->dev, "running hook %s\n", __func__);
if (cldev->me_cl->props.max_number_of_connections > 1)
cldev->do_match = 0;
@@ -62,11 +64,36 @@ static void number_of_connections(struct mei_cl_device *cldev)
*/
static void blacklist(struct mei_cl_device *cldev)
{
- dev_dbg(&cldev->dev, "running hook %s on %pUl\n",
- __func__, mei_me_cl_uuid(cldev->me_cl));
+ dev_dbg(&cldev->dev, "running hook %s\n", __func__);
+
cldev->do_match = 0;
}
+/**
+ * mei_wd - wd client on the bus, change protocol version
+ * as the API has changed.
+ *
+ * @cldev: me clients device
+ */
+#if IS_ENABLED(CONFIG_INTEL_MEI_ME)
+#include <linux/pci.h>
+#include "hw-me-regs.h"
+static void mei_wd(struct mei_cl_device *cldev)
+{
+ struct pci_dev *pdev = to_pci_dev(cldev->dev.parent);
+
+ dev_dbg(&cldev->dev, "running hook %s\n", __func__);
+ if (pdev->device == MEI_DEV_ID_WPT_LP ||
+ pdev->device == MEI_DEV_ID_SPT ||
+ pdev->device == MEI_DEV_ID_SPT_H)
+ cldev->me_cl->props.protocol_version = 0x2;
+
+ cldev->do_match = 1;
+}
+#else
+static inline void mei_wd(struct mei_cl_device *cldev) {}
+#endif /* CONFIG_INTEL_MEI_ME */
+
struct mei_nfc_cmd {
u8 command;
u8 status;
@@ -208,12 +235,11 @@ static void mei_nfc(struct mei_cl_device *cldev)
bus = cldev->bus;
- dev_dbg(bus->dev, "running hook %s: %pUl match=%d\n",
- __func__, mei_me_cl_uuid(cldev->me_cl), cldev->do_match);
+ dev_dbg(&cldev->dev, "running hook %s\n", __func__);
mutex_lock(&bus->device_lock);
/* we need to connect to INFO GUID */
- cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY);
+ cl = mei_cl_alloc_linked(bus);
if (IS_ERR(cl)) {
ret = PTR_ERR(cl);
cl = NULL;
@@ -282,6 +308,7 @@ static struct mei_fixup {
MEI_FIXUP(MEI_UUID_ANY, number_of_connections),
MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist),
MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc),
+ MEI_FIXUP(MEI_UUID_WD, mei_wd),
};
/**
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index 1a173d0af..038b6dd24 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -44,7 +44,7 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
bool blocking)
{
struct mei_device *bus;
- struct mei_cl_cb *cb = NULL;
+ struct mei_cl_cb *cb;
ssize_t rets;
if (WARN_ON(!cl || !cl->dev))
@@ -86,8 +86,6 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
out:
mutex_unlock(&bus->device_lock);
- if (rets < 0)
- mei_io_cb_free(cb);
return rets;
}
@@ -222,62 +220,78 @@ EXPORT_SYMBOL_GPL(mei_cldev_recv);
static void mei_cl_bus_event_work(struct work_struct *work)
{
struct mei_cl_device *cldev;
+ struct mei_device *bus;
cldev = container_of(work, struct mei_cl_device, event_work);
+ bus = cldev->bus;
+
if (cldev->event_cb)
cldev->event_cb(cldev, cldev->events, cldev->event_context);
cldev->events = 0;
/* Prepare for the next read */
- if (cldev->events_mask & BIT(MEI_CL_EVENT_RX))
+ if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
+ mutex_lock(&bus->device_lock);
mei_cl_read_start(cldev->cl, 0, NULL);
+ mutex_unlock(&bus->device_lock);
+ }
}
/**
* mei_cl_bus_notify_event - schedule notify cb on bus client
*
* @cl: host client
+ *
+ * Return: true if event was scheduled
+ * false if the client is not waiting for event
*/
-void mei_cl_bus_notify_event(struct mei_cl *cl)
+bool mei_cl_bus_notify_event(struct mei_cl *cl)
{
struct mei_cl_device *cldev = cl->cldev;
if (!cldev || !cldev->event_cb)
- return;
+ return false;
if (!(cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)))
- return;
+ return false;
if (!cl->notify_ev)
- return;
+ return false;
set_bit(MEI_CL_EVENT_NOTIF, &cldev->events);
schedule_work(&cldev->event_work);
cl->notify_ev = false;
+
+ return true;
}
/**
- * mei_cl_bus_rx_event - schedule rx evenet
+ * mei_cl_bus_rx_event - schedule rx event
*
* @cl: host client
+ *
+ * Return: true if event was scheduled
+ * false if the client is not waiting for event
*/
-void mei_cl_bus_rx_event(struct mei_cl *cl)
+bool mei_cl_bus_rx_event(struct mei_cl *cl)
{
struct mei_cl_device *cldev = cl->cldev;
if (!cldev || !cldev->event_cb)
- return;
+ return false;
if (!(cldev->events_mask & BIT(MEI_CL_EVENT_RX)))
- return;
+ return false;
set_bit(MEI_CL_EVENT_RX, &cldev->events);
schedule_work(&cldev->event_work);
+
+ return true;
}
/**
@@ -296,6 +310,7 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
unsigned long events_mask,
mei_cldev_event_cb_t event_cb, void *context)
{
+ struct mei_device *bus = cldev->bus;
int ret;
if (cldev->event_cb)
@@ -308,15 +323,17 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
INIT_WORK(&cldev->event_work, mei_cl_bus_event_work);
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
+ mutex_lock(&bus->device_lock);
ret = mei_cl_read_start(cldev->cl, 0, NULL);
+ mutex_unlock(&bus->device_lock);
if (ret && ret != -EBUSY)
return ret;
}
if (cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)) {
- mutex_lock(&cldev->cl->dev->device_lock);
+ mutex_lock(&bus->device_lock);
ret = mei_cl_notify_request(cldev->cl, NULL, event_cb ? 1 : 0);
- mutex_unlock(&cldev->cl->dev->device_lock);
+ mutex_unlock(&bus->device_lock);
if (ret)
return ret;
}
@@ -407,7 +424,7 @@ int mei_cldev_enable(struct mei_cl_device *cldev)
if (!cl) {
mutex_lock(&bus->device_lock);
- cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY);
+ cl = mei_cl_alloc_linked(bus);
mutex_unlock(&bus->device_lock);
if (IS_ERR(cl))
return PTR_ERR(cl);
@@ -967,6 +984,22 @@ void mei_cl_bus_rescan(struct mei_device *bus)
dev_dbg(bus->dev, "rescan end");
}
+void mei_cl_bus_rescan_work(struct work_struct *work)
+{
+ struct mei_device *bus =
+ container_of(work, struct mei_device, bus_rescan_work);
+ struct mei_me_client *me_cl;
+
+ mutex_lock(&bus->device_lock);
+ me_cl = mei_me_cl_by_uuid(bus, &mei_amthif_guid);
+ if (me_cl)
+ mei_amthif_host_init(bus, me_cl);
+ mei_me_cl_put(me_cl);
+ mutex_unlock(&bus->device_lock);
+
+ mei_cl_bus_rescan(bus);
+}
+
int __mei_cldev_driver_register(struct mei_cl_driver *cldrv,
struct module *owner)
{
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index a6c87c713..09f5280fa 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -359,7 +359,7 @@ void mei_io_cb_free(struct mei_cl_cb *cb)
* Return: mei_cl_cb pointer or NULL;
*/
struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type,
- struct file *fp)
+ const struct file *fp)
{
struct mei_cl_cb *cb;
@@ -368,7 +368,7 @@ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type,
return NULL;
INIT_LIST_HEAD(&cb->list);
- cb->file_object = fp;
+ cb->fp = fp;
cb->cl = cl;
cb->buf_idx = 0;
cb->fop_type = type;
@@ -455,7 +455,8 @@ int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length)
* Return: cb on success and NULL on failure
*/
struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
- enum mei_cb_file_ops type, struct file *fp)
+ enum mei_cb_file_ops type,
+ const struct file *fp)
{
struct mei_cl_cb *cb;
@@ -485,7 +486,7 @@ struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp)
struct mei_cl_cb *cb;
list_for_each_entry(cb, &cl->rd_completed, list)
- if (!fp || fp == cb->file_object)
+ if (!fp || fp == cb->fp)
return cb;
return NULL;
@@ -503,12 +504,12 @@ void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp)
struct mei_cl_cb *cb, *next;
list_for_each_entry_safe(cb, next, &cl->rd_completed, list)
- if (!fp || fp == cb->file_object)
+ if (!fp || fp == cb->fp)
mei_io_cb_free(cb);
list_for_each_entry_safe(cb, next, &cl->rd_pending, list)
- if (!fp || fp == cb->file_object)
+ if (!fp || fp == cb->fp)
mei_io_cb_free(cb);
}
@@ -535,7 +536,6 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
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);
- mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl);
mei_cl_read_cb_flush(cl, fp);
@@ -587,27 +587,23 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev)
* mei_cl_link - allocate host id in the host map
*
* @cl: host client
- * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one
*
* Return: 0 on success
* -EINVAL on incorrect values
* -EMFILE if open count exceeded.
*/
-int mei_cl_link(struct mei_cl *cl, int id)
+int mei_cl_link(struct mei_cl *cl)
{
struct mei_device *dev;
long open_handle_count;
+ int id;
if (WARN_ON(!cl || !cl->dev))
return -EINVAL;
dev = cl->dev;
- /* If Id is not assigned get one*/
- if (id == MEI_HOST_CLIENT_ID_ANY)
- id = find_first_zero_bit(dev->host_clients_map,
- MEI_CLIENTS_MAX);
-
+ id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX);
if (id >= MEI_CLIENTS_MAX) {
dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX);
return -EMFILE;
@@ -648,7 +644,7 @@ int mei_cl_unlink(struct mei_cl *cl)
if (!cl)
return 0;
- /* wd and amthif might not be initialized */
+ /* amthif might not be initialized */
if (!cl->dev)
return 0;
@@ -670,31 +666,12 @@ int mei_cl_unlink(struct mei_cl *cl)
return 0;
}
-
-void mei_host_client_init(struct work_struct *work)
+void mei_host_client_init(struct mei_device *dev)
{
- struct mei_device *dev =
- container_of(work, struct mei_device, init_work);
- struct mei_me_client *me_cl;
-
- mutex_lock(&dev->device_lock);
-
-
- me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
- if (me_cl)
- mei_amthif_host_init(dev, me_cl);
- mei_me_cl_put(me_cl);
-
- me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid);
- if (me_cl)
- mei_wd_host_init(dev, me_cl);
- mei_me_cl_put(me_cl);
-
dev->dev_state = MEI_DEV_ENABLED;
dev->reset_count = 0;
- mutex_unlock(&dev->device_lock);
- mei_cl_bus_rescan(dev);
+ schedule_work(&dev->bus_rescan_work);
pm_runtime_mark_last_busy(dev->dev);
dev_dbg(dev->dev, "rpm: autosuspend\n");
@@ -726,6 +703,33 @@ bool mei_hbuf_acquire(struct mei_device *dev)
}
/**
+ * mei_cl_wake_all - wake up readers, writers and event waiters so
+ * they can be interrupted
+ *
+ * @cl: host client
+ */
+static void mei_cl_wake_all(struct mei_cl *cl)
+{
+ struct mei_device *dev = cl->dev;
+
+ /* synchronized under device mutex */
+ if (waitqueue_active(&cl->rx_wait)) {
+ cl_dbg(dev, cl, "Waking up reading client!\n");
+ wake_up_interruptible(&cl->rx_wait);
+ }
+ /* synchronized under device mutex */
+ if (waitqueue_active(&cl->tx_wait)) {
+ cl_dbg(dev, cl, "Waking up writing client!\n");
+ wake_up_interruptible(&cl->tx_wait);
+ }
+ /* synchronized under device mutex */
+ if (waitqueue_active(&cl->ev_wait)) {
+ cl_dbg(dev, cl, "Waking up waiting for event clients!\n");
+ wake_up_interruptible(&cl->ev_wait);
+ }
+}
+
+/**
* mei_cl_set_disconnected - set disconnected state and clear
* associated states and resources
*
@@ -740,8 +744,11 @@ void mei_cl_set_disconnected(struct mei_cl *cl)
return;
cl->state = MEI_FILE_DISCONNECTED;
+ mei_io_list_free(&dev->write_list, cl);
+ mei_io_list_free(&dev->write_waiting_list, cl);
mei_io_list_flush(&dev->ctrl_rd_list, cl);
mei_io_list_flush(&dev->ctrl_wr_list, cl);
+ mei_cl_wake_all(cl);
cl->mei_flow_ctrl_creds = 0;
cl->timer_count = 0;
@@ -1034,7 +1041,7 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
* Return: 0 on success, <0 on failure.
*/
int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
- struct file *file)
+ const struct file *file)
{
struct mei_device *dev;
struct mei_cl_cb *cb;
@@ -1119,11 +1126,10 @@ nortpm:
* mei_cl_alloc_linked - allocate and link host client
*
* @dev: the device structure
- * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one
*
* Return: cl on success ERR_PTR on failure
*/
-struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id)
+struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev)
{
struct mei_cl *cl;
int ret;
@@ -1134,7 +1140,7 @@ struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id)
goto err;
}
- ret = mei_cl_link(cl, id);
+ ret = mei_cl_link(cl);
if (ret)
goto err;
@@ -1149,11 +1155,12 @@ err:
/**
* mei_cl_flow_ctrl_creds - checks flow_control credits for cl.
*
- * @cl: private data of the file object
+ * @cl: host client
+ * @fp: the file pointer associated with the pointer
*
* Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
*/
-int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
+static int mei_cl_flow_ctrl_creds(struct mei_cl *cl, const struct file *fp)
{
int rets;
@@ -1164,7 +1171,7 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
return 1;
if (mei_cl_is_fixed_address(cl)) {
- rets = mei_cl_read_start(cl, mei_cl_mtu(cl), NULL);
+ rets = mei_cl_read_start(cl, mei_cl_mtu(cl), fp);
if (rets && rets != -EBUSY)
return rets;
return 1;
@@ -1186,7 +1193,7 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
* 0 on success
* -EINVAL when ctrl credits are <= 0
*/
-int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
+static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
{
if (WARN_ON(!cl || !cl->me_cl))
return -EINVAL;
@@ -1283,7 +1290,8 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
*
* Return: 0 on such and error otherwise.
*/
-int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request)
+int mei_cl_notify_request(struct mei_cl *cl,
+ const struct file *file, u8 request)
{
struct mei_device *dev;
struct mei_cl_cb *cb;
@@ -1368,12 +1376,12 @@ void mei_cl_notify(struct mei_cl *cl)
cl_dbg(dev, cl, "notify event");
cl->notify_ev = true;
- wake_up_interruptible_all(&cl->ev_wait);
+ if (!mei_cl_bus_notify_event(cl))
+ wake_up_interruptible(&cl->ev_wait);
if (cl->ev_async)
kill_fasync(&cl->ev_async, SIGIO, POLL_PRI);
- mei_cl_bus_notify_event(cl);
}
/**
@@ -1422,6 +1430,25 @@ out:
}
/**
+ * mei_cl_is_read_fc_cb - check if read cb is waiting for flow control
+ * for given host client
+ *
+ * @cl: host client
+ *
+ * Return: true, if found at least one cb.
+ */
+static bool mei_cl_is_read_fc_cb(struct mei_cl *cl)
+{
+ struct mei_device *dev = cl->dev;
+ struct mei_cl_cb *cb;
+
+ list_for_each_entry(cb, &dev->ctrl_wr_list.list, list)
+ if (cb->fop_type == MEI_FOP_READ && cb->cl == cl)
+ return true;
+ return false;
+}
+
+/**
* mei_cl_read_start - the start read client message function.
*
* @cl: host client
@@ -1430,7 +1457,7 @@ out:
*
* Return: 0 on success, <0 on failure.
*/
-int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
+int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
{
struct mei_device *dev;
struct mei_cl_cb *cb;
@@ -1445,7 +1472,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
return -ENODEV;
/* HW currently supports only one pending read */
- if (!list_empty(&cl->rd_pending))
+ if (!list_empty(&cl->rd_pending) || mei_cl_is_read_fc_cb(cl))
return -EBUSY;
if (!mei_me_cl_is_active(cl->me_cl)) {
@@ -1524,7 +1551,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
first_chunk = cb->buf_idx == 0;
- rets = first_chunk ? mei_cl_flow_ctrl_creds(cl) : 1;
+ rets = first_chunk ? mei_cl_flow_ctrl_creds(cl, cb->fp) : 1;
if (rets < 0)
return rets;
@@ -1556,7 +1583,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
return 0;
}
- cl_dbg(dev, cl, "buf: size = %d idx = %lu\n",
+ cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n",
cb->buf.size, cb->buf_idx);
rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
@@ -1618,7 +1645,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(dev->dev);
cl_err(dev, cl, "rpm: get failed %d\n", rets);
- return rets;
+ goto free;
}
cb->buf_idx = 0;
@@ -1630,7 +1657,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
mei_hdr.msg_complete = 0;
mei_hdr.internal = cb->internal;
- rets = mei_cl_flow_ctrl_creds(cl);
+ rets = mei_cl_flow_ctrl_creds(cl, cb->fp);
if (rets < 0)
goto err;
@@ -1677,7 +1704,8 @@ out:
mutex_unlock(&dev->device_lock);
rets = wait_event_interruptible(cl->tx_wait,
- cl->writing_state == MEI_WRITE_COMPLETE);
+ cl->writing_state == MEI_WRITE_COMPLETE ||
+ (!mei_cl_is_connected(cl)));
mutex_lock(&dev->device_lock);
/* wait_event_interruptible returns -ERESTARTSYS */
if (rets) {
@@ -1685,6 +1713,10 @@ out:
rets = -EINTR;
goto err;
}
+ if (cl->writing_state != MEI_WRITE_COMPLETE) {
+ rets = -EFAULT;
+ goto err;
+ }
}
rets = size;
@@ -1692,6 +1724,8 @@ err:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
+free:
+ mei_io_cb_free(cb);
return rets;
}
@@ -1721,10 +1755,8 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
case MEI_FOP_READ:
list_add_tail(&cb->list, &cl->rd_completed);
- if (waitqueue_active(&cl->rx_wait))
- wake_up_interruptible_all(&cl->rx_wait);
- else
- mei_cl_bus_rx_event(cl);
+ if (!mei_cl_bus_rx_event(cl))
+ wake_up_interruptible(&cl->rx_wait);
break;
case MEI_FOP_CONNECT:
@@ -1735,6 +1767,10 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
wake_up(&cl->wait);
break;
+ case MEI_FOP_DISCONNECT_RSP:
+ mei_io_cb_free(cb);
+ mei_cl_set_disconnected(cl);
+ break;
default:
BUG_ON(0);
}
@@ -1753,44 +1789,3 @@ void mei_cl_all_disconnect(struct mei_device *dev)
list_for_each_entry(cl, &dev->file_list, link)
mei_cl_set_disconnected(cl);
}
-
-
-/**
- * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted
- *
- * @dev: mei device
- */
-void mei_cl_all_wakeup(struct mei_device *dev)
-{
- 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);
- }
- if (waitqueue_active(&cl->tx_wait)) {
- cl_dbg(dev, cl, "Waking up writing client!\n");
- wake_up_interruptible(&cl->tx_wait);
- }
-
- /* synchronized under device mutex */
- if (waitqueue_active(&cl->ev_wait)) {
- cl_dbg(dev, cl, "Waking up waiting for event clients!\n");
- wake_up_interruptible(&cl->ev_wait);
- }
- }
-}
-
-/**
- * mei_cl_all_write_clear - clear all pending writes
- *
- * @dev: mei device
- */
-void mei_cl_all_write_clear(struct mei_device *dev)
-{
- mei_io_list_free(&dev->write_list, NULL);
- mei_io_list_free(&dev->write_waiting_list, NULL);
-}
-
-
diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h
index 04e1aa392..0d7a3a1fe 100644
--- a/drivers/misc/mei/client.h
+++ b/drivers/misc/mei/client.h
@@ -18,7 +18,6 @@
#define _MEI_CLIENT_H_
#include <linux/types.h>
-#include <linux/watchdog.h>
#include <linux/poll.h>
#include <linux/mei.h>
@@ -84,7 +83,7 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl)
* MEI IO Functions
*/
struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type,
- struct file *fp);
+ const struct file *fp);
void mei_io_cb_free(struct mei_cl_cb *priv_cb);
int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length);
@@ -108,21 +107,19 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev);
void mei_cl_init(struct mei_cl *cl, struct mei_device *dev);
-int mei_cl_link(struct mei_cl *cl, int id);
+int mei_cl_link(struct mei_cl *cl);
int mei_cl_unlink(struct mei_cl *cl);
-struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id);
+struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev);
struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl,
const struct file *fp);
void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp);
struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
- enum mei_cb_file_ops type, struct file *fp);
+ enum mei_cb_file_ops type,
+ const struct file *fp);
int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp);
-int mei_cl_flow_ctrl_creds(struct mei_cl *cl);
-
-int mei_cl_flow_ctrl_reduce(struct mei_cl *cl);
/*
* MEI input output function prototype
*/
@@ -217,10 +214,10 @@ void mei_cl_set_disconnected(struct mei_cl *cl);
int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list);
int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
- struct file *file);
+ const struct file *file);
int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list);
-int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp);
+int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp);
int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr,
struct mei_cl_cb *cmpl_list);
int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking);
@@ -229,19 +226,18 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb);
-void mei_host_client_init(struct work_struct *work);
+void mei_host_client_init(struct mei_device *dev);
u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop);
enum mei_cb_file_ops mei_cl_notify_req2fop(u8 request);
-int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request);
+int mei_cl_notify_request(struct mei_cl *cl,
+ const struct file *file, u8 request);
int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list);
int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev);
void mei_cl_notify(struct mei_cl *cl);
void mei_cl_all_disconnect(struct mei_device *dev);
-void mei_cl_all_wakeup(struct mei_device *dev);
-void mei_cl_all_write_clear(struct mei_device *dev);
#define MEI_CL_FMT "cl:host=%02d me=%02d "
#define MEI_CL_PRM(cl) (cl)->host_client_id, mei_cl_me_id(cl)
@@ -249,6 +245,9 @@ void mei_cl_all_write_clear(struct mei_device *dev);
#define cl_dbg(dev, cl, format, arg...) \
dev_dbg((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg)
+#define cl_warn(dev, cl, format, arg...) \
+ dev_warn((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg)
+
#define cl_err(dev, cl, format, arg...) \
dev_err((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg)
diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c
index a138d8a27..c6c051b52 100644
--- a/drivers/misc/mei/debugfs.c
+++ b/drivers/misc/mei/debugfs.c
@@ -50,6 +50,7 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
}
pos += scnprintf(buf + pos, bufsz - pos, HDR);
+#undef HDR
/* if the driver is not enabled the list won't be consistent */
if (dev->dev_state != MEI_DEV_ENABLED)
@@ -90,23 +91,37 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
{
struct mei_device *dev = fp->private_data;
struct mei_cl *cl;
- const size_t bufsz = 1024;
+ size_t bufsz = 1;
char *buf;
int i = 0;
int pos = 0;
int ret;
+#define HDR " |me|host|state|rd|wr|\n"
+
if (!dev)
return -ENODEV;
+ mutex_lock(&dev->device_lock);
+
+ /*
+ * if the driver is not enabled the list won't be consistent,
+ * we output empty table
+ */
+ if (dev->dev_state == MEI_DEV_ENABLED)
+ list_for_each_entry(cl, &dev->file_list, link)
+ bufsz++;
+
+ bufsz *= sizeof(HDR) + 1;
+
buf = kzalloc(bufsz, GFP_KERNEL);
- if (!buf)
+ if (!buf) {
+ mutex_unlock(&dev->device_lock);
return -ENOMEM;
+ }
- pos += scnprintf(buf + pos, bufsz - pos,
- " |me|host|state|rd|wr|\n");
-
- mutex_lock(&dev->device_lock);
+ pos += scnprintf(buf + pos, bufsz - pos, HDR);
+#undef HDR
/* if the driver is not enabled the list won't be consistent */
if (dev->dev_state != MEI_DEV_ENABLED)
@@ -115,7 +130,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
list_for_each_entry(cl, &dev->file_list, link) {
pos += scnprintf(buf + pos, bufsz - pos,
- "%2d|%2d|%4d|%5d|%2d|%2d|\n",
+ "%3d|%2d|%4d|%5d|%2d|%2d|\n",
i, mei_cl_me_id(cl), cl->host_client_id, cl->state,
!list_empty(&cl->rd_completed), cl->writing_state);
i++;
@@ -150,16 +165,21 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf,
pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n",
mei_hbm_state_str(dev->hbm_state));
- if (dev->hbm_state == MEI_HBM_STARTED) {
+ if (dev->hbm_state >= MEI_HBM_ENUM_CLIENTS &&
+ dev->hbm_state <= MEI_HBM_STARTED) {
pos += scnprintf(buf + pos, bufsz - pos, "hbm features:\n");
pos += scnprintf(buf + pos, bufsz - pos, "\tPG: %01d\n",
dev->hbm_f_pg_supported);
pos += scnprintf(buf + pos, bufsz - pos, "\tDC: %01d\n",
dev->hbm_f_dc_supported);
+ pos += scnprintf(buf + pos, bufsz - pos, "\tIE: %01d\n",
+ dev->hbm_f_ie_supported);
pos += scnprintf(buf + pos, bufsz - pos, "\tDOT: %01d\n",
dev->hbm_f_dot_supported);
pos += scnprintf(buf + pos, bufsz - pos, "\tEV: %01d\n",
dev->hbm_f_ev_supported);
+ pos += scnprintf(buf + pos, bufsz - pos, "\tFA: %01d\n",
+ dev->hbm_f_fa_supported);
}
pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n",
@@ -175,6 +195,30 @@ static const struct file_operations mei_dbgfs_fops_devstate = {
.llseek = generic_file_llseek,
};
+static ssize_t mei_dbgfs_write_allow_fa(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct mei_device *dev;
+ int ret;
+
+ dev = container_of(file->private_data,
+ struct mei_device, allow_fixed_address);
+
+ ret = debugfs_write_file_bool(file, user_buf, count, ppos);
+ if (ret < 0)
+ return ret;
+ dev->override_fixed_address = true;
+ return ret;
+}
+
+static const struct file_operations mei_dbgfs_fops_allow_fa = {
+ .open = simple_open,
+ .read = debugfs_read_file_bool,
+ .write = mei_dbgfs_write_allow_fa,
+ .llseek = generic_file_llseek,
+};
+
/**
* mei_dbgfs_deregister - Remove the debugfs files and directories
*
@@ -224,8 +268,9 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name)
dev_err(dev->dev, "devstate: registration failed\n");
goto err;
}
- f = debugfs_create_bool("allow_fixed_address", S_IRUSR | S_IWUSR, dir,
- &dev->allow_fixed_address);
+ f = debugfs_create_file("allow_fixed_address", S_IRUSR | S_IWUSR, dir,
+ &dev->allow_fixed_address,
+ &mei_dbgfs_fops_allow_fa);
if (!f) {
dev_err(dev->dev, "allow_fixed_address: registration failed\n");
goto err;
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index e7b7aad09..8fe1ef821 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -301,7 +301,10 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev)
enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
memset(enum_req, 0, len);
enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
- enum_req->allow_add = dev->hbm_f_dc_supported;
+ enum_req->flags |= dev->hbm_f_dc_supported ?
+ MEI_HBM_ENUM_F_ALLOW_ADD : 0;
+ enum_req->flags |= dev->hbm_f_ie_supported ?
+ MEI_HBM_ENUM_F_IMMEDIATE_ENUM : 0;
ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
if (ret) {
@@ -401,6 +404,9 @@ static int mei_hbm_fw_add_cl_req(struct mei_device *dev,
if (ret)
status = !MEI_HBMS_SUCCESS;
+ if (dev->dev_state == MEI_DEV_ENABLED)
+ schedule_work(&dev->bus_rescan_work);
+
return mei_hbm_add_cl_resp(dev, req->me_addr, status);
}
@@ -543,7 +549,7 @@ static int mei_hbm_prop_req(struct mei_device *dev)
/* We got all client properties */
if (next_client_index == MEI_CLIENTS_MAX) {
dev->hbm_state = MEI_HBM_STARTED;
- schedule_work(&dev->init_work);
+ mei_host_client_init(dev);
return 0;
}
@@ -789,8 +795,11 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, struct mei_cl *cl,
cl->state = MEI_FILE_CONNECTED;
else {
cl->state = MEI_FILE_DISCONNECT_REPLY;
- if (rs->status == MEI_CL_CONN_NOT_FOUND)
+ if (rs->status == MEI_CL_CONN_NOT_FOUND) {
mei_me_cl_del(dev, cl->me_cl);
+ if (dev->dev_state == MEI_DEV_ENABLED)
+ schedule_work(&dev->bus_rescan_work);
+ }
}
cl->status = mei_cl_conn_status_to_errno(rs->status);
}
@@ -866,15 +875,14 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req);
if (cl) {
- cl_dbg(dev, cl, "fw disconnect request received\n");
+ cl_warn(dev, cl, "fw disconnect request received\n");
cl->state = MEI_FILE_DISCONNECTING;
cl->timer_count = 0;
cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL);
if (!cb)
return -ENOMEM;
- cl_dbg(dev, cl, "add disconnect response as first\n");
- list_add(&cb->list, &dev->ctrl_wr_list.list);
+ list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
}
return 0;
}
@@ -972,6 +980,9 @@ static void mei_hbm_config_features(struct mei_device *dev)
if (dev->version.major_version >= HBM_MAJOR_VERSION_DC)
dev->hbm_f_dc_supported = 1;
+ if (dev->version.major_version >= HBM_MAJOR_VERSION_IE)
+ dev->hbm_f_ie_supported = 1;
+
/* disconnect on connect timeout instead of link reset */
if (dev->version.major_version >= HBM_MAJOR_VERSION_DOT)
dev->hbm_f_dot_supported = 1;
@@ -979,6 +990,10 @@ static void mei_hbm_config_features(struct mei_device *dev)
/* Notification Event Support */
if (dev->version.major_version >= HBM_MAJOR_VERSION_EV)
dev->hbm_f_ev_supported = 1;
+
+ /* Fixed Address Client Support */
+ if (dev->version.major_version >= HBM_MAJOR_VERSION_FA)
+ dev->hbm_f_fa_supported = 1;
}
/**
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index a8a68acd3..0dcb854b4 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -121,6 +121,10 @@
#define MEI_DEV_ID_SPT_2 0x9D3B /* Sunrise Point 2 */
#define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */
#define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */
+
+#define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */
+#define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */
+
/*
* MEI HW Section
*/
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 25b1997a6..e2fb44cc5 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -189,8 +189,11 @@ static int mei_me_fw_status(struct mei_device *dev,
fw_status->count = fw_src->count;
for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) {
- ret = pci_read_config_dword(pdev,
- fw_src->status[i], &fw_status->status[i]);
+ ret = pci_read_config_dword(pdev, fw_src->status[i],
+ &fw_status->status[i]);
+ trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HSF_X",
+ fw_src->status[i],
+ fw_status->status[i]);
if (ret)
return ret;
}
@@ -215,6 +218,7 @@ static void mei_me_hw_config(struct mei_device *dev)
reg = 0;
pci_read_config_dword(pdev, PCI_CFG_HFS_1, &reg);
+ trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg);
hw->d0i3_supported =
((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK);
@@ -1248,6 +1252,7 @@ static bool mei_me_fw_type_nm(struct pci_dev *pdev)
u32 reg;
pci_read_config_dword(pdev, PCI_CFG_HFS_2, &reg);
+ trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg);
/* make sure that bit 9 (NM) is up and bit 10 (DM) is down */
return (reg & 0x600) == 0x200;
}
@@ -1260,6 +1265,7 @@ static bool mei_me_fw_type_sps(struct pci_dev *pdev)
u32 reg;
/* Read ME FW Status check for SPS Firmware */
pci_read_config_dword(pdev, PCI_CFG_HFS_1, &reg);
+ trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg);
/* if bits [19:16] = 15, running SPS Firmware */
return (reg & 0xf0000) == 0xf0000;
}
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c
index bae680c64..4a6c1b85f 100644
--- a/drivers/misc/mei/hw-txe.c
+++ b/drivers/misc/mei/hw-txe.c
@@ -28,6 +28,9 @@
#include "client.h"
#include "hbm.h"
+#include "mei-trace.h"
+
+
/**
* mei_txe_reg_read - Reads 32bit data from the txe device
*
@@ -640,8 +643,11 @@ static int mei_txe_fw_status(struct mei_device *dev,
fw_status->count = fw_src->count;
for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) {
- ret = pci_read_config_dword(pdev,
- fw_src->status[i], &fw_status->status[i]);
+ ret = pci_read_config_dword(pdev, fw_src->status[i],
+ &fw_status->status[i]);
+ trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HSF_X",
+ fw_src->status[i],
+ fw_status->status[i]);
if (ret)
return ret;
}
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index 4cebde859..9daf3f9ae 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -29,7 +29,6 @@
#define MEI_CLIENTS_INIT_TIMEOUT 15 /* HPS: Clients Enumeration Timeout */
#define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */
-#define MEI_IAMTHIF_READ_TIMER 10 /* HPS */
#define MEI_PGI_TIMEOUT 1 /* PG Isolation time response 1 sec */
#define MEI_D0I3_TIMEOUT 5 /* D0i3 set/unset max response time */
@@ -54,6 +53,12 @@
#define HBM_MAJOR_VERSION_DC 2
/*
+ * MEI version with immediate reply to enum request support
+ */
+#define HBM_MINOR_VERSION_IE 0
+#define HBM_MAJOR_VERSION_IE 2
+
+/*
* MEI version with disconnect on connection timeout support
*/
#define HBM_MINOR_VERSION_DOT 0
@@ -65,6 +70,12 @@
#define HBM_MINOR_VERSION_EV 0
#define HBM_MAJOR_VERSION_EV 2
+/*
+ * MEI version with fixed address client support
+ */
+#define HBM_MINOR_VERSION_FA 0
+#define HBM_MAJOR_VERSION_FA 2
+
/* Host bus message command opcode */
#define MEI_HBM_CMD_OP_MSK 0x7f
/* Host bus message command RESPONSE */
@@ -241,15 +252,26 @@ struct hbm_me_stop_request {
} __packed;
/**
- * struct hbm_host_enum_request - enumeration request from host to fw
+ * enum hbm_host_enum_flags - enumeration request flags (HBM version >= 2.0)
*
- * @hbm_cmd: bus message command header
- * @allow_add: allow dynamic clients add HBM version >= 2.0
+ * @MEI_HBM_ENUM_F_ALLOW_ADD: allow dynamic clients add
+ * @MEI_HBM_ENUM_F_IMMEDIATE_ENUM: allow FW to send answer immediately
+ */
+enum hbm_host_enum_flags {
+ MEI_HBM_ENUM_F_ALLOW_ADD = BIT(0),
+ MEI_HBM_ENUM_F_IMMEDIATE_ENUM = BIT(1),
+};
+
+/**
+ * struct hbm_host_enum_request - enumeration request from host to fw
+ *
+ * @hbm_cmd : bus message command header
+ * @flags : request flags
* @reserved: reserved
*/
struct hbm_host_enum_request {
u8 hbm_cmd;
- u8 allow_add;
+ u8 flags;
u8 reserved[2];
} __packed;
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index 3edafc8d3..f7c8dfdb6 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -91,8 +91,8 @@ EXPORT_SYMBOL_GPL(mei_fw_status2str);
*/
void mei_cancel_work(struct mei_device *dev)
{
- cancel_work_sync(&dev->init_work);
cancel_work_sync(&dev->reset_work);
+ cancel_work_sync(&dev->bus_rescan_work);
cancel_delayed_work(&dev->timer_work);
}
@@ -148,16 +148,10 @@ int mei_reset(struct mei_device *dev)
state != MEI_DEV_POWER_UP) {
/* remove all waiting requests */
- mei_cl_all_write_clear(dev);
-
mei_cl_all_disconnect(dev);
- /* wake up all readers and writers so they can be interrupted */
- mei_cl_all_wakeup(dev);
-
/* remove entry if already in list */
- dev_dbg(dev->dev, "remove iamthif and wd from the file list.\n");
- mei_cl_unlink(&dev->wd_cl);
+ dev_dbg(dev->dev, "remove iamthif from the file list.\n");
mei_cl_unlink(&dev->iamthif_cl);
mei_amthif_reset_params(dev);
}
@@ -165,7 +159,6 @@ int mei_reset(struct mei_device *dev)
mei_hbm_reset(dev);
dev->rd_msg_hdr = 0;
- dev->wd_pending = false;
if (ret) {
dev_err(dev->dev, "hw_reset failed ret = %d\n", ret);
@@ -335,16 +328,12 @@ void mei_stop(struct mei_device *dev)
mutex_lock(&dev->device_lock);
- mei_wd_stop(dev);
-
dev->dev_state = MEI_DEV_POWER_DOWN;
mei_reset(dev);
/* move device to disabled state unconditionally */
dev->dev_state = MEI_DEV_DISABLED;
mutex_unlock(&dev->device_lock);
-
- mei_watchdog_unregister(dev);
}
EXPORT_SYMBOL_GPL(mei_stop);
@@ -394,7 +383,6 @@ void mei_device_init(struct mei_device *dev,
init_waitqueue_head(&dev->wait_hw_ready);
init_waitqueue_head(&dev->wait_pg);
init_waitqueue_head(&dev->wait_hbm_start);
- init_waitqueue_head(&dev->wait_stop_wd);
dev->dev_state = MEI_DEV_INITIALIZING;
dev->reset_count = 0;
@@ -404,13 +392,11 @@ void mei_device_init(struct mei_device *dev,
mei_io_list_init(&dev->ctrl_rd_list);
INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
- INIT_WORK(&dev->init_work, mei_host_client_init);
INIT_WORK(&dev->reset_work, mei_reset_work);
+ INIT_WORK(&dev->bus_rescan_work, mei_cl_bus_rescan_work);
- INIT_LIST_HEAD(&dev->wd_cl.link);
INIT_LIST_HEAD(&dev->iamthif_cl.link);
mei_io_list_init(&dev->amthif_cmd_list);
- mei_io_list_init(&dev->amthif_rd_complete_list);
bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
dev->open_handle_count = 0;
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index 64b568a02..3831a7ba2 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -48,7 +48,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list)
dev_dbg(dev->dev, "completing call back.\n");
if (cl == &dev->iamthif_cl)
- mei_amthif_complete(dev, cb);
+ mei_amthif_complete(cl, cb);
else
mei_cl_complete(cl, cb);
}
@@ -76,7 +76,6 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl,
* @dev: mei device
* @hdr: message header
*/
-static inline
void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr)
{
/*
@@ -104,6 +103,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb;
unsigned char *buffer = NULL;
+ size_t buf_sz;
cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
if (!cb) {
@@ -124,11 +124,21 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
goto out;
}
- if (cb->buf.size < mei_hdr->length + cb->buf_idx) {
- cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n",
+ buf_sz = mei_hdr->length + cb->buf_idx;
+ /* catch for integer overflow */
+ if (buf_sz < cb->buf_idx) {
+ cl_err(dev, cl, "message is too big len %d idx %zu\n",
+ mei_hdr->length, cb->buf_idx);
+
+ list_move_tail(&cb->list, &complete_list->list);
+ cb->status = -EMSGSIZE;
+ goto out;
+ }
+
+ if (cb->buf.size < buf_sz) {
+ cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n",
cb->buf.size, mei_hdr->length, cb->buf_idx);
- buffer = krealloc(cb->buf.data, mei_hdr->length + cb->buf_idx,
- GFP_KERNEL);
+ buffer = krealloc(cb->buf.data, buf_sz, GFP_KERNEL);
if (!buffer) {
cb->status = -ENOMEM;
@@ -136,7 +146,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
goto out;
}
cb->buf.data = buffer;
- cb->buf.size = mei_hdr->length + cb->buf_idx;
+ cb->buf.size = buf_sz;
}
buffer = cb->buf.data + cb->buf_idx;
@@ -145,8 +155,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
cb->buf_idx += mei_hdr->length;
if (mei_hdr->msg_complete) {
- cb->read_time = jiffies;
- cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx);
+ cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx);
list_move_tail(&cb->list, &complete_list->list);
} else {
pm_runtime_mark_last_busy(dev->dev);
@@ -184,10 +193,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
return -EMSGSIZE;
ret = mei_hbm_cl_disconnect_rsp(dev, cl);
- mei_cl_set_disconnected(cl);
- mei_io_cb_free(cb);
- mei_me_cl_put(cl->me_cl);
- cl->me_cl = NULL;
+ list_move_tail(&cb->list, &cmpl_list->list);
return ret;
}
@@ -229,6 +235,16 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
return 0;
}
+static inline bool hdr_is_hbm(struct mei_msg_hdr *mei_hdr)
+{
+ return mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0;
+}
+
+static inline bool hdr_is_fixed(struct mei_msg_hdr *mei_hdr)
+{
+ return mei_hdr->host_addr == 0 && mei_hdr->me_addr != 0;
+}
+
/**
* mei_irq_read_handler - bottom half read routine after ISR to
* handle the read processing.
@@ -270,7 +286,7 @@ int mei_irq_read_handler(struct mei_device *dev,
}
/* HBM message */
- if (mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0) {
+ if (hdr_is_hbm(mei_hdr)) {
ret = mei_hbm_dispatch(dev, mei_hdr);
if (ret) {
dev_dbg(dev->dev, "mei_hbm_dispatch failed ret = %d\n",
@@ -290,6 +306,14 @@ int mei_irq_read_handler(struct mei_device *dev,
/* if no recipient cl was found we assume corrupted header */
if (&cl->link == &dev->file_list) {
+ /* A message for not connected fixed address clients
+ * should be silently discarded
+ */
+ if (hdr_is_fixed(mei_hdr)) {
+ mei_irq_discard_msg(dev, mei_hdr);
+ ret = 0;
+ goto reset_slots;
+ }
dev_err(dev->dev, "no destination client found 0x%08X\n",
dev->rd_msg_hdr);
ret = -EBADMSG;
@@ -360,21 +384,6 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
list_move_tail(&cb->list, &cmpl_list->list);
}
- if (dev->wd_state == MEI_WD_STOPPING) {
- dev->wd_state = MEI_WD_IDLE;
- wake_up(&dev->wait_stop_wd);
- }
-
- if (mei_cl_is_connected(&dev->wd_cl)) {
- if (dev->wd_pending &&
- mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) {
- ret = mei_wd_send(dev);
- if (ret)
- return ret;
- dev->wd_pending = false;
- }
- }
-
/* complete control write list CB */
dev_dbg(dev->dev, "complete control write list cb.\n");
list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) {
@@ -462,7 +471,6 @@ static void mei_connect_timeout(struct mei_cl *cl)
*/
void mei_timer(struct work_struct *work)
{
- unsigned long timeout;
struct mei_cl *cl;
struct mei_device *dev = container_of(work,
@@ -508,45 +516,15 @@ void mei_timer(struct work_struct *work)
mei_reset(dev);
dev->iamthif_canceled = false;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
- dev->iamthif_timer = 0;
mei_io_cb_free(dev->iamthif_current_cb);
dev->iamthif_current_cb = NULL;
- dev->iamthif_file_object = NULL;
+ dev->iamthif_fp = NULL;
mei_amthif_run_next_cmd(dev);
}
}
- if (dev->iamthif_timer) {
-
- timeout = dev->iamthif_timer +
- mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
-
- dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n",
- dev->iamthif_timer);
- dev_dbg(dev->dev, "timeout = %ld\n", timeout);
- dev_dbg(dev->dev, "jiffies = %ld\n", jiffies);
- if (time_after(jiffies, timeout)) {
- /*
- * User didn't read the AMTHI data on time (15sec)
- * freeing AMTHI for other requests
- */
-
- dev_dbg(dev->dev, "freeing AMTHI for other requests\n");
-
- mei_io_list_flush(&dev->amthif_rd_complete_list,
- &dev->iamthif_cl);
- mei_io_cb_free(dev->iamthif_current_cb);
- dev->iamthif_current_cb = NULL;
-
- dev->iamthif_file_object->private_data = NULL;
- dev->iamthif_file_object = NULL;
- dev->iamthif_timer = 0;
- mei_amthif_run_next_cmd(dev);
-
- }
- }
out:
if (dev->dev_state != MEI_DEV_DISABLED)
schedule_delayed_work(&dev->timer_work, 2 * HZ);
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 80f9afcb1..52635b063 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -65,7 +65,7 @@ static int mei_open(struct inode *inode, struct file *file)
goto err_unlock;
}
- cl = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY);
+ cl = mei_cl_alloc_linked(dev);
if (IS_ERR(cl)) {
err = PTR_ERR(cl);
goto err_unlock;
@@ -159,27 +159,22 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
goto out;
}
+ if (ubuf == NULL) {
+ rets = -EMSGSIZE;
+ goto out;
+ }
+
if (cl == &dev->iamthif_cl) {
rets = mei_amthif_read(dev, file, ubuf, length, offset);
goto out;
}
cb = mei_cl_read_cb(cl, file);
- if (cb) {
- /* read what left */
- if (cb->buf_idx > *offset)
- goto copy_buffer;
- /* offset is beyond buf_idx we have no more data return 0 */
- if (cb->buf_idx > 0 && cb->buf_idx <= *offset) {
- rets = 0;
- goto free;
- }
- /* Offset needs to be cleaned for contiguous reads*/
- if (cb->buf_idx == 0 && *offset > 0)
- *offset = 0;
- } else if (*offset > 0) {
+ if (cb)
+ goto copy_buffer;
+
+ if (*offset > 0)
*offset = 0;
- }
err = mei_cl_read_start(cl, length, file);
if (err && err != -EBUSY) {
@@ -214,11 +209,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
cb = mei_cl_read_cb(cl, file);
if (!cb) {
- if (mei_cl_is_fixed_address(cl) && dev->allow_fixed_address) {
- cb = mei_cl_read_cb(cl, NULL);
- if (cb)
- goto copy_buffer;
- }
rets = 0;
goto out;
}
@@ -231,10 +221,10 @@ copy_buffer:
goto free;
}
- cl_dbg(dev, cl, "buf.size = %d buf.idx = %ld\n",
- cb->buf.size, cb->buf_idx);
- if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) {
- rets = -EMSGSIZE;
+ cl_dbg(dev, cl, "buf.size = %zu buf.idx = %zu offset = %lld\n",
+ cb->buf.size, cb->buf_idx, *offset);
+ if (*offset >= cb->buf_idx) {
+ rets = 0;
goto free;
}
@@ -250,11 +240,13 @@ copy_buffer:
rets = length;
*offset += length;
- if ((unsigned long)*offset < cb->buf_idx)
+ /* not all data was read, keep the cb */
+ if (*offset < cb->buf_idx)
goto out;
free:
mei_io_cb_free(cb);
+ *offset = 0;
out:
cl_dbg(dev, cl, "end mei read rets = %d\n", rets);
@@ -275,9 +267,8 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
size_t length, loff_t *offset)
{
struct mei_cl *cl = file->private_data;
- struct mei_cl_cb *write_cb = NULL;
+ struct mei_cl_cb *cb;
struct mei_device *dev;
- unsigned long timeout = 0;
int rets;
if (WARN_ON(!cl || !cl->dev))
@@ -313,52 +304,31 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
goto out;
}
- if (cl == &dev->iamthif_cl) {
- write_cb = mei_amthif_find_read_list_entry(dev, file);
-
- if (write_cb) {
- timeout = write_cb->read_time +
- mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
-
- if (time_after(jiffies, timeout)) {
- *offset = 0;
- mei_io_cb_free(write_cb);
- write_cb = NULL;
- }
- }
- }
-
*offset = 0;
- write_cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file);
- if (!write_cb) {
+ cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file);
+ if (!cb) {
rets = -ENOMEM;
goto out;
}
- rets = copy_from_user(write_cb->buf.data, ubuf, length);
+ rets = copy_from_user(cb->buf.data, ubuf, length);
if (rets) {
dev_dbg(dev->dev, "failed to copy data from userland\n");
rets = -EFAULT;
+ mei_io_cb_free(cb);
goto out;
}
if (cl == &dev->iamthif_cl) {
- rets = mei_amthif_write(cl, write_cb);
-
- if (rets) {
- dev_err(dev->dev,
- "amthif write failed with status = %d\n", rets);
- goto out;
- }
- mutex_unlock(&dev->device_lock);
- return length;
+ rets = mei_amthif_write(cl, cb);
+ if (!rets)
+ rets = length;
+ goto out;
}
- rets = mei_cl_write(cl, write_cb, false);
+ rets = mei_cl_write(cl, cb, false);
out:
mutex_unlock(&dev->device_lock);
- if (rets < 0)
- mei_io_cb_free(write_cb);
return rets;
}
@@ -393,12 +363,22 @@ static int mei_ioctl_connect_client(struct file *file,
/* find ME client we're trying to connect to */
me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid);
- if (!me_cl ||
- (me_cl->props.fixed_address && !dev->allow_fixed_address)) {
+ if (!me_cl) {
dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n",
&data->in_client_uuid);
- mei_me_cl_put(me_cl);
- return -ENOTTY;
+ rets = -ENOTTY;
+ goto end;
+ }
+
+ if (me_cl->props.fixed_address) {
+ bool forbidden = dev->override_fixed_address ?
+ !dev->allow_fixed_address : !dev->hbm_f_fa_supported;
+ if (forbidden) {
+ dev_dbg(dev->dev, "Connection forbidden to FW Client UUID = %pUl\n",
+ &data->in_client_uuid);
+ rets = -ENOTTY;
+ goto end;
+ }
}
dev_dbg(dev->dev, "Connect to FW Client ID = %d\n",
@@ -454,7 +434,7 @@ end:
*
* Return: 0 on success , <0 on error
*/
-static int mei_ioctl_client_notify_request(struct file *file, u32 request)
+static int mei_ioctl_client_notify_request(const struct file *file, u32 request)
{
struct mei_cl *cl = file->private_data;
@@ -473,7 +453,7 @@ static int mei_ioctl_client_notify_request(struct file *file, u32 request)
*
* Return: 0 on success , <0 on error
*/
-static int mei_ioctl_client_notify_get(struct file *file, u32 *notify_get)
+static int mei_ioctl_client_notify_get(const struct file *file, u32 *notify_get)
{
struct mei_cl *cl = file->private_data;
bool notify_ev;
diff --git a/drivers/misc/mei/mei-trace.c b/drivers/misc/mei/mei-trace.c
index 388efb519..e19e6acb1 100644
--- a/drivers/misc/mei/mei-trace.c
+++ b/drivers/misc/mei/mei-trace.c
@@ -22,4 +22,6 @@
EXPORT_TRACEPOINT_SYMBOL(mei_reg_read);
EXPORT_TRACEPOINT_SYMBOL(mei_reg_write);
+EXPORT_TRACEPOINT_SYMBOL(mei_pci_cfg_read);
+EXPORT_TRACEPOINT_SYMBOL(mei_pci_cfg_write);
#endif /* __CHECKER__ */
diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h
index 47e1bc655..7d2d5d4a1 100644
--- a/drivers/misc/mei/mei-trace.h
+++ b/drivers/misc/mei/mei-trace.h
@@ -60,7 +60,45 @@ TRACE_EVENT(mei_reg_write,
__entry->offs = offs;
__entry->val = val;
),
- TP_printk("[%s] write %s[%#x] = %#x)",
+ TP_printk("[%s] write %s[%#x] = %#x",
+ __get_str(dev), __entry->reg, __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(mei_pci_cfg_read,
+ TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val),
+ TP_ARGS(dev, reg, offs, val),
+ TP_STRUCT__entry(
+ __string(dev, dev_name(dev))
+ __field(const char *, reg)
+ __field(u32, offs)
+ __field(u32, val)
+ ),
+ TP_fast_assign(
+ __assign_str(dev, dev_name(dev))
+ __entry->reg = reg;
+ __entry->offs = offs;
+ __entry->val = val;
+ ),
+ TP_printk("[%s] pci cfg read %s:[%#x] = %#x",
+ __get_str(dev), __entry->reg, __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(mei_pci_cfg_write,
+ TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val),
+ TP_ARGS(dev, reg, offs, val),
+ TP_STRUCT__entry(
+ __string(dev, dev_name(dev))
+ __field(const char *, reg)
+ __field(u32, offs)
+ __field(u32, val)
+ ),
+ TP_fast_assign(
+ __assign_str(dev, dev_name(dev))
+ __entry->reg = reg;
+ __entry->offs = offs;
+ __entry->val = val;
+ ),
+ TP_printk("[%s] pci cfg write %s[%#x] = %#x",
__get_str(dev), __entry->reg, __entry->offs, __entry->val)
);
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 4250555d5..859bdac22 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -18,7 +18,7 @@
#define _MEI_DEV_H_
#include <linux/types.h>
-#include <linux/watchdog.h>
+#include <linux/cdev.h>
#include <linux/poll.h>
#include <linux/mei.h>
#include <linux/mei_cl_bus.h>
@@ -26,33 +26,13 @@
#include "hw.h"
#include "hbm.h"
-/*
- * watch dog definition
- */
-#define MEI_WD_HDR_SIZE 4
-#define MEI_WD_STOP_MSG_SIZE MEI_WD_HDR_SIZE
-#define MEI_WD_START_MSG_SIZE (MEI_WD_HDR_SIZE + 16)
-
-#define MEI_WD_DEFAULT_TIMEOUT 120 /* seconds */
-#define MEI_WD_MIN_TIMEOUT 120 /* seconds */
-#define MEI_WD_MAX_TIMEOUT 65535 /* seconds */
-
-#define MEI_WD_STOP_TIMEOUT 10 /* msecs */
-
-#define MEI_WD_STATE_INDEPENDENCE_MSG_SENT (1 << 0)
-
-#define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32))
-
/*
* AMTHI Client UUID
*/
extern const uuid_le mei_amthif_guid;
-/*
- * Watchdog Client UUID
- */
-extern const uuid_le mei_wd_guid;
+#define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32))
/*
* Number of Maximum MEI Clients
@@ -73,15 +53,6 @@ extern const uuid_le mei_wd_guid;
*/
#define MEI_MAX_OPEN_HANDLE_COUNT (MEI_CLIENTS_MAX - 1)
-/*
- * Internal Clients Number
- */
-#define MEI_HOST_CLIENT_ID_ANY (-1)
-#define MEI_HBM_HOST_CLIENT_ID 0 /* not used, just for documentation */
-#define MEI_WD_HOST_CLIENT_ID 1
-#define MEI_IAMTHIF_HOST_CLIENT_ID 2
-
-
/* File state */
enum file_state {
MEI_FILE_INITIALIZING = 0,
@@ -123,12 +94,6 @@ enum mei_file_transaction_states {
MEI_READ_COMPLETE
};
-enum mei_wd_states {
- MEI_WD_IDLE,
- MEI_WD_RUNNING,
- MEI_WD_STOPPING,
-};
-
/**
* enum mei_cb_file_ops - file operation associated with the callback
* @MEI_FOP_READ: read
@@ -153,7 +118,7 @@ enum mei_cb_file_ops {
* Intel MEI message data struct
*/
struct mei_msg_data {
- u32 size;
+ size_t size;
unsigned char *data;
};
@@ -206,8 +171,7 @@ struct mei_cl;
* @fop_type: file operation type
* @buf: buffer for data associated with the callback
* @buf_idx: last read index
- * @read_time: last read operation time stamp (iamthif)
- * @file_object: pointer to file structure
+ * @fp: pointer to file structure
* @status: io status of the cb
* @internal: communication between driver and FW flag
* @completed: the transfer or reception has completed
@@ -217,9 +181,8 @@ struct mei_cl_cb {
struct mei_cl *cl;
enum mei_cb_file_ops fop_type;
struct mei_msg_data buf;
- unsigned long buf_idx;
- unsigned long read_time;
- struct file *file_object;
+ size_t buf_idx;
+ const struct file *fp;
int status;
u32 internal:1;
u32 completed:1;
@@ -341,12 +304,13 @@ struct mei_hw_ops {
/* MEI bus API*/
void mei_cl_bus_rescan(struct mei_device *bus);
+void mei_cl_bus_rescan_work(struct work_struct *work);
void mei_cl_bus_dev_fixup(struct mei_cl_device *dev);
ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
bool blocking);
ssize_t __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_notify_event(struct mei_cl *cl);
+bool mei_cl_bus_rx_event(struct mei_cl *cl);
+bool mei_cl_bus_notify_event(struct mei_cl *cl);
void mei_cl_bus_remove_devices(struct mei_device *bus);
int mei_cl_bus_init(void);
void mei_cl_bus_exit(void);
@@ -404,7 +368,6 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @wait_hw_ready : wait queue for receive HW ready message form FW
* @wait_pg : wait queue for receive PG message from FW
* @wait_hbm_start : wait queue for receive HBM start message from FW
- * @wait_stop_wd : wait queue for receive WD stop message from FW
*
* @reset_count : number of consecutive resets
* @dev_state : device state
@@ -426,6 +389,8 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @hbm_f_dc_supported : hbm feature dynamic clients
* @hbm_f_dot_supported : hbm feature disconnect on timeout
* @hbm_f_ev_supported : hbm feature event notification
+ * @hbm_f_fa_supported : hbm feature fixed address client
+ * @hbm_f_ie_supported : hbm feature immediate reply to enum request
*
* @me_clients_rwsem: rw lock over me_clients list
* @me_clients : list of FW clients
@@ -434,26 +399,19 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @me_client_index : last FW client index in enumeration
*
* @allow_fixed_address: allow user space to connect a fixed client
- *
- * @wd_cl : watchdog client
- * @wd_state : watchdog client state
- * @wd_pending : watchdog command is pending
- * @wd_timeout : watchdog expiration timeout
- * @wd_data : watchdog message buffer
+ * @override_fixed_address: force allow fixed address behavior
*
* @amthif_cmd_list : amthif list for cmd waiting
- * @amthif_rd_complete_list : amthif list for reading completed cmd data
- * @iamthif_file_object : file for current amthif operation
+ * @iamthif_fp : file for current amthif operation
* @iamthif_cl : amthif host client
* @iamthif_current_cb : amthif current operation callback
* @iamthif_open_count : number of opened amthif connections
- * @iamthif_timer : time stamp of current amthif command completion
* @iamthif_stall_timer : timer to detect amthif hang
* @iamthif_state : amthif processor state
* @iamthif_canceled : current amthif command is canceled
*
- * @init_work : work item for the device init
* @reset_work : work item for the device reset
+ * @bus_rescan_work : work item for the bus rescan
*
* @device_list : mei client bus list
* @cl_bus_lock : client bus list lock
@@ -486,7 +444,6 @@ struct mei_device {
wait_queue_head_t wait_hw_ready;
wait_queue_head_t wait_pg;
wait_queue_head_t wait_hbm_start;
- wait_queue_head_t wait_stop_wd;
/*
* mei device states
@@ -522,6 +479,8 @@ struct mei_device {
unsigned int hbm_f_dc_supported:1;
unsigned int hbm_f_dot_supported:1;
unsigned int hbm_f_ev_supported:1;
+ unsigned int hbm_f_fa_supported:1;
+ unsigned int hbm_f_ie_supported:1;
struct rw_semaphore me_clients_rwsem;
struct list_head me_clients;
@@ -530,29 +489,21 @@ struct mei_device {
unsigned long me_client_index;
bool allow_fixed_address;
-
- struct mei_cl wd_cl;
- enum mei_wd_states wd_state;
- bool wd_pending;
- u16 wd_timeout;
- unsigned char wd_data[MEI_WD_START_MSG_SIZE];
-
+ bool override_fixed_address;
/* amthif list for cmd waiting */
struct mei_cl_cb amthif_cmd_list;
/* driver managed amthif list for reading completed amthif cmd data */
- struct mei_cl_cb amthif_rd_complete_list;
- struct file *iamthif_file_object;
+ const struct file *iamthif_fp;
struct mei_cl iamthif_cl;
struct mei_cl_cb *iamthif_current_cb;
long iamthif_open_count;
- unsigned long iamthif_timer;
u32 iamthif_stall_timer;
enum iamthif_states iamthif_state;
bool iamthif_canceled;
- struct work_struct init_work;
struct work_struct reset_work;
+ struct work_struct bus_rescan_work;
/* List of bus devices */
struct list_head device_list;
@@ -635,47 +586,18 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
int mei_amthif_release(struct mei_device *dev, struct file *file);
-struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
- struct file *file);
-
int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb);
int mei_amthif_run_next_cmd(struct mei_device *dev);
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);
+void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb);
int mei_amthif_irq_read_msg(struct mei_cl *cl,
struct mei_msg_hdr *mei_hdr,
struct mei_cl_cb *complete_list);
int mei_amthif_irq_read(struct mei_device *dev, s32 *slots);
/*
- * NFC functions
- */
-int mei_nfc_host_init(struct mei_device *dev, struct mei_me_client *me_cl);
-void mei_nfc_host_exit(struct mei_device *dev);
-
-/*
- * NFC Client UUID
- */
-extern const uuid_le mei_nfc_guid;
-
-int mei_wd_send(struct mei_device *dev);
-int mei_wd_stop(struct mei_device *dev);
-int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl);
-/*
- * mei_watchdog_register - Registering watchdog interface
- * once we got connection to the WD Client
- * @dev: mei device
- */
-int mei_watchdog_register(struct mei_device *dev);
-/*
- * mei_watchdog_unregister - Unregistering watchdog interface
- * @dev: mei device
- */
-void mei_watchdog_unregister(struct mei_device *dev);
-
-/*
* Register Access Function
*/
@@ -782,6 +704,8 @@ bool mei_hbuf_acquire(struct mei_device *dev);
bool mei_write_is_idle(struct mei_device *dev);
+void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr);
+
#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);
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 75fc9c688..64e64da6d 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -88,6 +88,9 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, mei_me_pch8_cfg)},
+
/* required last entry */
{0, }
};
@@ -210,7 +213,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err = mei_register(dev, &pdev->dev);
if (err)
- goto release_irq;
+ goto stop;
pci_set_drvdata(pdev, dev);
@@ -231,6 +234,8 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
+stop:
+ mei_stop(dev);
release_irq:
mei_cancel_work(dev);
mei_disable_interrupts(dev);
diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c
index 71f8a7475..30cc30683 100644
--- a/drivers/misc/mei/pci-txe.c
+++ b/drivers/misc/mei/pci-txe.c
@@ -154,7 +154,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err = mei_register(dev, &pdev->dev);
if (err)
- goto release_irq;
+ goto stop;
pci_set_drvdata(pdev, dev);
@@ -170,6 +170,8 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
+stop:
+ mei_stop(dev);
release_irq:
mei_cancel_work(dev);
diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c
deleted file mode 100644
index b34663883..000000000
--- a/drivers/misc/mei/wd.c
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- *
- * Intel Management Engine Interface (Intel MEI) Linux driver
- * Copyright (c) 2003-2012, 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/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/sched.h>
-#include <linux/watchdog.h>
-
-#include <linux/mei.h>
-
-#include "mei_dev.h"
-#include "hbm.h"
-#include "client.h"
-
-static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
-static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 };
-
-/*
- * AMT Watchdog Device
- */
-#define INTEL_AMT_WATCHDOG_ID "INTCAMT"
-
-/* UUIDs for AMT F/W clients */
-const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89,
- 0x9D, 0xA9, 0x15, 0x14, 0xCB,
- 0x32, 0xAB);
-
-static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)
-{
- dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout);
- memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE);
- memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16));
-}
-
-/**
- * mei_wd_host_init - connect to the watchdog client
- *
- * @dev: the device structure
- * @me_cl: me client
- *
- * Return: -ENOTTY if wd client cannot be found
- * -EIO if write has failed
- * 0 on success
- */
-int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
-{
- struct mei_cl *cl = &dev->wd_cl;
- int ret;
-
- mei_cl_init(cl, dev);
-
- dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT;
- dev->wd_state = MEI_WD_IDLE;
-
- ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID);
- if (ret < 0) {
- dev_info(dev->dev, "wd: failed link client\n");
- return ret;
- }
-
- ret = mei_cl_connect(cl, me_cl, NULL);
- if (ret) {
- dev_err(dev->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;
-}
-
-/**
- * mei_wd_send - sends watch dog message to fw.
- *
- * @dev: the device structure
- *
- * Return: 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 = cl->host_client_id;
- hdr.me_addr = mei_cl_me_id(cl);
- hdr.msg_complete = 1;
- hdr.reserved = 0;
- hdr.internal = 0;
-
- if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE))
- 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 {
- dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n");
- return -EINVAL;
- }
-
- ret = mei_write_message(dev, &hdr, dev->wd_data);
- if (ret) {
- dev_err(dev->dev, "wd: write message failed\n");
- return ret;
- }
-
- ret = mei_cl_flow_ctrl_reduce(cl);
- if (ret) {
- dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n");
- return ret;
- }
-
- return 0;
-}
-
-/**
- * mei_wd_stop - sends watchdog stop message to fw.
- *
- * @dev: the device structure
- *
- * Return: 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)
-{
- struct mei_cl *cl = &dev->wd_cl;
- int ret;
-
- if (!mei_cl_is_connected(cl) ||
- dev->wd_state != MEI_WD_RUNNING)
- return 0;
-
- memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE);
-
- dev->wd_state = MEI_WD_STOPPING;
-
- ret = mei_cl_flow_ctrl_creds(cl);
- if (ret < 0)
- 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;
- }
-
- mutex_unlock(&dev->device_lock);
-
- 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) {
- /* timeout */
- ret = -ETIME;
- dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret);
- goto err;
- }
- dev_dbg(dev->dev, "wd: stop completed after %u msec\n",
- MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret));
- return 0;
-err:
- return ret;
-}
-
-/**
- * mei_wd_ops_start - wd start command from the watchdog core.
- *
- * @wd_dev: watchdog device struct
- *
- * Return: 0 if success, negative errno code for failure
- */
-static int mei_wd_ops_start(struct watchdog_device *wd_dev)
-{
- struct mei_device *dev;
- struct mei_cl *cl;
- int err = -ENODEV;
-
- dev = watchdog_get_drvdata(wd_dev);
- if (!dev)
- return -ENODEV;
-
- cl = &dev->wd_cl;
-
- mutex_lock(&dev->device_lock);
-
- if (dev->dev_state != MEI_DEV_ENABLED) {
- dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n",
- mei_dev_state_str(dev->dev_state));
- goto end_unlock;
- }
-
- if (!mei_cl_is_connected(cl)) {
- cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n");
- goto end_unlock;
- }
-
- mei_wd_set_start_timeout(dev, dev->wd_timeout);
-
- err = 0;
-end_unlock:
- mutex_unlock(&dev->device_lock);
- return err;
-}
-
-/**
- * mei_wd_ops_stop - wd stop command from the watchdog core.
- *
- * @wd_dev: watchdog device struct
- *
- * Return: 0 if success, negative errno code for failure
- */
-static int mei_wd_ops_stop(struct watchdog_device *wd_dev)
-{
- struct mei_device *dev;
-
- dev = watchdog_get_drvdata(wd_dev);
- if (!dev)
- return -ENODEV;
-
- mutex_lock(&dev->device_lock);
- mei_wd_stop(dev);
- mutex_unlock(&dev->device_lock);
-
- return 0;
-}
-
-/**
- * mei_wd_ops_ping - wd ping command from the watchdog core.
- *
- * @wd_dev: watchdog device struct
- *
- * Return: 0 if success, negative errno code for failure
- */
-static int mei_wd_ops_ping(struct watchdog_device *wd_dev)
-{
- struct mei_device *dev;
- struct mei_cl *cl;
- int ret;
-
- dev = watchdog_get_drvdata(wd_dev);
- if (!dev)
- return -ENODEV;
-
- cl = &dev->wd_cl;
-
- mutex_lock(&dev->device_lock);
-
- if (!mei_cl_is_connected(cl)) {
- cl_err(dev, cl, "wd: not connected.\n");
- ret = -ENODEV;
- goto end;
- }
-
- dev->wd_state = MEI_WD_RUNNING;
-
- ret = mei_cl_flow_ctrl_creds(cl);
- if (ret < 0)
- goto end;
-
- /* Check if we can send the ping to HW*/
- if (ret && mei_hbuf_acquire(dev)) {
- dev_dbg(dev->dev, "wd: sending ping\n");
-
- ret = mei_wd_send(dev);
- if (ret)
- goto end;
- dev->wd_pending = false;
- } else {
- dev->wd_pending = true;
- }
-
-end:
- mutex_unlock(&dev->device_lock);
- return ret;
-}
-
-/**
- * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core.
- *
- * @wd_dev: watchdog device struct
- * @timeout: timeout value to set
- *
- * Return: 0 if success, negative errno code for failure
- */
-static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev,
- unsigned int timeout)
-{
- struct mei_device *dev;
-
- dev = watchdog_get_drvdata(wd_dev);
- if (!dev)
- return -ENODEV;
-
- /* Check Timeout value */
- if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT)
- return -EINVAL;
-
- mutex_lock(&dev->device_lock);
-
- dev->wd_timeout = timeout;
- wd_dev->timeout = timeout;
- mei_wd_set_start_timeout(dev, dev->wd_timeout);
-
- mutex_unlock(&dev->device_lock);
-
- return 0;
-}
-
-/*
- * Watchdog Device structs
- */
-static const struct watchdog_ops wd_ops = {
- .owner = THIS_MODULE,
- .start = mei_wd_ops_start,
- .stop = mei_wd_ops_stop,
- .ping = mei_wd_ops_ping,
- .set_timeout = mei_wd_ops_set_timeout,
-};
-static const struct watchdog_info wd_info = {
- .identity = INTEL_AMT_WATCHDOG_ID,
- .options = WDIOF_KEEPALIVEPING |
- WDIOF_SETTIMEOUT |
- WDIOF_ALARMONLY,
-};
-
-static struct watchdog_device amt_wd_dev = {
- .info = &wd_info,
- .ops = &wd_ops,
- .timeout = MEI_WD_DEFAULT_TIMEOUT,
- .min_timeout = MEI_WD_MIN_TIMEOUT,
- .max_timeout = MEI_WD_MAX_TIMEOUT,
-};
-
-
-int mei_watchdog_register(struct mei_device *dev)
-{
-
- int ret;
-
- amt_wd_dev.parent = dev->dev;
- /* 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->dev, "wd: unable to register watchdog device = %d.\n",
- ret);
- return ret;
- }
-
- dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n");
- watchdog_set_drvdata(&amt_wd_dev, dev);
- return 0;
-}
-
-void mei_watchdog_unregister(struct mei_device *dev)
-{
- if (watchdog_get_drvdata(&amt_wd_dev) == NULL)
- return;
-
- watchdog_set_drvdata(&amt_wd_dev, NULL);
- watchdog_unregister_device(&amt_wd_dev);
-}
-