summaryrefslogtreecommitdiffstats
path: root/drivers/misc/mei
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/mei')
-rw-r--r--drivers/misc/mei/amthif.c28
-rw-r--r--drivers/misc/mei/bus.c150
-rw-r--r--drivers/misc/mei/client.c473
-rw-r--r--drivers/misc/mei/client.h114
-rw-r--r--drivers/misc/mei/debugfs.c15
-rw-r--r--drivers/misc/mei/hbm.c16
-rw-r--r--drivers/misc/mei/hw-me.c59
-rw-r--r--drivers/misc/mei/hw-txe.c33
-rw-r--r--drivers/misc/mei/init.c8
-rw-r--r--drivers/misc/mei/interrupt.c95
-rw-r--r--drivers/misc/mei/main.c57
-rw-r--r--drivers/misc/mei/mei_dev.h102
-rw-r--r--drivers/misc/mei/nfc.c223
-rw-r--r--drivers/misc/mei/pci-txe.c2
-rw-r--r--drivers/misc/mei/wd.c22
15 files changed, 734 insertions, 663 deletions
diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c
index d2cd53e3fac3..1e42781592d8 100644
--- a/drivers/misc/mei/amthif.c
+++ b/drivers/misc/mei/amthif.c
@@ -59,46 +59,29 @@ void mei_amthif_reset_params(struct mei_device *dev)
* mei_amthif_host_init - mei initialization amthif client.
*
* @dev: the device structure
+ * @me_cl: me client
*
* Return: 0 on success, <0 on failure.
*/
-int mei_amthif_host_init(struct mei_device *dev)
+int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
{
struct mei_cl *cl = &dev->iamthif_cl;
- struct mei_me_client *me_cl;
int ret;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
mei_cl_init(cl, dev);
- me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
- if (!me_cl) {
- dev_info(dev->dev, "amthif: failed to find the client");
- return -ENOTTY;
- }
-
- cl->me_client_id = me_cl->client_id;
- cl->cl_uuid = me_cl->props.protocol_name;
-
- /* Assign iamthif_mtu to the value received from ME */
-
- dev->iamthif_mtu = me_cl->props.max_msg_length;
- dev_dbg(dev->dev, "IAMTHIF_MTU = %d\n", dev->iamthif_mtu);
-
-
ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID);
if (ret < 0) {
dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
- goto out;
+ return ret;
}
- ret = mei_cl_connect(cl, NULL);
+ ret = mei_cl_connect(cl, me_cl, NULL);
dev->iamthif_state = MEI_IAMTHIF_IDLE;
-out:
- mei_me_cl_put(me_cl);
return ret;
}
@@ -250,7 +233,6 @@ static int mei_amthif_read_start(struct mei_cl *cl, struct file *file)
{
struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb;
- size_t length = dev->iamthif_mtu;
int rets;
cb = mei_io_cb_init(cl, MEI_FOP_READ, file);
@@ -259,7 +241,7 @@ static int mei_amthif_read_start(struct mei_cl *cl, struct file *file)
goto err;
}
- rets = mei_io_cb_alloc_buf(cb, length);
+ rets = mei_io_cb_alloc_buf(cb, mei_cl_mtu(cl));
if (rets)
goto err;
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index 4cf38c39878a..357b6ae4d207 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -35,18 +35,30 @@ static int mei_cl_device_match(struct device *dev, struct device_driver *drv)
struct mei_cl_device *device = to_mei_cl_device(dev);
struct mei_cl_driver *driver = to_mei_cl_driver(drv);
const struct mei_cl_device_id *id;
+ const uuid_le *uuid;
+ const char *name;
if (!device)
return 0;
+ uuid = mei_me_cl_uuid(device->me_cl);
+ name = device->name;
+
if (!driver || !driver->id_table)
return 0;
id = driver->id_table;
- while (id->name[0]) {
- if (!strncmp(dev_name(dev), id->name, sizeof(id->name)))
- return 1;
+ while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) {
+
+ if (!uuid_le_cmp(*uuid, id->uuid)) {
+ if (id->name[0]) {
+ if (!strncmp(name, id->name, sizeof(id->name)))
+ return 1;
+ } else {
+ return 1;
+ }
+ }
id++;
}
@@ -69,7 +81,7 @@ static int mei_cl_device_probe(struct device *dev)
dev_dbg(dev, "Device probe\n");
- strlcpy(id.name, dev_name(dev), sizeof(id.name));
+ strlcpy(id.name, device->name, sizeof(id.name));
return driver->probe(device, &id);
}
@@ -97,18 +109,48 @@ static int mei_cl_device_remove(struct device *dev)
return driver->remove(device);
}
+static ssize_t name_show(struct device *dev, struct device_attribute *a,
+ char *buf)
+{
+ struct mei_cl_device *device = to_mei_cl_device(dev);
+ size_t len;
+
+ len = snprintf(buf, PAGE_SIZE, "%s", device->name);
+
+ return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+static DEVICE_ATTR_RO(name);
+
+static ssize_t uuid_show(struct device *dev, struct device_attribute *a,
+ char *buf)
+{
+ struct mei_cl_device *device = to_mei_cl_device(dev);
+ const uuid_le *uuid = mei_me_cl_uuid(device->me_cl);
+ size_t len;
+
+ len = snprintf(buf, PAGE_SIZE, "%pUl", uuid);
+
+ return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+static DEVICE_ATTR_RO(uuid);
+
static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
char *buf)
{
- int len;
+ struct mei_cl_device *device = to_mei_cl_device(dev);
+ const uuid_le *uuid = mei_me_cl_uuid(device->me_cl);
+ size_t len;
- len = snprintf(buf, PAGE_SIZE, "mei:%s\n", dev_name(dev));
+ len = snprintf(buf, PAGE_SIZE, "mei:%s:" MEI_CL_UUID_FMT ":",
+ device->name, MEI_CL_UUID_ARGS(uuid->b));
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
}
static DEVICE_ATTR_RO(modalias);
static struct attribute *mei_cl_dev_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_uuid.attr,
&dev_attr_modalias.attr,
NULL,
};
@@ -116,7 +158,17 @@ ATTRIBUTE_GROUPS(mei_cl_dev);
static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env)
{
- if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev)))
+ struct mei_cl_device *device = to_mei_cl_device(dev);
+ const uuid_le *uuid = mei_me_cl_uuid(device->me_cl);
+
+ if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid))
+ return -ENOMEM;
+
+ if (add_uevent_var(env, "MEI_CL_NAME=%s", device->name))
+ return -ENOMEM;
+
+ if (add_uevent_var(env, "MODALIAS=mei:%s:" MEI_CL_UUID_FMT ":",
+ device->name, MEI_CL_UUID_ARGS(uuid->b)))
return -ENOMEM;
return 0;
@@ -133,7 +185,13 @@ static struct bus_type mei_cl_bus_type = {
static void mei_cl_dev_release(struct device *dev)
{
- kfree(to_mei_cl_device(dev));
+ struct mei_cl_device *device = to_mei_cl_device(dev);
+
+ if (!device)
+ return;
+
+ mei_me_cl_put(device->me_cl);
+ kfree(device);
}
static struct device_type mei_cl_device_type = {
@@ -141,45 +199,50 @@ static struct device_type mei_cl_device_type = {
};
struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *dev,
- uuid_le uuid)
+ uuid_le uuid)
{
struct mei_cl *cl;
list_for_each_entry(cl, &dev->device_list, device_link) {
- if (!uuid_le_cmp(uuid, cl->cl_uuid))
+ if (cl->device && cl->device->me_cl &&
+ !uuid_le_cmp(uuid, *mei_me_cl_uuid(cl->device->me_cl)))
return cl;
}
return NULL;
}
+
struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
- uuid_le uuid, char *name,
- struct mei_cl_ops *ops)
+ struct mei_me_client *me_cl,
+ struct mei_cl *cl,
+ char *name)
{
struct mei_cl_device *device;
- struct mei_cl *cl;
int status;
- cl = mei_cl_bus_find_cl_by_uuid(dev, uuid);
- if (cl == NULL)
- return NULL;
-
device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL);
if (!device)
return NULL;
- device->cl = cl;
- device->ops = ops;
+ device->me_cl = mei_me_cl_get(me_cl);
+ if (!device->me_cl) {
+ kfree(device);
+ return NULL;
+ }
+ device->cl = cl;
device->dev.parent = dev->dev;
device->dev.bus = &mei_cl_bus_type;
device->dev.type = &mei_cl_device_type;
- dev_set_name(&device->dev, "%s", name);
+ strlcpy(device->name, name, sizeof(device->name));
+
+ dev_set_name(&device->dev, "mei:%s:%pUl", name, mei_me_cl_uuid(me_cl));
status = device_register(&device->dev);
if (status) {
dev_err(dev->dev, "Failed to register MEI device\n");
+ mei_me_cl_put(device->me_cl);
kfree(device);
return NULL;
}
@@ -224,11 +287,10 @@ void mei_cl_driver_unregister(struct mei_cl_driver *driver)
}
EXPORT_SYMBOL_GPL(mei_cl_driver_unregister);
-static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
+ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
bool blocking)
{
struct mei_device *dev;
- struct mei_me_client *me_cl = NULL;
struct mei_cl_cb *cb = NULL;
ssize_t rets;
@@ -244,13 +306,12 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
}
/* Check if we have an ME client device */
- me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
- if (!me_cl) {
+ if (!mei_me_cl_is_active(cl->me_cl)) {
rets = -ENOTTY;
goto out;
}
- if (length > me_cl->props.max_msg_length) {
+ if (length > mei_cl_mtu(cl)) {
rets = -EFBIG;
goto out;
}
@@ -266,7 +327,6 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
rets = mei_cl_write(cl, cb, blocking);
out:
- mei_me_cl_put(me_cl);
mutex_unlock(&dev->device_lock);
if (rets < 0)
mei_io_cb_free(cb);
@@ -341,16 +401,6 @@ out:
return rets;
}
-inline ssize_t __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length)
-{
- return ___mei_cl_send(cl, buf, length, 0);
-}
-
-inline ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length)
-{
- return ___mei_cl_send(cl, buf, length, 1);
-}
-
ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
{
struct mei_cl *cl = device->cl;
@@ -358,23 +408,17 @@ ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
if (cl == NULL)
return -ENODEV;
- if (device->ops && device->ops->send)
- return device->ops->send(device, buf, length);
-
- return __mei_cl_send(cl, buf, length);
+ return __mei_cl_send(cl, buf, length, 1);
}
EXPORT_SYMBOL_GPL(mei_cl_send);
ssize_t mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length)
{
- struct mei_cl *cl = device->cl;
+ struct mei_cl *cl = device->cl;
if (cl == NULL)
return -ENODEV;
- if (device->ops && device->ops->recv)
- return device->ops->recv(device, buf, length);
-
return __mei_cl_recv(cl, buf, length);
}
EXPORT_SYMBOL_GPL(mei_cl_recv);
@@ -436,7 +480,13 @@ int mei_cl_enable_device(struct mei_cl_device *device)
mutex_lock(&dev->device_lock);
- err = mei_cl_connect(cl, NULL);
+ if (mei_cl_is_connected(cl)) {
+ mutex_unlock(&dev->device_lock);
+ dev_warn(dev->dev, "Already connected");
+ return -EBUSY;
+ }
+
+ err = mei_cl_connect(cl, device->me_cl, NULL);
if (err < 0) {
mutex_unlock(&dev->device_lock);
dev_err(dev->dev, "Could not connect to the ME client");
@@ -449,10 +499,7 @@ int mei_cl_enable_device(struct mei_cl_device *device)
if (device->event_cb)
mei_cl_read_start(device->cl, 0, NULL);
- if (!device->ops || !device->ops->enable)
- return 0;
-
- return device->ops->enable(device);
+ return 0;
}
EXPORT_SYMBOL_GPL(mei_cl_enable_device);
@@ -467,9 +514,6 @@ int mei_cl_disable_device(struct mei_cl_device *device)
dev = cl->dev;
- if (device->ops && device->ops->disable)
- device->ops->disable(device);
-
device->event_cb = NULL;
mutex_lock(&dev->device_lock);
@@ -480,8 +524,6 @@ int mei_cl_disable_device(struct mei_cl_device *device)
goto out;
}
- cl->state = MEI_FILE_DISCONNECTING;
-
err = mei_cl_disconnect(cl);
if (err < 0) {
dev_err(dev->dev, "Could not disconnect from the ME client");
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 1e99ef6a54a2..6decbe136ea7 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -83,7 +83,7 @@ void mei_me_cl_put(struct mei_me_client *me_cl)
}
/**
- * __mei_me_cl_del - delete me client form the list and decrease
+ * __mei_me_cl_del - delete me client from the list and decrease
* reference counter
*
* @dev: mei device
@@ -96,11 +96,25 @@ static void __mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl)
if (!me_cl)
return;
- list_del(&me_cl->list);
+ list_del_init(&me_cl->list);
mei_me_cl_put(me_cl);
}
/**
+ * mei_me_cl_del - delete me client from the list and decrease
+ * reference counter
+ *
+ * @dev: mei device
+ * @me_cl: me client
+ */
+void mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl)
+{
+ down_write(&dev->me_clients_rwsem);
+ __mei_me_cl_del(dev, me_cl);
+ up_write(&dev->me_clients_rwsem);
+}
+
+/**
* mei_me_cl_add - add me client to the list
*
* @dev: mei device
@@ -317,7 +331,7 @@ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
{
return cl1 && cl2 &&
(cl1->host_client_id == cl2->host_client_id) &&
- (cl1->me_client_id == cl2->me_client_id);
+ (mei_cl_me_id(cl1) == mei_cl_me_id(cl2));
}
/**
@@ -546,6 +560,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
INIT_LIST_HEAD(&cl->link);
INIT_LIST_HEAD(&cl->device_link);
cl->writing_state = MEI_IDLE;
+ cl->state = MEI_FILE_INITIALIZING;
cl->dev = dev;
}
@@ -619,7 +634,7 @@ int mei_cl_link(struct mei_cl *cl, int id)
}
/**
- * mei_cl_unlink - remove me_cl from the list
+ * mei_cl_unlink - remove host client from the list
*
* @cl: host client
*
@@ -667,17 +682,17 @@ void mei_host_client_init(struct work_struct *work)
me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
if (me_cl)
- mei_amthif_host_init(dev);
+ 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);
+ mei_wd_host_init(dev, me_cl);
mei_me_cl_put(me_cl);
me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid);
if (me_cl)
- mei_nfc_host_init(dev);
+ mei_nfc_host_init(dev, me_cl);
mei_me_cl_put(me_cl);
@@ -699,7 +714,7 @@ void mei_host_client_init(struct work_struct *work)
bool mei_hbuf_acquire(struct mei_device *dev)
{
if (mei_pg_state(dev) == MEI_PG_ON ||
- dev->pg_event == MEI_PG_EVENT_WAIT) {
+ mei_pg_in_transition(dev)) {
dev_dbg(dev->dev, "device is in pg\n");
return false;
}
@@ -715,6 +730,120 @@ bool mei_hbuf_acquire(struct mei_device *dev)
}
/**
+ * mei_cl_set_disconnected - set disconnected state and clear
+ * associated states and resources
+ *
+ * @cl: host client
+ */
+void mei_cl_set_disconnected(struct mei_cl *cl)
+{
+ struct mei_device *dev = cl->dev;
+
+ if (cl->state == MEI_FILE_DISCONNECTED ||
+ cl->state == MEI_FILE_INITIALIZING)
+ return;
+
+ cl->state = MEI_FILE_DISCONNECTED;
+ mei_io_list_flush(&dev->ctrl_rd_list, cl);
+ mei_io_list_flush(&dev->ctrl_wr_list, cl);
+ cl->mei_flow_ctrl_creds = 0;
+ cl->timer_count = 0;
+
+ if (!cl->me_cl)
+ return;
+
+ if (!WARN_ON(cl->me_cl->connect_count == 0))
+ cl->me_cl->connect_count--;
+
+ if (cl->me_cl->connect_count == 0)
+ cl->me_cl->mei_flow_ctrl_creds = 0;
+
+ mei_me_cl_put(cl->me_cl);
+ cl->me_cl = NULL;
+}
+
+static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl)
+{
+ if (!mei_me_cl_get(me_cl))
+ return -ENOENT;
+
+ /* only one connection is allowed for fixed address clients */
+ if (me_cl->props.fixed_address) {
+ if (me_cl->connect_count) {
+ mei_me_cl_put(me_cl);
+ return -EBUSY;
+ }
+ }
+
+ cl->me_cl = me_cl;
+ cl->state = MEI_FILE_CONNECTING;
+ cl->me_cl->connect_count++;
+
+ return 0;
+}
+
+/*
+ * mei_cl_send_disconnect - send disconnect request
+ *
+ * @cl: host client
+ * @cb: callback block
+ *
+ * Return: 0, OK; otherwise, error.
+ */
+static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb)
+{
+ struct mei_device *dev;
+ int ret;
+
+ dev = cl->dev;
+
+ ret = mei_hbm_cl_disconnect_req(dev, cl);
+ cl->status = ret;
+ if (ret) {
+ cl->state = MEI_FILE_DISCONNECT_REPLY;
+ return ret;
+ }
+
+ list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
+ cl->timer_count = MEI_CONNECT_TIMEOUT;
+
+ return 0;
+}
+
+/**
+ * mei_cl_irq_disconnect - processes close related operation from
+ * interrupt thread context - send disconnect request
+ *
+ * @cl: client
+ * @cb: callback block.
+ * @cmpl_list: complete list.
+ *
+ * Return: 0, OK; otherwise, error.
+ */
+int mei_cl_irq_disconnect(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;
+
+ msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
+ slots = mei_hbuf_empty_slots(dev);
+
+ if (slots < msg_slots)
+ return -EMSGSIZE;
+
+ ret = mei_cl_send_disconnect(cl, cb);
+ if (ret)
+ list_move_tail(&cb->list, &cmpl_list->list);
+
+ return ret;
+}
+
+
+
+/**
* mei_cl_disconnect - disconnect host client from the me one
*
* @cl: host client
@@ -736,8 +865,13 @@ int mei_cl_disconnect(struct mei_cl *cl)
cl_dbg(dev, cl, "disconnecting");
- if (cl->state != MEI_FILE_DISCONNECTING)
+ if (!mei_cl_is_connected(cl))
+ return 0;
+
+ if (mei_cl_is_fixed_address(cl)) {
+ mei_cl_set_disconnected(cl);
return 0;
+ }
rets = pm_runtime_get(dev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
@@ -746,44 +880,41 @@ int mei_cl_disconnect(struct mei_cl *cl)
return rets;
}
+ cl->state = MEI_FILE_DISCONNECTING;
+
cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL);
rets = cb ? 0 : -ENOMEM;
if (rets)
- goto free;
+ goto out;
+
+ cl_dbg(dev, cl, "add disconnect cb to control write list\n");
+ list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
if (mei_hbuf_acquire(dev)) {
- if (mei_hbm_cl_disconnect_req(dev, cl)) {
- rets = -ENODEV;
+ rets = mei_cl_send_disconnect(cl, cb);
+ if (rets) {
cl_err(dev, cl, "failed to disconnect.\n");
- goto free;
+ goto out;
}
- cl->timer_count = MEI_CONNECT_TIMEOUT;
- mdelay(10); /* Wait for hardware disconnection ready */
- list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
- } else {
- cl_dbg(dev, cl, "add disconnect cb to control write list\n");
- list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
-
}
- mutex_unlock(&dev->device_lock);
-
- wait_event_timeout(cl->wait,
- MEI_FILE_DISCONNECTED == cl->state,
- mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
+ mutex_unlock(&dev->device_lock);
+ wait_event_timeout(cl->wait, cl->state == MEI_FILE_DISCONNECT_REPLY,
+ mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
mutex_lock(&dev->device_lock);
- if (MEI_FILE_DISCONNECTED == cl->state) {
- rets = 0;
- cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
- } else {
+ rets = cl->status;
+ if (cl->state != MEI_FILE_DISCONNECT_REPLY) {
cl_dbg(dev, cl, "timeout on disconnect from FW client.\n");
rets = -ETIME;
}
- mei_io_list_flush(&dev->ctrl_rd_list, cl);
- mei_io_list_flush(&dev->ctrl_wr_list, cl);
-free:
+out:
+ /* we disconnect also on error */
+ mei_cl_set_disconnected(cl);
+ if (!rets)
+ cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
+
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
@@ -801,53 +932,119 @@ free:
*
* Return: true if other client is connected, false - otherwise.
*/
-bool mei_cl_is_other_connecting(struct mei_cl *cl)
+static bool mei_cl_is_other_connecting(struct mei_cl *cl)
{
struct mei_device *dev;
- struct mei_cl *ocl; /* the other client */
-
- if (WARN_ON(!cl || !cl->dev))
- return false;
+ struct mei_cl_cb *cb;
dev = cl->dev;
- 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)
+ list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) {
+ if (cb->fop_type == MEI_FOP_CONNECT &&
+ mei_cl_me_id(cl) == mei_cl_me_id(cb->cl))
return true;
-
}
return false;
}
/**
+ * mei_cl_send_connect - send connect request
+ *
+ * @cl: host client
+ * @cb: callback block
+ *
+ * Return: 0, OK; otherwise, error.
+ */
+static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb)
+{
+ struct mei_device *dev;
+ int ret;
+
+ dev = cl->dev;
+
+ ret = mei_hbm_cl_connect_req(dev, cl);
+ cl->status = ret;
+ if (ret) {
+ cl->state = MEI_FILE_DISCONNECT_REPLY;
+ return ret;
+ }
+
+ list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
+ cl->timer_count = MEI_CONNECT_TIMEOUT;
+ return 0;
+}
+
+/**
+ * mei_cl_irq_connect - send connect request in irq_thread context
+ *
+ * @cl: host client
+ * @cb: callback block
+ * @cmpl_list: complete list
+ *
+ * Return: 0, OK; otherwise, error.
+ */
+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 rets;
+
+ msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
+ slots = mei_hbuf_empty_slots(dev);
+
+ if (mei_cl_is_other_connecting(cl))
+ return 0;
+
+ if (slots < msg_slots)
+ return -EMSGSIZE;
+
+ rets = mei_cl_send_connect(cl, cb);
+ if (rets)
+ list_move_tail(&cb->list, &cmpl_list->list);
+
+ return rets;
+}
+
+/**
* mei_cl_connect - connect host client to the me one
*
* @cl: host client
+ * @me_cl: me client
* @file: pointer to file structure
*
* Locking: called under "dev->device_lock" lock
*
* Return: 0 on success, <0 on failure.
*/
-int mei_cl_connect(struct mei_cl *cl, struct file *file)
+int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
+ struct file *file)
{
struct mei_device *dev;
struct mei_cl_cb *cb;
int rets;
- if (WARN_ON(!cl || !cl->dev))
+ if (WARN_ON(!cl || !cl->dev || !me_cl))
return -ENODEV;
dev = cl->dev;
+ rets = mei_cl_set_connecting(cl, me_cl);
+ if (rets)
+ return rets;
+
+ if (mei_cl_is_fixed_address(cl)) {
+ cl->state = MEI_FILE_CONNECTED;
+ return 0;
+ }
+
rets = pm_runtime_get(dev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(dev->dev);
cl_err(dev, cl, "rpm: get failed %d\n", rets);
- return rets;
+ goto nortpm;
}
cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file);
@@ -855,45 +1052,40 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
if (rets)
goto out;
+ list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
+
/* run hbuf acquire last so we don't have to undo */
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
- cl->state = MEI_FILE_CONNECTING;
- if (mei_hbm_cl_connect_req(dev, cl)) {
- rets = -ENODEV;
+ rets = mei_cl_send_connect(cl, cb);
+ if (rets)
goto out;
- }
- cl->timer_count = MEI_CONNECT_TIMEOUT;
- list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
- } else {
- cl->state = MEI_FILE_INITIALIZING;
- list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
}
mutex_unlock(&dev->device_lock);
wait_event_timeout(cl->wait,
(cl->state == MEI_FILE_CONNECTED ||
- cl->state == MEI_FILE_DISCONNECTED),
+ cl->state == MEI_FILE_DISCONNECT_REPLY),
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
mutex_lock(&dev->device_lock);
if (!mei_cl_is_connected(cl)) {
- cl->state = MEI_FILE_DISCONNECTED;
- /* something went really wrong */
+ /* timeout or 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);
}
rets = cl->status;
-
out:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
mei_io_cb_free(cb);
+
+nortpm:
+ if (!mei_cl_is_connected(cl))
+ mei_cl_set_disconnected(cl);
+
return rets;
}
@@ -934,36 +1126,29 @@ err:
* @cl: private data of the file object
*
* Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
- * -ENOENT if mei_cl is not present
- * -EINVAL if single_recv_buf == 0
*/
int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
{
- struct mei_device *dev;
- struct mei_me_client *me_cl;
- int rets = 0;
+ int rets;
- if (WARN_ON(!cl || !cl->dev))
+ if (WARN_ON(!cl || !cl->me_cl))
return -EINVAL;
- dev = cl->dev;
-
if (cl->mei_flow_ctrl_creds > 0)
return 1;
- me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
- if (!me_cl) {
- cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
- return -ENOENT;
+ if (mei_cl_is_fixed_address(cl)) {
+ rets = mei_cl_read_start(cl, mei_cl_mtu(cl), NULL);
+ if (rets && rets != -EBUSY)
+ return rets;
+ return 1;
}
- if (me_cl->mei_flow_ctrl_creds > 0) {
- rets = 1;
- if (WARN_ON(me_cl->props.single_recv_buf == 0))
- rets = -EINVAL;
+ if (mei_cl_is_single_recv_buf(cl)) {
+ if (cl->me_cl->mei_flow_ctrl_creds > 0)
+ return 1;
}
- mei_me_cl_put(me_cl);
- return rets;
+ return 0;
}
/**
@@ -973,43 +1158,26 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
*
* Return:
* 0 on success
- * -ENOENT when me client is not found
* -EINVAL when ctrl credits are <= 0
*/
int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
{
- struct mei_device *dev;
- struct mei_me_client *me_cl;
- int rets;
-
- if (WARN_ON(!cl || !cl->dev))
+ if (WARN_ON(!cl || !cl->me_cl))
return -EINVAL;
- dev = cl->dev;
-
- me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
- if (!me_cl) {
- cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
- return -ENOENT;
- }
+ if (mei_cl_is_fixed_address(cl))
+ return 0;
- if (me_cl->props.single_recv_buf) {
- if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) {
- rets = -EINVAL;
- goto out;
- }
- me_cl->mei_flow_ctrl_creds--;
+ if (mei_cl_is_single_recv_buf(cl)) {
+ if (WARN_ON(cl->me_cl->mei_flow_ctrl_creds <= 0))
+ return -EINVAL;
+ cl->me_cl->mei_flow_ctrl_creds--;
} else {
- if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) {
- rets = -EINVAL;
- goto out;
- }
+ if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
+ return -EINVAL;
cl->mei_flow_ctrl_creds--;
}
- rets = 0;
-out:
- mei_me_cl_put(me_cl);
- return rets;
+ return 0;
}
/**
@@ -1025,7 +1193,6 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
{
struct mei_device *dev;
struct mei_cl_cb *cb;
- struct mei_me_client *me_cl;
int rets;
if (WARN_ON(!cl || !cl->dev))
@@ -1040,27 +1207,29 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
if (!list_empty(&cl->rd_pending))
return -EBUSY;
- me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
- if (!me_cl) {
- cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
+ if (!mei_me_cl_is_active(cl->me_cl)) {
+ cl_err(dev, cl, "no such me client\n");
return -ENOTTY;
}
+
/* always allocate at least client max message */
- length = max_t(size_t, length, me_cl->props.max_msg_length);
- mei_me_cl_put(me_cl);
+ length = max_t(size_t, length, mei_cl_mtu(cl));
+ cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp);
+ if (!cb)
+ return -ENOMEM;
+
+ if (mei_cl_is_fixed_address(cl)) {
+ list_add_tail(&cb->list, &cl->rd_pending);
+ return 0;
+ }
rets = pm_runtime_get(dev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(dev->dev);
cl_err(dev, cl, "rpm: get failed %d\n", rets);
- return rets;
+ goto nortpm;
}
- cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp);
- rets = cb ? 0 : -ENOMEM;
- if (rets)
- goto out;
-
if (mei_hbuf_acquire(dev)) {
rets = mei_hbm_cl_flow_control_req(dev, cl);
if (rets < 0)
@@ -1068,6 +1237,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
list_add_tail(&cb->list, &cl->rd_pending);
} else {
+ rets = 0;
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
}
@@ -1075,7 +1245,7 @@ out:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
-
+nortpm:
if (rets)
mei_io_cb_free(cb);
@@ -1102,6 +1272,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
u32 msg_slots;
int slots;
int rets;
+ bool first_chunk;
if (WARN_ON(!cl || !cl->dev))
return -ENODEV;
@@ -1110,7 +1281,9 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
buf = &cb->buf;
- rets = mei_cl_flow_ctrl_creds(cl);
+ first_chunk = cb->buf_idx == 0;
+
+ rets = first_chunk ? mei_cl_flow_ctrl_creds(cl) : 1;
if (rets < 0)
return rets;
@@ -1123,8 +1296,8 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
len = buf->size - cb->buf_idx;
msg_slots = mei_data2slots(len);
- mei_hdr.host_addr = cl->host_client_id;
- mei_hdr.me_addr = cl->me_client_id;
+ mei_hdr.host_addr = mei_cl_host_addr(cl);
+ mei_hdr.me_addr = mei_cl_me_id(cl);
mei_hdr.reserved = 0;
mei_hdr.internal = cb->internal;
@@ -1157,12 +1330,14 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
cb->buf_idx += mei_hdr.length;
cb->completed = mei_hdr.msg_complete == 1;
- if (mei_hdr.msg_complete) {
+ if (first_chunk) {
if (mei_cl_flow_ctrl_reduce(cl))
return -EIO;
- list_move_tail(&cb->list, &dev->write_waiting_list.list);
}
+ if (mei_hdr.msg_complete)
+ list_move_tail(&cb->list, &dev->write_waiting_list.list);
+
return 0;
}
@@ -1207,8 +1382,8 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
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.host_addr = mei_cl_host_addr(cl);
+ mei_hdr.me_addr = mei_cl_me_id(cl);
mei_hdr.reserved = 0;
mei_hdr.msg_complete = 0;
mei_hdr.internal = cb->internal;
@@ -1241,21 +1416,19 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
if (rets)
goto err;
+ rets = mei_cl_flow_ctrl_reduce(cl);
+ if (rets)
+ goto err;
+
cl->writing_state = MEI_WRITING;
cb->buf_idx = mei_hdr.length;
cb->completed = mei_hdr.msg_complete == 1;
out:
- if (mei_hdr.msg_complete) {
- rets = mei_cl_flow_ctrl_reduce(cl);
- if (rets < 0)
- goto err;
-
+ if (mei_hdr.msg_complete)
list_add_tail(&cb->list, &dev->write_waiting_list.list);
- } else {
+ else
list_add_tail(&cb->list, &dev->write_list.list);
- }
-
if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
@@ -1289,20 +1462,36 @@ err:
*/
void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
{
- if (cb->fop_type == MEI_FOP_WRITE) {
+ struct mei_device *dev = cl->dev;
+
+ switch (cb->fop_type) {
+ case MEI_FOP_WRITE:
mei_io_cb_free(cb);
- cb = NULL;
cl->writing_state = MEI_WRITE_COMPLETE;
- if (waitqueue_active(&cl->tx_wait))
+ if (waitqueue_active(&cl->tx_wait)) {
wake_up_interruptible(&cl->tx_wait);
+ } else {
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_request_autosuspend(dev->dev);
+ }
+ break;
- } else if (cb->fop_type == MEI_FOP_READ) {
+ 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);
+ break;
+
+ case MEI_FOP_CONNECT:
+ case MEI_FOP_DISCONNECT:
+ if (waitqueue_active(&cl->wait))
+ wake_up(&cl->wait);
+ break;
+ default:
+ BUG_ON(0);
}
}
@@ -1312,16 +1501,12 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
*
* @dev: mei device
*/
-
void mei_cl_all_disconnect(struct mei_device *dev)
{
struct mei_cl *cl;
- list_for_each_entry(cl, &dev->file_list, link) {
- cl->state = MEI_FILE_DISCONNECTED;
- cl->mei_flow_ctrl_creds = 0;
- cl->timer_count = 0;
- }
+ list_for_each_entry(cl, &dev->file_list, link)
+ mei_cl_set_disconnected(cl);
}
diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h
index 0a39e5d45171..8d7f057f1045 100644
--- a/drivers/misc/mei/client.h
+++ b/drivers/misc/mei/client.h
@@ -44,6 +44,30 @@ void mei_me_cl_rm_by_uuid_id(struct mei_device *dev,
const uuid_le *uuid, u8 id);
void mei_me_cl_rm_all(struct mei_device *dev);
+/**
+ * mei_me_cl_is_active - check whether me client is active in the fw
+ *
+ * @me_cl: me client
+ *
+ * Return: true if the me client is active in the firmware
+ */
+static inline bool mei_me_cl_is_active(const struct mei_me_client *me_cl)
+{
+ return !list_empty_careful(&me_cl->list);
+}
+
+/**
+ * mei_me_cl_uuid - return me client protocol name (uuid)
+ *
+ * @me_cl: me client
+ *
+ * Return: me client protocol name
+ */
+static inline const uuid_le *mei_me_cl_uuid(const struct mei_me_client *me_cl)
+{
+ return &me_cl->props.protocol_name;
+}
+
/*
* MEI IO Functions
*/
@@ -94,18 +118,96 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl);
/**
* mei_cl_is_connected - host client is connected
*
- * @cl: host clinet
+ * @cl: host client
*
- * Return: true if the host clinet is connected
+ * Return: true if the host client is connected
*/
static inline bool mei_cl_is_connected(struct mei_cl *cl)
{
return cl->state == MEI_FILE_CONNECTED;
}
-bool mei_cl_is_other_connecting(struct mei_cl *cl);
+/**
+ * mei_cl_me_id - me client id
+ *
+ * @cl: host client
+ *
+ * Return: me client id or 0 if client is not connected
+ */
+static inline u8 mei_cl_me_id(const struct mei_cl *cl)
+{
+ return cl->me_cl ? cl->me_cl->client_id : 0;
+}
+
+/**
+ * mei_cl_mtu - maximal message that client can send and receive
+ *
+ * @cl: host client
+ *
+ * Return: mtu
+ */
+static inline size_t mei_cl_mtu(const struct mei_cl *cl)
+{
+ return cl->me_cl->props.max_msg_length;
+}
+
+/**
+ * mei_cl_is_fixed_address - check whether the me client uses fixed address
+ *
+ * @cl: host client
+ *
+ * Return: true if the client is connected and it has fixed me address
+ */
+static inline bool mei_cl_is_fixed_address(const struct mei_cl *cl)
+{
+ return cl->me_cl && cl->me_cl->props.fixed_address;
+}
+
+/**
+ * mei_cl_is_single_recv_buf- check whether the me client
+ * uses single receiving buffer
+ *
+ * @cl: host client
+ *
+ * Return: true if single_recv_buf == 1; 0 otherwise
+ */
+static inline bool mei_cl_is_single_recv_buf(const struct mei_cl *cl)
+{
+ return cl->me_cl->props.single_recv_buf;
+}
+
+/**
+ * mei_cl_uuid - client's uuid
+ *
+ * @cl: host client
+ *
+ * Return: return uuid of connected me client
+ */
+static inline const uuid_le *mei_cl_uuid(const struct mei_cl *cl)
+{
+ return mei_me_cl_uuid(cl->me_cl);
+}
+
+/**
+ * mei_cl_host_addr - client's host address
+ *
+ * @cl: host client
+ *
+ * Return: 0 for fixed address client, host address for dynamic client
+ */
+static inline u8 mei_cl_host_addr(const struct mei_cl *cl)
+{
+ return mei_cl_is_fixed_address(cl) ? 0 : cl->host_client_id;
+}
+
int mei_cl_disconnect(struct mei_cl *cl);
-int mei_cl_connect(struct mei_cl *cl, struct file *file);
+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);
+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_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr,
struct mei_cl_cb *cmpl_list);
@@ -117,14 +219,12 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb);
void mei_host_client_init(struct work_struct *work);
-
-
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, (cl)->me_client_id
+#define MEI_CL_PRM(cl) (cl)->host_client_id, mei_cl_me_id(cl)
#define cl_dbg(dev, cl, format, arg...) \
dev_dbg((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 d9cd7e6ee484..eb868341247f 100644
--- a/drivers/misc/mei/debugfs.c
+++ b/drivers/misc/mei/debugfs.c
@@ -116,7 +116,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
pos += scnprintf(buf + pos, bufsz - pos,
"%2d|%2d|%4d|%5d|%2d|%2d|\n",
- i, cl->me_client_id, cl->host_client_id, cl->state,
+ i, mei_cl_me_id(cl), cl->host_client_id, cl->state,
!list_empty(&cl->rd_completed), cl->writing_state);
i++;
}
@@ -149,6 +149,13 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf,
mei_dev_state_str(dev->dev_state));
pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n",
mei_hbm_state_str(dev->hbm_state));
+
+ if (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, "pg: %s, %s\n",
mei_pg_is_enabled(dev) ? "ENABLED" : "DISABLED",
mei_pg_state_str(mei_pg_state(dev)));
@@ -209,6 +216,12 @@ 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);
+ if (!f) {
+ dev_err(dev->dev, "allow_fixed_address: registration failed\n");
+ goto err;
+ }
dev->dbgfs_dir = dir;
return 0;
err:
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index 58da92565c5e..a4f283165a33 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -150,8 +150,8 @@ void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
memset(cmd, 0, len);
cmd->hbm_cmd = hbm_cmd;
- cmd->host_addr = cl->host_client_id;
- cmd->me_addr = cl->me_client_id;
+ cmd->host_addr = mei_cl_host_addr(cl);
+ cmd->me_addr = mei_cl_me_id(cl);
}
/**
@@ -188,8 +188,8 @@ int mei_hbm_cl_write(struct mei_device *dev,
static inline
bool mei_hbm_cl_addr_equal(struct mei_cl *cl, struct mei_hbm_cl_cmd *cmd)
{
- return cl->host_client_id == cmd->host_addr &&
- cl->me_client_id == cmd->me_addr;
+ return mei_cl_host_addr(cl) == cmd->host_addr &&
+ mei_cl_me_id(cl) == cmd->me_addr;
}
/**
@@ -572,7 +572,7 @@ static void mei_hbm_cl_disconnect_res(struct mei_device *dev, struct mei_cl *cl,
cl_dbg(dev, cl, "hbm: disconnect response status=%d\n", rs->status);
if (rs->status == MEI_CL_DISCONN_SUCCESS)
- cl->state = MEI_FILE_DISCONNECTED;
+ cl->state = MEI_FILE_DISCONNECT_REPLY;
cl->status = 0;
}
@@ -611,7 +611,7 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, struct mei_cl *cl,
if (rs->status == MEI_CL_CONN_SUCCESS)
cl->state = MEI_FILE_CONNECTED;
else
- cl->state = MEI_FILE_DISCONNECTED;
+ cl->state = MEI_FILE_DISCONNECT_REPLY;
cl->status = mei_cl_conn_status_to_errno(rs->status);
}
@@ -680,8 +680,8 @@ 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, "disconnect request received\n");
- cl->state = MEI_FILE_DISCONNECTED;
+ cl_dbg(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);
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 6fb75e62a764..43d7101ff993 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -663,11 +663,27 @@ int mei_me_pg_exit_sync(struct mei_device *dev)
mutex_lock(&dev->device_lock);
reply:
- if (dev->pg_event == MEI_PG_EVENT_RECEIVED)
- ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD);
+ if (dev->pg_event != MEI_PG_EVENT_RECEIVED) {
+ ret = -ETIME;
+ goto out;
+ }
+
+ dev->pg_event = MEI_PG_EVENT_INTR_WAIT;
+ ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD);
+ if (ret)
+ return ret;
+
+ mutex_unlock(&dev->device_lock);
+ wait_event_timeout(dev->wait_pg,
+ dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED, timeout);
+ mutex_lock(&dev->device_lock);
+
+ if (dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED)
+ ret = 0;
else
ret = -ETIME;
+out:
dev->pg_event = MEI_PG_EVENT_IDLE;
hw->pg_state = MEI_PG_OFF;
@@ -675,6 +691,19 @@ reply:
}
/**
+ * mei_me_pg_in_transition - is device now in pg transition
+ *
+ * @dev: the device structure
+ *
+ * Return: true if in pg transition, false otherwise
+ */
+static bool mei_me_pg_in_transition(struct mei_device *dev)
+{
+ return dev->pg_event >= MEI_PG_EVENT_WAIT &&
+ dev->pg_event <= MEI_PG_EVENT_INTR_WAIT;
+}
+
+/**
* mei_me_pg_is_enabled - detect if PG is supported by HW
*
* @dev: the device structure
@@ -705,6 +734,24 @@ notsupported:
}
/**
+ * mei_me_pg_intr - perform pg processing in interrupt thread handler
+ *
+ * @dev: the device structure
+ */
+static void mei_me_pg_intr(struct mei_device *dev)
+{
+ struct mei_me_hw *hw = to_me_hw(dev);
+
+ if (dev->pg_event != MEI_PG_EVENT_INTR_WAIT)
+ return;
+
+ dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED;
+ hw->pg_state = MEI_PG_OFF;
+ if (waitqueue_active(&dev->wait_pg))
+ wake_up(&dev->wait_pg);
+}
+
+/**
* mei_me_irq_quick_handler - The ISR of the MEI device
*
* @irq: The irq number
@@ -761,6 +808,8 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
goto end;
}
+ mei_me_pg_intr(dev);
+
/* check if we need to start the dev */
if (!mei_host_is_ready(dev)) {
if (mei_hw_is_ready(dev)) {
@@ -797,9 +846,10 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
/*
* During PG handshake only allowed write is the replay to the
* PG exit message, so block calling write function
- * if the pg state is not idle
+ * if the pg event is in PG handshake
*/
- if (dev->pg_event == MEI_PG_EVENT_IDLE) {
+ if (dev->pg_event != MEI_PG_EVENT_WAIT &&
+ dev->pg_event != MEI_PG_EVENT_RECEIVED) {
rets = mei_irq_write_handler(dev, &complete_list);
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
}
@@ -824,6 +874,7 @@ static const struct mei_hw_ops mei_me_hw_ops = {
.hw_config = mei_me_hw_config,
.hw_start = mei_me_hw_start,
+ .pg_in_transition = mei_me_pg_in_transition,
.pg_is_enabled = mei_me_pg_is_enabled,
.intr_clear = mei_me_intr_clear,
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c
index 7abafe7d120d..bae680c648ff 100644
--- a/drivers/misc/mei/hw-txe.c
+++ b/drivers/misc/mei/hw-txe.c
@@ -16,6 +16,7 @@
#include <linux/pci.h>
#include <linux/jiffies.h>
+#include <linux/ktime.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/irqreturn.h>
@@ -218,26 +219,25 @@ static u32 mei_txe_aliveness_get(struct mei_device *dev)
*
* Polls for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set
*
- * Return: > 0 if the expected value was received, -ETIME otherwise
+ * Return: 0 if the expected value was received, -ETIME otherwise
*/
static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
{
struct mei_txe_hw *hw = to_txe_hw(dev);
- int t = 0;
+ ktime_t stop, start;
+ start = ktime_get();
+ stop = ktime_add(start, ms_to_ktime(SEC_ALIVENESS_WAIT_TIMEOUT));
do {
hw->aliveness = mei_txe_aliveness_get(dev);
if (hw->aliveness == expected) {
dev->pg_event = MEI_PG_EVENT_IDLE;
- dev_dbg(dev->dev,
- "aliveness settled after %d msecs\n", t);
- return t;
+ dev_dbg(dev->dev, "aliveness settled after %lld usecs\n",
+ ktime_to_us(ktime_sub(ktime_get(), start)));
+ return 0;
}
- mutex_unlock(&dev->device_lock);
- msleep(MSEC_PER_SEC / 5);
- mutex_lock(&dev->device_lock);
- t += MSEC_PER_SEC / 5;
- } while (t < SEC_ALIVENESS_WAIT_TIMEOUT);
+ usleep_range(20, 50);
+ } while (ktime_before(ktime_get(), stop));
dev->pg_event = MEI_PG_EVENT_IDLE;
dev_err(dev->dev, "aliveness timed out\n");
@@ -302,6 +302,18 @@ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req)
}
/**
+ * mei_txe_pg_in_transition - is device now in pg transition
+ *
+ * @dev: the device structure
+ *
+ * Return: true if in pg transition, false otherwise
+ */
+static bool mei_txe_pg_in_transition(struct mei_device *dev)
+{
+ return dev->pg_event == MEI_PG_EVENT_WAIT;
+}
+
+/**
* mei_txe_pg_is_enabled - detect if PG is supported by HW
*
* @dev: the device structure
@@ -1138,6 +1150,7 @@ static const struct mei_hw_ops mei_txe_hw_ops = {
.hw_config = mei_txe_hw_config,
.hw_start = mei_txe_hw_start,
+ .pg_in_transition = mei_txe_pg_in_transition,
.pg_is_enabled = mei_txe_pg_is_enabled,
.intr_clear = mei_txe_intr_clear,
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index 97353cf8d9b6..94514b2c7a50 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -361,13 +361,15 @@ bool mei_write_is_idle(struct mei_device *dev)
{
bool idle = (dev->dev_state == MEI_DEV_ENABLED &&
list_empty(&dev->ctrl_wr_list.list) &&
- list_empty(&dev->write_list.list));
+ list_empty(&dev->write_list.list) &&
+ list_empty(&dev->write_waiting_list.list));
- dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d\n",
+ dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%01d write=%01d wwait=%01d\n",
idle,
mei_dev_state_str(dev->dev_state),
list_empty(&dev->ctrl_wr_list.list),
- list_empty(&dev->write_list.list));
+ list_empty(&dev->write_list.list),
+ list_empty(&dev->write_waiting_list.list));
return idle;
}
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index 3f84d2edcde4..3f3405269c39 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -65,8 +65,8 @@ EXPORT_SYMBOL_GPL(mei_irq_compl_handler);
static inline int mei_cl_hbm_equal(struct mei_cl *cl,
struct mei_msg_hdr *mei_hdr)
{
- return cl->host_client_id == mei_hdr->host_addr &&
- cl->me_client_id == mei_hdr->me_addr;
+ return mei_cl_host_addr(cl) == mei_hdr->host_addr &&
+ mei_cl_me_id(cl) == mei_hdr->me_addr;
}
/**
@@ -180,56 +180,14 @@ 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);
-
- cl->state = MEI_FILE_DISCONNECTED;
- cl->status = 0;
+ mei_cl_set_disconnected(cl);
mei_io_cb_free(cb);
+ mei_me_cl_put(cl->me_cl);
+ cl->me_cl = NULL;
return ret;
}
-
-
-/**
- * mei_cl_irq_disconnect - processes close related operation from
- * interrupt thread context - send disconnect request
- *
- * @cl: client
- * @cb: callback block.
- * @cmpl_list: complete list.
- *
- * Return: 0, OK; otherwise, error.
- */
-static int mei_cl_irq_disconnect(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;
-
- msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
- slots = mei_hbuf_empty_slots(dev);
-
- if (slots < msg_slots)
- return -EMSGSIZE;
-
- if (mei_hbm_cl_disconnect_req(dev, cl)) {
- cl->status = 0;
- cb->buf_idx = 0;
- list_move_tail(&cb->list, &cmpl_list->list);
- return -EIO;
- }
-
- cl->state = MEI_FILE_DISCONNECTING;
- cl->status = 0;
- cb->buf_idx = 0;
- list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
- cl->timer_count = MEI_CONNECT_TIMEOUT;
-
- return 0;
-}
-
-
/**
* mei_cl_irq_read - processes client read related operation from the
* interrupt thread context - request for flow control credits
@@ -267,49 +225,6 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
return 0;
}
-
-/**
- * mei_cl_irq_connect - send connect request in irq_thread context
- *
- * @cl: client
- * @cb: callback block.
- * @cmpl_list: complete list.
- *
- * Return: 0, OK; otherwise, error.
- */
-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;
-
- msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
- slots = mei_hbuf_empty_slots(dev);
-
- if (mei_cl_is_other_connecting(cl))
- return 0;
-
- if (slots < msg_slots)
- return -EMSGSIZE;
-
- cl->state = MEI_FILE_CONNECTING;
-
- ret = mei_hbm_cl_connect_req(dev, cl);
- if (ret) {
- cl->status = ret;
- cb->buf_idx = 0;
- list_del_init(&cb->list);
- return ret;
- }
-
- list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
- cl->timer_count = MEI_CONNECT_TIMEOUT;
- return 0;
-}
-
-
/**
* mei_irq_read_handler - bottom half read routine after ISR to
* handle the read processing.
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 3e2968159506..8eb0a9500a90 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -94,7 +94,7 @@ static int mei_release(struct inode *inode, struct file *file)
{
struct mei_cl *cl = file->private_data;
struct mei_device *dev;
- int rets = 0;
+ int rets;
if (WARN_ON(!cl || !cl->dev))
return -ENODEV;
@@ -106,11 +106,8 @@ static int mei_release(struct inode *inode, struct file *file)
rets = mei_amthif_release(dev, file);
goto out;
}
- if (mei_cl_is_connected(cl)) {
- cl->state = MEI_FILE_DISCONNECTING;
- cl_dbg(dev, cl, "disconnecting\n");
- rets = mei_cl_disconnect(cl);
- }
+ rets = mei_cl_disconnect(cl);
+
mei_cl_flush_queues(cl, file);
cl_dbg(dev, cl, "removing\n");
@@ -186,8 +183,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
err = mei_cl_read_start(cl, length, file);
if (err && err != -EBUSY) {
- dev_dbg(dev->dev,
- "mei start read failure with status = %d\n", err);
+ cl_dbg(dev, cl, "mei start read failure status = %d\n", err);
rets = err;
goto out;
}
@@ -218,6 +214,11 @@ 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;
}
@@ -226,11 +227,11 @@ copy_buffer:
/* now copy the data to user space */
if (cb->status) {
rets = cb->status;
- dev_dbg(dev->dev, "read operation failed %d\n", rets);
+ cl_dbg(dev, cl, "read operation failed %d\n", rets);
goto free;
}
- dev_dbg(dev->dev, "buf.size = %d buf.idx= %ld\n",
+ 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;
@@ -256,7 +257,7 @@ free:
mei_io_cb_free(cb);
out:
- dev_dbg(dev->dev, "end mei read rets= %d\n", rets);
+ cl_dbg(dev, cl, "end mei read rets = %d\n", rets);
mutex_unlock(&dev->device_lock);
return rets;
}
@@ -274,7 +275,6 @@ 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_me_client *me_cl = NULL;
struct mei_cl_cb *write_cb = NULL;
struct mei_device *dev;
unsigned long timeout = 0;
@@ -292,27 +292,27 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
goto out;
}
- me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
- if (!me_cl) {
- rets = -ENOTTY;
+ if (!mei_cl_is_connected(cl)) {
+ cl_err(dev, cl, "is not connected");
+ rets = -ENODEV;
goto out;
}
- if (length == 0) {
- rets = 0;
+ if (!mei_me_cl_is_active(cl->me_cl)) {
+ rets = -ENOTTY;
goto out;
}
- if (length > me_cl->props.max_msg_length) {
+ if (length > mei_cl_mtu(cl)) {
rets = -EFBIG;
goto out;
}
- if (!mei_cl_is_connected(cl)) {
- cl_err(dev, cl, "is not connected");
- rets = -ENODEV;
+ if (length == 0) {
+ rets = 0;
goto out;
}
+
if (cl == &dev->iamthif_cl) {
write_cb = mei_amthif_find_read_list_entry(dev, file);
@@ -350,14 +350,12 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
"amthif write failed with status = %d\n", rets);
goto out;
}
- mei_me_cl_put(me_cl);
mutex_unlock(&dev->device_lock);
return length;
}
rets = mei_cl_write(cl, write_cb, false);
out:
- mei_me_cl_put(me_cl);
mutex_unlock(&dev->device_lock);
if (rets < 0)
mei_io_cb_free(write_cb);
@@ -395,17 +393,16 @@ 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) {
+ if (!me_cl ||
+ (me_cl->props.fixed_address && !dev->allow_fixed_address)) {
dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n",
- &data->in_client_uuid);
+ &data->in_client_uuid);
+ mei_me_cl_put(me_cl);
return -ENOTTY;
}
- cl->me_client_id = me_cl->client_id;
- cl->cl_uuid = me_cl->props.protocol_name;
-
dev_dbg(dev->dev, "Connect to FW Client ID = %d\n",
- cl->me_client_id);
+ me_cl->client_id);
dev_dbg(dev->dev, "FW Client - Protocol Version = %d\n",
me_cl->props.protocol_version);
dev_dbg(dev->dev, "FW Client - Max Msg Len = %d\n",
@@ -441,7 +438,7 @@ static int mei_ioctl_connect_client(struct file *file,
client->protocol_version = me_cl->props.protocol_version;
dev_dbg(dev->dev, "Can connect?\n");
- rets = mei_cl_connect(cl, file);
+ rets = mei_cl_connect(cl, me_cl, file);
end:
mei_me_cl_put(me_cl);
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index f066ecd71939..453f6a333b42 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -88,7 +88,8 @@ enum file_state {
MEI_FILE_CONNECTING,
MEI_FILE_CONNECTED,
MEI_FILE_DISCONNECTING,
- MEI_FILE_DISCONNECTED
+ MEI_FILE_DISCONNECT_REPLY,
+ MEI_FILE_DISCONNECTED,
};
/* MEI device states */
@@ -176,6 +177,8 @@ struct mei_fw_status {
* @props: client properties
* @client_id: me client id
* @mei_flow_ctrl_creds: flow control credits
+ * @connect_count: number connections to this client
+ * @reserved: reserved
*/
struct mei_me_client {
struct list_head list;
@@ -183,6 +186,8 @@ struct mei_me_client {
struct mei_client_properties props;
u8 client_id;
u8 mei_flow_ctrl_creds;
+ u8 connect_count;
+ u8 reserved;
};
@@ -226,11 +231,11 @@ struct mei_cl_cb {
* @rx_wait: wait queue for rx completion
* @wait: wait queue for management operation
* @status: connection status
- * @cl_uuid: client uuid name
+ * @me_cl: fw client connected
* @host_client_id: host id
- * @me_client_id: me/fw id
* @mei_flow_ctrl_creds: transmit flow credentials
* @timer_count: watchdog timer for operation completion
+ * @reserved: reserved for alignment
* @writing_state: state of the tx
* @rd_pending: pending read credits
* @rd_completed: completed read
@@ -246,11 +251,11 @@ struct mei_cl {
wait_queue_head_t rx_wait;
wait_queue_head_t wait;
int status;
- uuid_le cl_uuid;
+ struct mei_me_client *me_cl;
u8 host_client_id;
- u8 me_client_id;
u8 mei_flow_ctrl_creds;
u8 timer_count;
+ u8 reserved;
enum mei_file_transaction_states writing_state;
struct list_head rd_pending;
struct list_head rd_completed;
@@ -271,6 +276,7 @@ struct mei_cl {
* @fw_status : get fw status registers
* @pg_state : power gating state of the device
+ * @pg_in_transition : is device now in pg transition
* @pg_is_enabled : is power gating enabled
* @intr_clear : clear pending interrupts
@@ -300,6 +306,7 @@ struct mei_hw_ops {
int (*fw_status)(struct mei_device *dev, struct mei_fw_status *fw_sts);
enum mei_pg_state (*pg_state)(struct mei_device *dev);
+ bool (*pg_in_transition)(struct mei_device *dev);
bool (*pg_is_enabled)(struct mei_device *dev);
void (*intr_clear)(struct mei_device *dev);
@@ -323,34 +330,14 @@ struct mei_hw_ops {
/* MEI bus API*/
-/**
- * struct mei_cl_ops - MEI CL device ops
- * This structure allows ME host clients to implement technology
- * specific operations.
- *
- * @enable: Enable an MEI CL device. Some devices require specific
- * HECI commands to initialize completely.
- * @disable: Disable an MEI CL device.
- * @send: Tx hook for the device. This allows ME host clients to trap
- * the device driver buffers before actually physically
- * pushing it to the ME.
- * @recv: Rx hook for the device. This allows ME host clients to trap the
- * ME buffers before forwarding them to the device driver.
- */
-struct mei_cl_ops {
- int (*enable)(struct mei_cl_device *device);
- int (*disable)(struct mei_cl_device *device);
- int (*send)(struct mei_cl_device *device, u8 *buf, size_t length);
- int (*recv)(struct mei_cl_device *device, u8 *buf, size_t length);
-};
-
struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
- uuid_le uuid, char *name,
- struct mei_cl_ops *ops);
+ struct mei_me_client *me_cl,
+ struct mei_cl *cl,
+ char *name);
void mei_cl_remove_device(struct mei_cl_device *device);
-ssize_t __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length);
-ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length);
+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_remove_devices(struct mei_device *dev);
@@ -358,51 +345,21 @@ int mei_cl_bus_init(void);
void mei_cl_bus_exit(void);
struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *dev, uuid_le uuid);
-
-/**
- * struct mei_cl_device - MEI device handle
- * An mei_cl_device pointer is returned from mei_add_device()
- * and links MEI bus clients to their actual ME host client pointer.
- * Drivers for MEI devices will get an mei_cl_device pointer
- * when being probed and shall use it for doing ME bus I/O.
- *
- * @dev: linux driver model device pointer
- * @cl: mei client
- * @ops: ME transport ops
- * @event_work: async work to execute event callback
- * @event_cb: Drivers register this callback to get asynchronous ME
- * events (e.g. Rx buffer pending) notifications.
- * @event_context: event callback run context
- * @events: Events bitmask sent to the driver.
- * @priv_data: client private data
- */
-struct mei_cl_device {
- struct device dev;
-
- struct mei_cl *cl;
-
- const struct mei_cl_ops *ops;
-
- struct work_struct event_work;
- mei_cl_event_cb_t event_cb;
- void *event_context;
- unsigned long events;
-
- void *priv_data;
-};
-
-
/**
* enum mei_pg_event - power gating transition events
*
* @MEI_PG_EVENT_IDLE: the driver is not in power gating transition
* @MEI_PG_EVENT_WAIT: the driver is waiting for a pg event to complete
* @MEI_PG_EVENT_RECEIVED: the driver received pg event
+ * @MEI_PG_EVENT_INTR_WAIT: the driver is waiting for a pg event interrupt
+ * @MEI_PG_EVENT_INTR_RECEIVED: the driver received pg event interrupt
*/
enum mei_pg_event {
MEI_PG_EVENT_IDLE,
MEI_PG_EVENT_WAIT,
MEI_PG_EVENT_RECEIVED,
+ MEI_PG_EVENT_INTR_WAIT,
+ MEI_PG_EVENT_INTR_RECEIVED,
};
/**
@@ -467,6 +424,8 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @host_clients_map : host clients id pool
* @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
@@ -479,7 +438,6 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @iamthif_cl : amthif host client
* @iamthif_current_cb : amthif current operation callback
* @iamthif_open_count : number of opened amthif connections
- * @iamthif_mtu : amthif client max message length
* @iamthif_timer : time stamp of current amthif command completion
* @iamthif_stall_timer : timer to detect amthif hang
* @iamthif_state : amthif processor state
@@ -558,6 +516,8 @@ struct mei_device {
DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX);
unsigned long me_client_index;
+ u32 allow_fixed_address;
+
struct mei_cl wd_cl;
enum mei_wd_states wd_state;
bool wd_pending;
@@ -573,7 +533,6 @@ struct mei_device {
struct mei_cl iamthif_cl;
struct mei_cl_cb *iamthif_current_cb;
long iamthif_open_count;
- int iamthif_mtu;
unsigned long iamthif_timer;
u32 iamthif_stall_timer;
enum iamthif_states iamthif_state;
@@ -652,7 +611,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list);
*/
void mei_amthif_reset_params(struct mei_device *dev);
-int mei_amthif_host_init(struct mei_device *dev);
+int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl);
int mei_amthif_read(struct mei_device *dev, struct file *file,
char __user *ubuf, size_t length, loff_t *offset);
@@ -679,7 +638,7 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots);
/*
* NFC functions
*/
-int mei_nfc_host_init(struct mei_device *dev);
+int mei_nfc_host_init(struct mei_device *dev, struct mei_me_client *me_cl);
void mei_nfc_host_exit(struct mei_device *dev);
/*
@@ -689,7 +648,7 @@ 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);
+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
@@ -717,6 +676,11 @@ static inline enum mei_pg_state mei_pg_state(struct mei_device *dev)
return dev->ops->pg_state(dev);
}
+static inline bool mei_pg_in_transition(struct mei_device *dev)
+{
+ return dev->ops->pg_in_transition(dev);
+}
+
static inline bool mei_pg_is_enabled(struct mei_device *dev)
{
return dev->ops->pg_is_enabled(dev);
diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c
index c3bcb63686d7..b983c4ecad38 100644
--- a/drivers/misc/mei/nfc.c
+++ b/drivers/misc/mei/nfc.c
@@ -91,30 +91,25 @@ struct mei_nfc_hci_hdr {
/**
* struct mei_nfc_dev - NFC mei device
*
+ * @me_cl: NFC me client
* @cl: NFC host client
* @cl_info: NFC info host client
* @init_work: perform connection to the info client
- * @send_wq: send completion wait queue
* @fw_ivn: NFC Interface Version Number
* @vendor_id: NFC manufacturer ID
* @radio_type: NFC radio type
* @bus_name: bus name
*
- * @req_id: message counter
- * @recv_req_id: reception message counter
*/
struct mei_nfc_dev {
+ struct mei_me_client *me_cl;
struct mei_cl *cl;
struct mei_cl *cl_info;
struct work_struct init_work;
- wait_queue_head_t send_wq;
u8 fw_ivn;
u8 vendor_id;
u8 radio_type;
char *bus_name;
-
- u16 req_id;
- u16 recv_req_id;
};
/* UUIDs for NFC F/W clients */
@@ -151,6 +146,7 @@ static void mei_nfc_free(struct mei_nfc_dev *ndev)
kfree(ndev->cl_info);
}
+ mei_me_cl_put(ndev->me_cl);
kfree(ndev);
}
@@ -199,73 +195,6 @@ static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev)
return 0;
}
-static int mei_nfc_connect(struct mei_nfc_dev *ndev)
-{
- struct mei_device *dev;
- struct mei_cl *cl;
- struct mei_nfc_cmd *cmd, *reply;
- struct mei_nfc_connect *connect;
- struct mei_nfc_connect_resp *connect_resp;
- size_t connect_length, connect_resp_length;
- int bytes_recv, ret;
-
- cl = ndev->cl;
- dev = cl->dev;
-
- connect_length = sizeof(struct mei_nfc_cmd) +
- sizeof(struct mei_nfc_connect);
-
- connect_resp_length = sizeof(struct mei_nfc_cmd) +
- sizeof(struct mei_nfc_connect_resp);
-
- cmd = kzalloc(connect_length, GFP_KERNEL);
- if (!cmd)
- return -ENOMEM;
- connect = (struct mei_nfc_connect *)cmd->data;
-
- reply = kzalloc(connect_resp_length, GFP_KERNEL);
- if (!reply) {
- kfree(cmd);
- return -ENOMEM;
- }
-
- connect_resp = (struct mei_nfc_connect_resp *)reply->data;
-
- cmd->command = MEI_NFC_CMD_MAINTENANCE;
- cmd->data_size = 3;
- cmd->sub_command = MEI_NFC_SUBCMD_CONNECT;
- connect->fw_ivn = ndev->fw_ivn;
- connect->vendor_id = ndev->vendor_id;
-
- ret = __mei_cl_send(cl, (u8 *)cmd, connect_length);
- if (ret < 0) {
- dev_err(dev->dev, "Could not send connect cmd\n");
- goto err;
- }
-
- bytes_recv = __mei_cl_recv(cl, (u8 *)reply, connect_resp_length);
- if (bytes_recv < 0) {
- dev_err(dev->dev, "Could not read connect response\n");
- ret = bytes_recv;
- goto err;
- }
-
- dev_info(dev->dev, "IVN 0x%x Vendor ID 0x%x\n",
- connect_resp->fw_ivn, connect_resp->vendor_id);
-
- dev_info(dev->dev, "ME FW %d.%d.%d.%d\n",
- connect_resp->me_major, connect_resp->me_minor,
- connect_resp->me_hotfix, connect_resp->me_build);
-
- ret = 0;
-
-err:
- kfree(reply);
- kfree(cmd);
-
- return ret;
-}
-
static int mei_nfc_if_version(struct mei_nfc_dev *ndev)
{
struct mei_device *dev;
@@ -285,7 +214,7 @@ static int mei_nfc_if_version(struct mei_nfc_dev *ndev)
cmd.data_size = 1;
cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION;
- ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd));
+ ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd), 1);
if (ret < 0) {
dev_err(dev->dev, "Could not send IF version cmd\n");
return ret;
@@ -317,106 +246,13 @@ err:
return ret;
}
-static int mei_nfc_enable(struct mei_cl_device *cldev)
-{
- struct mei_device *dev;
- struct mei_nfc_dev *ndev;
- int ret;
-
- ndev = (struct mei_nfc_dev *)cldev->priv_data;
- dev = ndev->cl->dev;
-
- ret = mei_nfc_connect(ndev);
- if (ret < 0) {
- dev_err(dev->dev, "Could not connect to NFC");
- return ret;
- }
-
- return 0;
-}
-
-static int mei_nfc_disable(struct mei_cl_device *cldev)
-{
- return 0;
-}
-
-static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length)
-{
- struct mei_device *dev;
- struct mei_nfc_dev *ndev;
- struct mei_nfc_hci_hdr *hdr;
- u8 *mei_buf;
- int err;
-
- ndev = (struct mei_nfc_dev *) cldev->priv_data;
- dev = ndev->cl->dev;
-
- err = -ENOMEM;
- mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL);
- if (!mei_buf)
- goto out;
-
- hdr = (struct mei_nfc_hci_hdr *) mei_buf;
- hdr->cmd = MEI_NFC_CMD_HCI_SEND;
- hdr->status = 0;
- hdr->req_id = ndev->req_id;
- hdr->reserved = 0;
- hdr->data_size = length;
-
- memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length);
- err = __mei_cl_send(ndev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE);
- if (err < 0)
- goto out;
-
- if (!wait_event_interruptible_timeout(ndev->send_wq,
- ndev->recv_req_id == ndev->req_id, HZ)) {
- dev_err(dev->dev, "NFC MEI command timeout\n");
- err = -ETIME;
- } else {
- ndev->req_id++;
- }
-out:
- kfree(mei_buf);
- return err;
-}
-
-static int mei_nfc_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
-{
- struct mei_nfc_dev *ndev;
- struct mei_nfc_hci_hdr *hci_hdr;
- int received_length;
-
- ndev = (struct mei_nfc_dev *)cldev->priv_data;
-
- received_length = __mei_cl_recv(ndev->cl, buf, length);
- if (received_length < 0)
- return received_length;
-
- hci_hdr = (struct mei_nfc_hci_hdr *) buf;
-
- if (hci_hdr->cmd == MEI_NFC_CMD_HCI_SEND) {
- ndev->recv_req_id = hci_hdr->req_id;
- wake_up(&ndev->send_wq);
-
- return 0;
- }
-
- return received_length;
-}
-
-static struct mei_cl_ops nfc_ops = {
- .enable = mei_nfc_enable,
- .disable = mei_nfc_disable,
- .send = mei_nfc_send,
- .recv = mei_nfc_recv,
-};
-
static void mei_nfc_init(struct work_struct *work)
{
struct mei_device *dev;
struct mei_cl_device *cldev;
struct mei_nfc_dev *ndev;
struct mei_cl *cl_info;
+ struct mei_me_client *me_cl_info;
ndev = container_of(work, struct mei_nfc_dev, init_work);
@@ -425,13 +261,22 @@ static void mei_nfc_init(struct work_struct *work)
mutex_lock(&dev->device_lock);
- if (mei_cl_connect(cl_info, NULL) < 0) {
+ /* check for valid client id */
+ me_cl_info = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid);
+ if (!me_cl_info) {
+ mutex_unlock(&dev->device_lock);
+ dev_info(dev->dev, "nfc: failed to find the info client\n");
+ goto err;
+ }
+
+ if (mei_cl_connect(cl_info, me_cl_info, NULL) < 0) {
+ mei_me_cl_put(me_cl_info);
mutex_unlock(&dev->device_lock);
dev_err(dev->dev, "Could not connect to the NFC INFO ME client");
goto err;
}
-
+ mei_me_cl_put(me_cl_info);
mutex_unlock(&dev->device_lock);
if (mei_nfc_if_version(ndev) < 0) {
@@ -459,7 +304,8 @@ static void mei_nfc_init(struct work_struct *work)
return;
}
- cldev = mei_cl_add_device(dev, mei_nfc_guid, ndev->bus_name, &nfc_ops);
+ cldev = mei_cl_add_device(dev, ndev->me_cl, ndev->cl,
+ ndev->bus_name);
if (!cldev) {
dev_err(dev->dev, "Could not add the NFC device to the MEI bus\n");
@@ -479,11 +325,10 @@ err:
}
-int mei_nfc_host_init(struct mei_device *dev)
+int mei_nfc_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
{
struct mei_nfc_dev *ndev;
struct mei_cl *cl_info, *cl;
- struct mei_me_client *me_cl = NULL;
int ret;
@@ -500,11 +345,9 @@ int mei_nfc_host_init(struct mei_device *dev)
goto err;
}
- /* check for valid client id */
- me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid);
- if (!me_cl) {
- dev_info(dev->dev, "nfc: failed to find the client\n");
- ret = -ENOTTY;
+ ndev->me_cl = mei_me_cl_get(me_cl);
+ if (!ndev->me_cl) {
+ ret = -ENODEV;
goto err;
}
@@ -514,48 +357,26 @@ int mei_nfc_host_init(struct mei_device *dev)
goto err;
}
- cl_info->me_client_id = me_cl->client_id;
- cl_info->cl_uuid = me_cl->props.protocol_name;
- mei_me_cl_put(me_cl);
- me_cl = NULL;
-
list_add_tail(&cl_info->device_link, &dev->device_list);
ndev->cl_info = cl_info;
- /* check for valid client id */
- me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid);
- if (!me_cl) {
- dev_info(dev->dev, "nfc: failed to find the client\n");
- ret = -ENOTTY;
- goto err;
- }
-
cl = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY);
if (IS_ERR(cl)) {
ret = PTR_ERR(cl);
goto err;
}
- cl->me_client_id = me_cl->client_id;
- cl->cl_uuid = me_cl->props.protocol_name;
- mei_me_cl_put(me_cl);
- me_cl = NULL;
-
list_add_tail(&cl->device_link, &dev->device_list);
ndev->cl = cl;
- ndev->req_id = 1;
-
INIT_WORK(&ndev->init_work, mei_nfc_init);
- init_waitqueue_head(&ndev->send_wq);
schedule_work(&ndev->init_work);
return 0;
err:
- mei_me_cl_put(me_cl);
mei_nfc_free(ndev);
return ret;
diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c
index dcfcba44b6f7..0882c0201907 100644
--- a/drivers/misc/mei/pci-txe.c
+++ b/drivers/misc/mei/pci-txe.c
@@ -338,7 +338,7 @@ static int mei_txe_pm_runtime_suspend(struct device *device)
* However if device is not wakeable we do not enter
* D-low state and we need to keep the interrupt kicking
*/
- if (!ret && pci_dev_run_wake(pdev))
+ if (!ret && pci_dev_run_wake(pdev))
mei_disable_interrupts(dev);
dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret);
diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c
index 2725f865c3d6..2bc0f5089f82 100644
--- a/drivers/misc/mei/wd.c
+++ b/drivers/misc/mei/wd.c
@@ -50,15 +50,15 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)
* 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)
+int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
{
struct mei_cl *cl = &dev->wd_cl;
- struct mei_me_client *me_cl;
int ret;
mei_cl_init(cl, dev);
@@ -66,27 +66,13 @@ int mei_wd_host_init(struct mei_device *dev)
dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT;
dev->wd_state = MEI_WD_IDLE;
-
- /* check for valid client id */
- me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid);
- if (!me_cl) {
- dev_info(dev->dev, "wd: failed to find the client\n");
- return -ENOTTY;
- }
-
- cl->me_client_id = me_cl->client_id;
- cl->cl_uuid = me_cl->props.protocol_name;
- mei_me_cl_put(me_cl);
-
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, NULL);
-
+ 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);
@@ -118,7 +104,7 @@ int mei_wd_send(struct mei_device *dev)
int ret;
hdr.host_addr = cl->host_client_id;
- hdr.me_addr = cl->me_client_id;
+ hdr.me_addr = mei_cl_me_id(cl);
hdr.msg_complete = 1;
hdr.reserved = 0;
hdr.internal = 0;