diff options
Diffstat (limited to 'drivers/misc/mei')
-rw-r--r-- | drivers/misc/mei/amthif.c | 28 | ||||
-rw-r--r-- | drivers/misc/mei/bus.c | 150 | ||||
-rw-r--r-- | drivers/misc/mei/client.c | 473 | ||||
-rw-r--r-- | drivers/misc/mei/client.h | 114 | ||||
-rw-r--r-- | drivers/misc/mei/debugfs.c | 15 | ||||
-rw-r--r-- | drivers/misc/mei/hbm.c | 16 | ||||
-rw-r--r-- | drivers/misc/mei/hw-me.c | 59 | ||||
-rw-r--r-- | drivers/misc/mei/hw-txe.c | 33 | ||||
-rw-r--r-- | drivers/misc/mei/init.c | 8 | ||||
-rw-r--r-- | drivers/misc/mei/interrupt.c | 95 | ||||
-rw-r--r-- | drivers/misc/mei/main.c | 57 | ||||
-rw-r--r-- | drivers/misc/mei/mei_dev.h | 102 | ||||
-rw-r--r-- | drivers/misc/mei/nfc.c | 223 | ||||
-rw-r--r-- | drivers/misc/mei/pci-txe.c | 2 | ||||
-rw-r--r-- | drivers/misc/mei/wd.c | 22 |
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; |