summaryrefslogtreecommitdiff
path: root/drivers/misc/mei
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/mei')
-rw-r--r--drivers/misc/mei/bus-fixup.c92
-rw-r--r--drivers/misc/mei/bus.c36
-rw-r--r--drivers/misc/mei/client.c128
-rw-r--r--drivers/misc/mei/client.h2
-rw-r--r--drivers/misc/mei/debugfs.c2
-rw-r--r--drivers/misc/mei/hbm.c37
-rw-r--r--drivers/misc/mei/hw-me.c77
-rw-r--r--drivers/misc/mei/hw-me.h6
-rw-r--r--drivers/misc/mei/hw-txe.c66
-rw-r--r--drivers/misc/mei/hw.h73
-rw-r--r--drivers/misc/mei/interrupt.c17
-rw-r--r--drivers/misc/mei/main.c41
-rw-r--r--drivers/misc/mei/mei_dev.h77
13 files changed, 479 insertions, 175 deletions
diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c
index 0208c4b027c5..a6f41f96f2a1 100644
--- a/drivers/misc/mei/bus-fixup.c
+++ b/drivers/misc/mei/bus-fixup.c
@@ -1,7 +1,7 @@
/*
*
* Intel Management Engine Interface (Intel MEI) Linux driver
- * Copyright (c) 2003-2013, Intel Corporation.
+ * Copyright (c) 2003-2018, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -96,8 +96,22 @@ struct mkhi_fwcaps {
u8 data[0];
} __packed;
+struct mkhi_fw_ver_block {
+ u16 minor;
+ u8 major;
+ u8 platform;
+ u16 buildno;
+ u16 hotfix;
+} __packed;
+
+struct mkhi_fw_ver {
+ struct mkhi_fw_ver_block ver[MEI_MAX_FW_VER_BLOCKS];
+} __packed;
+
#define MKHI_FWCAPS_GROUP_ID 0x3
#define MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD 6
+#define MKHI_GEN_GROUP_ID 0xFF
+#define MKHI_GEN_GET_FW_VERSION_CMD 0x2
struct mkhi_msg_hdr {
u8 group_id;
u8 command;
@@ -139,21 +153,81 @@ static int mei_osver(struct mei_cl_device *cldev)
return __mei_cl_send(cldev->cl, buf, size, mode);
}
+#define MKHI_FWVER_BUF_LEN (sizeof(struct mkhi_msg_hdr) + \
+ sizeof(struct mkhi_fw_ver))
+#define MKHI_FWVER_LEN(__num) (sizeof(struct mkhi_msg_hdr) + \
+ sizeof(struct mkhi_fw_ver_block) * (__num))
+#define MKHI_RCV_TIMEOUT 500 /* receive timeout in msec */
+static int mei_fwver(struct mei_cl_device *cldev)
+{
+ char buf[MKHI_FWVER_BUF_LEN];
+ struct mkhi_msg *req;
+ struct mkhi_fw_ver *fwver;
+ int bytes_recv, ret, i;
+
+ memset(buf, 0, sizeof(buf));
+
+ req = (struct mkhi_msg *)buf;
+ req->hdr.group_id = MKHI_GEN_GROUP_ID;
+ req->hdr.command = MKHI_GEN_GET_FW_VERSION_CMD;
+
+ ret = __mei_cl_send(cldev->cl, buf, sizeof(struct mkhi_msg_hdr),
+ MEI_CL_IO_TX_BLOCKING);
+ if (ret < 0) {
+ dev_err(&cldev->dev, "Could not send ReqFWVersion cmd\n");
+ return ret;
+ }
+
+ ret = 0;
+ bytes_recv = __mei_cl_recv(cldev->cl, buf, sizeof(buf), 0,
+ MKHI_RCV_TIMEOUT);
+ if (bytes_recv < 0 || (size_t)bytes_recv < MKHI_FWVER_LEN(1)) {
+ /*
+ * Should be at least one version block,
+ * error out if nothing found
+ */
+ dev_err(&cldev->dev, "Could not read FW version\n");
+ return -EIO;
+ }
+
+ fwver = (struct mkhi_fw_ver *)req->data;
+ memset(cldev->bus->fw_ver, 0, sizeof(cldev->bus->fw_ver));
+ for (i = 0; i < MEI_MAX_FW_VER_BLOCKS; i++) {
+ if ((size_t)bytes_recv < MKHI_FWVER_LEN(i + 1))
+ break;
+ dev_dbg(&cldev->dev, "FW version%d %d:%d.%d.%d.%d\n",
+ i, fwver->ver[i].platform,
+ fwver->ver[i].major, fwver->ver[i].minor,
+ fwver->ver[i].hotfix, fwver->ver[i].buildno);
+
+ cldev->bus->fw_ver[i].platform = fwver->ver[i].platform;
+ cldev->bus->fw_ver[i].major = fwver->ver[i].major;
+ cldev->bus->fw_ver[i].minor = fwver->ver[i].minor;
+ cldev->bus->fw_ver[i].hotfix = fwver->ver[i].hotfix;
+ cldev->bus->fw_ver[i].buildno = fwver->ver[i].buildno;
+ }
+
+ return ret;
+}
+
static void mei_mkhi_fix(struct mei_cl_device *cldev)
{
int ret;
- if (!cldev->bus->hbm_f_os_supported)
- return;
-
ret = mei_cldev_enable(cldev);
if (ret)
return;
- ret = mei_osver(cldev);
+ ret = mei_fwver(cldev);
if (ret < 0)
- dev_err(&cldev->dev, "OS version command failed %d\n", ret);
+ dev_err(&cldev->dev, "FW version command failed %d\n", ret);
+ if (cldev->bus->hbm_f_os_supported) {
+ ret = mei_osver(cldev);
+ if (ret < 0)
+ dev_err(&cldev->dev, "OS version command failed %d\n",
+ ret);
+ }
mei_cldev_disable(cldev);
}
@@ -266,8 +340,8 @@ static int mei_nfc_if_version(struct mei_cl *cl,
return -ENOMEM;
ret = 0;
- bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length, 0);
- if (bytes_recv < if_version_length) {
+ bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length, 0, 0);
+ if (bytes_recv < 0 || (size_t)bytes_recv < if_version_length) {
dev_err(bus->dev, "Could not read IF version\n");
ret = -EIO;
goto err;
@@ -410,7 +484,7 @@ void mei_cl_bus_dev_fixup(struct mei_cl_device *cldev)
{
struct mei_fixup *f;
const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
- int i;
+ size_t i;
for (i = 0; i < ARRAY_SIZE(mei_fixups); i++) {
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index b1133739fb4b..7bba62a72921 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -116,11 +116,12 @@ out:
* @buf: buffer to receive
* @length: buffer length
* @mode: io mode
+ * @timeout: recv timeout, 0 for infinite timeout
*
* Return: read size in bytes of < 0 on error
*/
ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length,
- unsigned int mode)
+ unsigned int mode, unsigned long timeout)
{
struct mei_device *bus;
struct mei_cl_cb *cb;
@@ -158,13 +159,28 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length,
mutex_unlock(&bus->device_lock);
- if (wait_event_interruptible(cl->rx_wait,
- (!list_empty(&cl->rd_completed)) ||
- (!mei_cl_is_connected(cl)))) {
-
- if (signal_pending(current))
- return -EINTR;
- return -ERESTARTSYS;
+ if (timeout) {
+ rets = wait_event_interruptible_timeout
+ (cl->rx_wait,
+ (!list_empty(&cl->rd_completed)) ||
+ (!mei_cl_is_connected(cl)),
+ msecs_to_jiffies(timeout));
+ if (rets == 0)
+ return -ETIME;
+ if (rets < 0) {
+ if (signal_pending(current))
+ return -EINTR;
+ return -ERESTARTSYS;
+ }
+ } else {
+ if (wait_event_interruptible
+ (cl->rx_wait,
+ (!list_empty(&cl->rd_completed)) ||
+ (!mei_cl_is_connected(cl)))) {
+ if (signal_pending(current))
+ return -EINTR;
+ return -ERESTARTSYS;
+ }
}
mutex_lock(&bus->device_lock);
@@ -231,7 +247,7 @@ ssize_t mei_cldev_recv_nonblock(struct mei_cl_device *cldev, u8 *buf,
{
struct mei_cl *cl = cldev->cl;
- return __mei_cl_recv(cl, buf, length, MEI_CL_IO_RX_NONBLOCK);
+ return __mei_cl_recv(cl, buf, length, MEI_CL_IO_RX_NONBLOCK, 0);
}
EXPORT_SYMBOL_GPL(mei_cldev_recv_nonblock);
@@ -248,7 +264,7 @@ ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
{
struct mei_cl *cl = cldev->cl;
- return __mei_cl_recv(cl, buf, length, 0);
+ return __mei_cl_recv(cl, buf, length, 0, 0);
}
EXPORT_SYMBOL_GPL(mei_cldev_recv);
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 8d6197a88b54..4ab6251d418e 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -863,10 +863,12 @@ int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
int slots;
int ret;
- msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
+ msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_request));
slots = mei_hbuf_empty_slots(dev);
+ if (slots < 0)
+ return -EOVERFLOW;
- if (slots < msg_slots)
+ if ((u32)slots < msg_slots)
return -EMSGSIZE;
ret = mei_cl_send_disconnect(cl, cb);
@@ -1053,13 +1055,15 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
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)
+ msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_request));
+ slots = mei_hbuf_empty_slots(dev);
+ if (slots < 0)
+ return -EOVERFLOW;
+
+ if ((u32)slots < msg_slots)
return -EMSGSIZE;
rets = mei_cl_send_connect(cl, cb);
@@ -1294,10 +1298,12 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
int ret;
bool request;
- msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
+ msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_request));
slots = mei_hbuf_empty_slots(dev);
+ if (slots < 0)
+ return -EOVERFLOW;
- if (slots < msg_slots)
+ if ((u32)slots < msg_slots)
return -EMSGSIZE;
request = mei_cl_notify_fop2req(cb->fop_type);
@@ -1533,6 +1539,23 @@ nortpm:
}
/**
+ * mei_msg_hdr_init - initialize mei message header
+ *
+ * @mei_hdr: mei message header
+ * @cb: message callback structure
+ */
+static void mei_msg_hdr_init(struct mei_msg_hdr *mei_hdr, struct mei_cl_cb *cb)
+{
+ mei_hdr->host_addr = mei_cl_host_addr(cb->cl);
+ mei_hdr->me_addr = mei_cl_me_id(cb->cl);
+ mei_hdr->length = 0;
+ mei_hdr->reserved = 0;
+ mei_hdr->msg_complete = 0;
+ mei_hdr->dma_ring = 0;
+ mei_hdr->internal = cb->internal;
+}
+
+/**
* mei_cl_irq_write - write a message to device
* from the interrupt thread context
*
@@ -1548,9 +1571,10 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_device *dev;
struct mei_msg_data *buf;
struct mei_msg_hdr mei_hdr;
+ size_t hdr_len = sizeof(mei_hdr);
size_t len;
- u32 msg_slots;
- int slots;
+ size_t hbuf_len;
+ int hbuf_slots;
int rets;
bool first_chunk;
@@ -1572,40 +1596,41 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
return 0;
}
- slots = mei_hbuf_empty_slots(dev);
len = buf->size - cb->buf_idx;
- msg_slots = mei_data2slots(len);
+ hbuf_slots = mei_hbuf_empty_slots(dev);
+ if (hbuf_slots < 0) {
+ rets = -EOVERFLOW;
+ goto err;
+ }
- 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;
+ hbuf_len = mei_slots2data(hbuf_slots);
- if (slots >= msg_slots) {
+ mei_msg_hdr_init(&mei_hdr, cb);
+
+ /**
+ * Split the message only if we can write the whole host buffer
+ * otherwise wait for next time the host buffer is empty.
+ */
+ if (len + hdr_len <= hbuf_len) {
mei_hdr.length = len;
mei_hdr.msg_complete = 1;
- /* Split the message only if we can write the whole host buffer */
- } else if (slots == dev->hbuf_depth) {
- msg_slots = slots;
- len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
- mei_hdr.length = len;
- mei_hdr.msg_complete = 0;
+ } else if ((u32)hbuf_slots == mei_hbuf_depth(dev)) {
+ mei_hdr.length = hbuf_len - hdr_len;
} else {
- /* wait for next time the host buffer is empty */
return 0;
}
cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n",
cb->buf.size, cb->buf_idx);
- rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
+ rets = mei_write_message(dev, &mei_hdr, hdr_len,
+ buf->data + cb->buf_idx, mei_hdr.length);
if (rets)
goto err;
cl->status = 0;
cl->writing_state = MEI_WRITING;
cb->buf_idx += mei_hdr.length;
- cb->completed = mei_hdr.msg_complete == 1;
if (first_chunk) {
if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) {
@@ -1634,13 +1659,16 @@ err:
*
* Return: number of bytes sent on success, <0 on failure.
*/
-int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
+ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
{
struct mei_device *dev;
struct mei_msg_data *buf;
struct mei_msg_hdr mei_hdr;
- int size;
- int rets;
+ size_t hdr_len = sizeof(mei_hdr);
+ size_t len;
+ size_t hbuf_len;
+ int hbuf_slots;
+ ssize_t rets;
bool blocking;
if (WARN_ON(!cl || !cl->dev))
@@ -1652,52 +1680,57 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
dev = cl->dev;
buf = &cb->buf;
- size = buf->size;
+ len = buf->size;
blocking = cb->blocking;
- cl_dbg(dev, cl, "size=%d\n", size);
+ cl_dbg(dev, cl, "len=%zd\n", len);
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);
+ cl_err(dev, cl, "rpm: get failed %zd\n", rets);
goto free;
}
cb->buf_idx = 0;
cl->writing_state = MEI_IDLE;
- 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;
rets = mei_cl_tx_flow_ctrl_creds(cl);
if (rets < 0)
goto err;
+ mei_msg_hdr_init(&mei_hdr, cb);
+
if (rets == 0) {
cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
- rets = size;
+ rets = len;
goto out;
}
+
if (!mei_hbuf_acquire(dev)) {
cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
- rets = size;
+ rets = len;
goto out;
}
- /* Check for a maximum length */
- if (size > mei_hbuf_max_len(dev)) {
- mei_hdr.length = mei_hbuf_max_len(dev);
- mei_hdr.msg_complete = 0;
- } else {
- mei_hdr.length = size;
+ hbuf_slots = mei_hbuf_empty_slots(dev);
+ if (hbuf_slots < 0) {
+ rets = -EOVERFLOW;
+ goto out;
+ }
+
+ hbuf_len = mei_slots2data(hbuf_slots);
+
+ if (len + hdr_len <= hbuf_len) {
+ mei_hdr.length = len;
mei_hdr.msg_complete = 1;
+ } else {
+ mei_hdr.length = hbuf_len - hdr_len;
}
- rets = mei_write_message(dev, &mei_hdr, buf->data);
+ rets = mei_write_message(dev, &mei_hdr, hdr_len,
+ buf->data, mei_hdr.length);
if (rets)
goto err;
@@ -1707,7 +1740,6 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
cl->writing_state = MEI_WRITING;
cb->buf_idx = mei_hdr.length;
- cb->completed = mei_hdr.msg_complete == 1;
out:
if (mei_hdr.msg_complete)
@@ -1735,7 +1767,7 @@ out:
}
}
- rets = size;
+ rets = buf->size;
err:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(dev->dev);
diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h
index 5371df4d8af3..64e318f589b4 100644
--- a/drivers/misc/mei/client.h
+++ b/drivers/misc/mei/client.h
@@ -202,7 +202,7 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
struct list_head *cmpl_list);
int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp);
-int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb);
+ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb);
int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
struct list_head *cmpl_list);
diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c
index c815da91089c..7b5df8fd6c5a 100644
--- a/drivers/misc/mei/debugfs.c
+++ b/drivers/misc/mei/debugfs.c
@@ -183,6 +183,8 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf,
dev->hbm_f_fa_supported);
pos += scnprintf(buf + pos, bufsz - pos, "\tOS: %01d\n",
dev->hbm_f_os_supported);
+ pos += scnprintf(buf + pos, bufsz - pos, "\tDR: %01d\n",
+ dev->hbm_f_dr_supported);
}
pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n",
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index fe6595fe94f1..09e233d4c0de 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -96,6 +96,20 @@ static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
}
/**
+ * mei_hbm_write_message - wrapper for sending hbm messages.
+ *
+ * @dev: mei device
+ * @hdr: mei header
+ * @data: payload
+ */
+static inline int mei_hbm_write_message(struct mei_device *dev,
+ struct mei_msg_hdr *hdr,
+ const void *data)
+{
+ return mei_write_message(dev, hdr, sizeof(*hdr), data, hdr->length);
+}
+
+/**
* mei_hbm_idle - set hbm to idle state
*
* @dev: the device structure
@@ -131,6 +145,7 @@ static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
hdr->me_addr = 0;
hdr->length = length;
hdr->msg_complete = 1;
+ hdr->dma_ring = 0;
hdr->reserved = 0;
hdr->internal = 0;
}
@@ -174,7 +189,7 @@ static inline int mei_hbm_cl_write(struct mei_device *dev, struct mei_cl *cl,
mei_hbm_hdr(&mei_hdr, len);
mei_hbm_cl_hdr(cl, hbm_cmd, buf, len);
- return mei_write_message(dev, &mei_hdr, buf);
+ return mei_hbm_write_message(dev, &mei_hdr, buf);
}
/**
@@ -267,7 +282,7 @@ int mei_hbm_start_req(struct mei_device *dev)
start_req.host_version.minor_version = HBM_MINOR_VERSION;
dev->hbm_state = MEI_HBM_IDLE;
- ret = mei_write_message(dev, &mei_hdr, &start_req);
+ ret = mei_hbm_write_message(dev, &mei_hdr, &start_req);
if (ret) {
dev_err(dev->dev, "version message write failed: ret = %d\n",
ret);
@@ -304,7 +319,7 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev)
enum_req.flags |= dev->hbm_f_ie_supported ?
MEI_HBM_ENUM_F_IMMEDIATE_ENUM : 0;
- ret = mei_write_message(dev, &mei_hdr, &enum_req);
+ ret = mei_hbm_write_message(dev, &mei_hdr, &enum_req);
if (ret) {
dev_err(dev->dev, "enumeration request write failed: ret = %d.\n",
ret);
@@ -373,7 +388,7 @@ static int mei_hbm_add_cl_resp(struct mei_device *dev, u8 addr, u8 status)
resp.me_addr = addr;
resp.status = status;
- ret = mei_write_message(dev, &mei_hdr, &resp);
+ ret = mei_hbm_write_message(dev, &mei_hdr, &resp);
if (ret)
dev_err(dev->dev, "add client response write failed: ret = %d\n",
ret);
@@ -430,7 +445,7 @@ int mei_hbm_cl_notify_req(struct mei_device *dev,
req.start = start;
- ret = mei_write_message(dev, &mei_hdr, &req);
+ ret = mei_hbm_write_message(dev, &mei_hdr, &req);
if (ret)
dev_err(dev->dev, "notify request failed: ret = %d\n", ret);
@@ -555,7 +570,7 @@ static int mei_hbm_prop_req(struct mei_device *dev, unsigned long start_idx)
prop_req.hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
prop_req.me_addr = addr;
- ret = mei_write_message(dev, &mei_hdr, &prop_req);
+ ret = mei_hbm_write_message(dev, &mei_hdr, &prop_req);
if (ret) {
dev_err(dev->dev, "properties request write failed: ret = %d\n",
ret);
@@ -592,7 +607,7 @@ int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd)
memset(&req, 0, len);
req.hbm_cmd = pg_cmd;
- ret = mei_write_message(dev, &mei_hdr, &req);
+ ret = mei_hbm_write_message(dev, &mei_hdr, &req);
if (ret)
dev_err(dev->dev, "power gate command write failed.\n");
return ret;
@@ -618,7 +633,7 @@ static int mei_hbm_stop_req(struct mei_device *dev)
req.hbm_cmd = HOST_STOP_REQ_CMD;
req.reason = DRIVER_STOP_REQUEST;
- return mei_write_message(dev, &mei_hdr, &req);
+ return mei_hbm_write_message(dev, &mei_hdr, &req);
}
/**
@@ -992,6 +1007,12 @@ static void mei_hbm_config_features(struct mei_device *dev)
/* OS ver message Support */
if (dev->version.major_version >= HBM_MAJOR_VERSION_OS)
dev->hbm_f_os_supported = 1;
+
+ /* DMA Ring Support */
+ if (dev->version.major_version > HBM_MAJOR_VERSION_DR ||
+ (dev->version.major_version == HBM_MAJOR_VERSION_DR &&
+ dev->version.minor_version >= HBM_MINOR_VERSION_DR))
+ dev->hbm_f_dr_supported = 1;
}
/**
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 334ab02e1de2..0759c3a668de 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -19,6 +19,7 @@
#include <linux/kthread.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
+#include <linux/sizes.h>
#include "mei_dev.h"
#include "hbm.h"
@@ -228,7 +229,7 @@ static void mei_me_hw_config(struct mei_device *dev)
/* Doesn't change in runtime */
hcsr = mei_hcsr_read(dev);
- dev->hbuf_depth = (hcsr & H_CBD) >> 24;
+ hw->hbuf_depth = (hcsr & H_CBD) >> 24;
reg = 0;
pci_read_config_dword(pdev, PCI_CFG_HFS_1, &reg);
@@ -490,70 +491,82 @@ static bool mei_me_hbuf_is_empty(struct mei_device *dev)
*/
static int mei_me_hbuf_empty_slots(struct mei_device *dev)
{
+ struct mei_me_hw *hw = to_me_hw(dev);
unsigned char filled_slots, empty_slots;
filled_slots = mei_hbuf_filled_slots(dev);
- empty_slots = dev->hbuf_depth - filled_slots;
+ empty_slots = hw->hbuf_depth - filled_slots;
/* check for overflow */
- if (filled_slots > dev->hbuf_depth)
+ if (filled_slots > hw->hbuf_depth)
return -EOVERFLOW;
return empty_slots;
}
/**
- * mei_me_hbuf_max_len - returns size of hw buffer.
+ * mei_me_hbuf_depth - returns depth of the hw buffer.
*
* @dev: the device structure
*
- * Return: size of hw buffer in bytes
+ * Return: size of hw buffer in slots
*/
-static size_t mei_me_hbuf_max_len(const struct mei_device *dev)
+static u32 mei_me_hbuf_depth(const struct mei_device *dev)
{
- return dev->hbuf_depth * sizeof(u32) - sizeof(struct mei_msg_hdr);
-}
+ struct mei_me_hw *hw = to_me_hw(dev);
+ return hw->hbuf_depth;
+}
/**
* mei_me_hbuf_write - writes a message to host hw buffer.
*
* @dev: the device structure
- * @header: mei HECI header of message
- * @buf: message payload will be written
+ * @hdr: header of message
+ * @hdr_len: header length in bytes: must be multiplication of a slot (4bytes)
+ * @data: payload
+ * @data_len: payload length in bytes
*
- * Return: -EIO if write has failed
+ * Return: 0 if success, < 0 - otherwise.
*/
static int mei_me_hbuf_write(struct mei_device *dev,
- struct mei_msg_hdr *header,
- const unsigned char *buf)
+ const void *hdr, size_t hdr_len,
+ const void *data, size_t data_len)
{
unsigned long rem;
- unsigned long length = header->length;
- u32 *reg_buf = (u32 *)buf;
+ unsigned long i;
+ const u32 *reg_buf;
u32 dw_cnt;
- int i;
int empty_slots;
- dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header));
+ if (WARN_ON(!hdr || !data || hdr_len & 0x3))
+ return -EINVAL;
+
+ dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM((struct mei_msg_hdr *)hdr));
empty_slots = mei_hbuf_empty_slots(dev);
dev_dbg(dev->dev, "empty slots = %hu.\n", empty_slots);
- dw_cnt = mei_data2slots(length);
- if (empty_slots < 0 || dw_cnt > empty_slots)
+ if (empty_slots < 0)
+ return -EOVERFLOW;
+
+ dw_cnt = mei_data2slots(hdr_len + data_len);
+ if (dw_cnt > (u32)empty_slots)
return -EMSGSIZE;
- mei_me_hcbww_write(dev, *((u32 *) header));
+ reg_buf = hdr;
+ for (i = 0; i < hdr_len / MEI_SLOT_SIZE; i++)
+ mei_me_hcbww_write(dev, reg_buf[i]);
- for (i = 0; i < length / 4; i++)
+ reg_buf = data;
+ for (i = 0; i < data_len / MEI_SLOT_SIZE; i++)
mei_me_hcbww_write(dev, reg_buf[i]);
- rem = length & 0x3;
+ rem = data_len & 0x3;
if (rem > 0) {
u32 reg = 0;
- memcpy(&reg, &buf[length - rem], rem);
+ memcpy(&reg, (const u8 *)data + data_len - rem, rem);
mei_me_hcbww_write(dev, reg);
}
@@ -601,11 +614,11 @@ static int mei_me_count_full_read_slots(struct mei_device *dev)
* Return: always 0
*/
static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer,
- unsigned long buffer_length)
+ unsigned long buffer_length)
{
u32 *reg_buf = (u32 *)buffer;
- for (; buffer_length >= sizeof(u32); buffer_length -= sizeof(u32))
+ for (; buffer_length >= MEI_SLOT_SIZE; buffer_length -= MEI_SLOT_SIZE)
*reg_buf++ = mei_me_mecbrw_read(dev);
if (buffer_length > 0) {
@@ -1314,7 +1327,7 @@ static const struct mei_hw_ops mei_me_hw_ops = {
.hbuf_free_slots = mei_me_hbuf_empty_slots,
.hbuf_is_ready = mei_me_hbuf_is_empty,
- .hbuf_max_len = mei_me_hbuf_max_len,
+ .hbuf_depth = mei_me_hbuf_depth,
.write = mei_me_hbuf_write,
@@ -1377,6 +1390,11 @@ static bool mei_me_fw_type_sps(struct pci_dev *pdev)
.fw_status.status[4] = PCI_CFG_HFS_5, \
.fw_status.status[5] = PCI_CFG_HFS_6
+#define MEI_CFG_DMA_128 \
+ .dma_size[DMA_DSCR_HOST] = SZ_128K, \
+ .dma_size[DMA_DSCR_DEVICE] = SZ_128K, \
+ .dma_size[DMA_DSCR_CTRL] = PAGE_SIZE
+
/* ICH Legacy devices */
static const struct mei_cfg mei_me_ich_cfg = {
MEI_CFG_ICH_HFS,
@@ -1409,6 +1427,12 @@ static const struct mei_cfg mei_me_pch8_sps_cfg = {
MEI_CFG_FW_SPS,
};
+/* Cannon Lake and newer devices */
+static const struct mei_cfg mei_me_pch12_cfg = {
+ MEI_CFG_PCH8_HFS,
+ MEI_CFG_DMA_128,
+};
+
/*
* mei_cfg_list - A list of platform platform specific configurations.
* Note: has to be synchronized with enum mei_cfg_idx.
@@ -1421,6 +1445,7 @@ static const struct mei_cfg *const mei_cfg_list[] = {
[MEI_ME_PCH_CPT_PBG_CFG] = &mei_me_pch_cpt_pbg_cfg,
[MEI_ME_PCH8_CFG] = &mei_me_pch8_cfg,
[MEI_ME_PCH8_SPS_CFG] = &mei_me_pch8_sps_cfg,
+ [MEI_ME_PCH12_CFG] = &mei_me_pch12_cfg,
};
const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx)
diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h
index 67892533576e..bbcc5fc106cd 100644
--- a/drivers/misc/mei/hw-me.h
+++ b/drivers/misc/mei/hw-me.h
@@ -31,10 +31,12 @@
*
* @fw_status: FW status
* @quirk_probe: device exclusion quirk
+ * @dma_size: device DMA buffers size
*/
struct mei_cfg {
const struct mei_fw_status fw_status;
bool (*quirk_probe)(struct pci_dev *pdev);
+ size_t dma_size[DMA_DSCR_NUM];
};
@@ -52,12 +54,14 @@ struct mei_cfg {
* @mem_addr: io memory address
* @pg_state: power gating state
* @d0i3_supported: di03 support
+ * @hbuf_depth: depth of hardware host/write buffer in slots
*/
struct mei_me_hw {
const struct mei_cfg *cfg;
void __iomem *mem_addr;
enum mei_pg_state pg_state;
bool d0i3_supported;
+ u8 hbuf_depth;
};
#define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw)
@@ -78,6 +82,7 @@ struct mei_me_hw {
* @MEI_ME_PCH8_SPS_CFG: Platform Controller Hub Gen8 and newer
* servers platforms with quirk for
* SPS firmware exclusion.
+ * @MEI_ME_PCH12_CFG: Platform Controller Hub Gen12 and newer
* @MEI_ME_NUM_CFG: Upper Sentinel.
*/
enum mei_cfg_idx {
@@ -88,6 +93,7 @@ enum mei_cfg_idx {
MEI_ME_PCH_CPT_PBG_CFG,
MEI_ME_PCH8_CFG,
MEI_ME_PCH8_SPS_CFG,
+ MEI_ME_PCH12_CFG,
MEI_ME_NUM_CFG,
};
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c
index c2c8993e2a51..8449fe0367ff 100644
--- a/drivers/misc/mei/hw-txe.c
+++ b/drivers/misc/mei/hw-txe.c
@@ -31,6 +31,7 @@
#include "mei-trace.h"
+#define TXE_HBUF_DEPTH (PAYLOAD_SIZE / MEI_SLOT_SIZE)
/**
* mei_txe_reg_read - Reads 32bit data from the txe device
@@ -681,9 +682,6 @@ static void mei_txe_hw_config(struct mei_device *dev)
struct mei_txe_hw *hw = to_txe_hw(dev);
- /* Doesn't change in runtime */
- dev->hbuf_depth = PAYLOAD_SIZE / 4;
-
hw->aliveness = mei_txe_aliveness_get(dev);
hw->readiness = mei_txe_readiness_get(dev);
@@ -691,37 +689,34 @@ static void mei_txe_hw_config(struct mei_device *dev)
hw->aliveness, hw->readiness);
}
-
/**
* mei_txe_write - writes a message to device.
*
* @dev: the device structure
- * @header: header of message
- * @buf: message buffer will be written
+ * @hdr: header of message
+ * @hdr_len: header length in bytes - must multiplication of a slot (4bytes)
+ * @data: payload
+ * @data_len: paylead length in bytes
*
- * Return: 0 if success, <0 - otherwise.
+ * Return: 0 if success, < 0 - otherwise.
*/
-
static int mei_txe_write(struct mei_device *dev,
- struct mei_msg_hdr *header,
- const unsigned char *buf)
+ const void *hdr, size_t hdr_len,
+ const void *data, size_t data_len)
{
struct mei_txe_hw *hw = to_txe_hw(dev);
unsigned long rem;
- unsigned long length;
- int slots = dev->hbuf_depth;
- u32 *reg_buf = (u32 *)buf;
+ const u32 *reg_buf;
+ u32 slots = TXE_HBUF_DEPTH;
u32 dw_cnt;
- int i;
+ unsigned long i, j;
- if (WARN_ON(!header || !buf))
+ if (WARN_ON(!hdr || !data || hdr_len & 0x3))
return -EINVAL;
- length = header->length;
-
- dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header));
+ dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM((struct mei_msg_hdr *)hdr));
- dw_cnt = mei_data2slots(length);
+ dw_cnt = mei_data2slots(hdr_len + data_len);
if (dw_cnt > slots)
return -EMSGSIZE;
@@ -739,17 +734,20 @@ static int mei_txe_write(struct mei_device *dev,
return -EAGAIN;
}
- mei_txe_input_payload_write(dev, 0, *((u32 *)header));
+ reg_buf = hdr;
+ for (i = 0; i < hdr_len / MEI_SLOT_SIZE; i++)
+ mei_txe_input_payload_write(dev, i, reg_buf[i]);
- for (i = 0; i < length / 4; i++)
- mei_txe_input_payload_write(dev, i + 1, reg_buf[i]);
+ reg_buf = data;
+ for (j = 0; j < data_len / MEI_SLOT_SIZE; j++)
+ mei_txe_input_payload_write(dev, i + j, reg_buf[j]);
- rem = length & 0x3;
+ rem = data_len & 0x3;
if (rem > 0) {
u32 reg = 0;
- memcpy(&reg, &buf[length - rem], rem);
- mei_txe_input_payload_write(dev, i + 1, reg);
+ memcpy(&reg, (const u8 *)data + data_len - rem, rem);
+ mei_txe_input_payload_write(dev, i + j, reg);
}
/* after each write the whole buffer is consumed */
@@ -762,15 +760,15 @@ static int mei_txe_write(struct mei_device *dev,
}
/**
- * mei_txe_hbuf_max_len - mimics the me hbuf circular buffer
+ * mei_txe_hbuf_depth - mimics the me hbuf circular buffer
*
* @dev: the device structure
*
- * Return: the PAYLOAD_SIZE - 4
+ * Return: the TXE_HBUF_DEPTH
*/
-static size_t mei_txe_hbuf_max_len(const struct mei_device *dev)
+static u32 mei_txe_hbuf_depth(const struct mei_device *dev)
{
- return PAYLOAD_SIZE - sizeof(struct mei_msg_hdr);
+ return TXE_HBUF_DEPTH;
}
/**
@@ -778,7 +776,7 @@ static size_t mei_txe_hbuf_max_len(const struct mei_device *dev)
*
* @dev: the device structure
*
- * Return: always hbuf_depth
+ * Return: always TXE_HBUF_DEPTH
*/
static int mei_txe_hbuf_empty_slots(struct mei_device *dev)
{
@@ -797,7 +795,7 @@ static int mei_txe_hbuf_empty_slots(struct mei_device *dev)
static int mei_txe_count_full_read_slots(struct mei_device *dev)
{
/* read buffers has static size */
- return PAYLOAD_SIZE / 4;
+ return TXE_HBUF_DEPTH;
}
/**
@@ -839,7 +837,7 @@ static int mei_txe_read(struct mei_device *dev,
dev_dbg(dev->dev, "buffer-length = %lu buf[0]0x%08X\n",
len, mei_txe_out_data_read(dev, 0));
- for (i = 0; i < len / 4; i++) {
+ for (i = 0; i < len / MEI_SLOT_SIZE; i++) {
/* skip header: index starts from 1 */
reg = mei_txe_out_data_read(dev, i + 1);
dev_dbg(dev->dev, "buf[%d] = 0x%08X\n", i, reg);
@@ -1140,7 +1138,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
/* Input Ready: Detection if host can write to SeC */
if (test_and_clear_bit(TXE_INTR_IN_READY_BIT, &hw->intr_cause)) {
dev->hbuf_is_ready = true;
- hw->slots = dev->hbuf_depth;
+ hw->slots = TXE_HBUF_DEPTH;
}
if (hw->aliveness && dev->hbuf_is_ready) {
@@ -1186,7 +1184,7 @@ static const struct mei_hw_ops mei_txe_hw_ops = {
.hbuf_free_slots = mei_txe_hbuf_empty_slots,
.hbuf_is_ready = mei_txe_is_input_ready,
- .hbuf_max_len = mei_txe_hbuf_max_len,
+ .hbuf_depth = mei_txe_hbuf_depth,
.write = mei_txe_write,
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index 5c8286b40b62..65655925791a 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -28,8 +28,6 @@
#define MEI_CL_CONNECT_TIMEOUT 15 /* HPS: Client Connect Timeout */
#define MEI_CLIENTS_INIT_TIMEOUT 15 /* HPS: Clients Enumeration Timeout */
-#define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */
-
#define MEI_PGI_TIMEOUT 1 /* PG Isolation time response 1 sec */
#define MEI_D0I3_TIMEOUT 5 /* D0i3 set/unset max response time */
#define MEI_HBM_TIMEOUT 1 /* 1 second */
@@ -82,6 +80,12 @@
#define HBM_MINOR_VERSION_OS 0
#define HBM_MAJOR_VERSION_OS 2
+/*
+ * MEI version with dma ring support
+ */
+#define HBM_MINOR_VERSION_DR 1
+#define HBM_MAJOR_VERSION_DR 2
+
/* Host bus message command opcode */
#define MEI_HBM_CMD_OP_MSK 0x7f
/* Host bus message command RESPONSE */
@@ -124,6 +128,9 @@
#define MEI_HBM_NOTIFY_RES_CMD 0x90
#define MEI_HBM_NOTIFICATION_CMD 0x11
+#define MEI_HBM_DMA_SETUP_REQ_CMD 0x12
+#define MEI_HBM_DMA_SETUP_RES_CMD 0x92
+
/*
* MEI Stop Reason
* used by hbm_host_stop_request.reason
@@ -189,19 +196,27 @@ enum mei_cl_disconnect_status {
MEI_CL_DISCONN_SUCCESS = MEI_HBMS_SUCCESS
};
-/*
- * MEI BUS Interface Section
+/**
+ * struct mei_msg_hdr - MEI BUS Interface Section
+ *
+ * @me_addr: device address
+ * @host_addr: host address
+ * @length: message length
+ * @reserved: reserved
+ * @dma_ring: message is on dma ring
+ * @internal: message is internal
+ * @msg_complete: last packet of the message
*/
struct mei_msg_hdr {
u32 me_addr:8;
u32 host_addr:8;
u32 length:9;
- u32 reserved:5;
+ u32 reserved:4;
+ u32 dma_ring:1;
u32 internal:1;
u32 msg_complete:1;
} __packed;
-
struct mei_bus_message {
u8 hbm_cmd;
u8 data[0];
@@ -451,4 +466,50 @@ struct hbm_notification {
u8 reserved[1];
} __packed;
+/**
+ * struct hbm_dma_mem_dscr - dma ring
+ *
+ * @addr_hi: the high 32bits of 64 bit address
+ * @addr_lo: the low 32bits of 64 bit address
+ * @size : size in bytes (must be power of 2)
+ */
+struct hbm_dma_mem_dscr {
+ u32 addr_hi;
+ u32 addr_lo;
+ u32 size;
+} __packed;
+
+enum {
+ DMA_DSCR_HOST = 0,
+ DMA_DSCR_DEVICE = 1,
+ DMA_DSCR_CTRL = 2,
+ DMA_DSCR_NUM,
+};
+
+/**
+ * struct hbm_dma_setup_request - dma setup request
+ *
+ * @hbm_cmd: bus message command header
+ * @reserved: reserved for alignment
+ * @dma_dscr: dma descriptor for HOST, DEVICE, and CTRL
+ */
+struct hbm_dma_setup_request {
+ u8 hbm_cmd;
+ u8 reserved[3];
+ struct hbm_dma_mem_dscr dma_dscr[DMA_DSCR_NUM];
+} __packed;
+
+/**
+ * struct hbm_dma_setup_response - dma setup response
+ *
+ * @hbm_cmd: bus message command header
+ * @status: 0 on success; otherwise DMA setup failed.
+ * @reserved: reserved for alignment
+ */
+struct hbm_dma_setup_response {
+ u8 hbm_cmd;
+ u8 status;
+ u8 reserved[2];
+} __packed;
+
#endif
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index 6649f0d56d2f..5a661cbdf2ae 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -173,10 +173,12 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
int slots;
int ret;
+ msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_response));
slots = mei_hbuf_empty_slots(dev);
- msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_response));
+ if (slots < 0)
+ return -EOVERFLOW;
- if (slots < msg_slots)
+ if ((u32)slots < msg_slots)
return -EMSGSIZE;
ret = mei_hbm_cl_disconnect_rsp(dev, cl);
@@ -206,10 +208,12 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
if (!list_empty(&cl->rd_pending))
return 0;
- msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
+ msg_slots = mei_hbm2slots(sizeof(struct hbm_flow_control));
slots = mei_hbuf_empty_slots(dev);
+ if (slots < 0)
+ return -EOVERFLOW;
- if (slots < msg_slots)
+ if ((u32)slots < msg_slots)
return -EMSGSIZE;
ret = mei_hbm_cl_flow_control_req(dev, cl);
@@ -368,7 +372,10 @@ int mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list)
return 0;
slots = mei_hbuf_empty_slots(dev);
- if (slots <= 0)
+ if (slots < 0)
+ return -EOVERFLOW;
+
+ if (slots == 0)
return -EMSGSIZE;
/* complete all waiting for write CB */
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 7465f17e1559..4d77a6ae183a 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -1,7 +1,7 @@
/*
*
* Intel Management Engine Interface (Intel MEI) Linux driver
- * Copyright (c) 2003-2012, Intel Corporation.
+ * Copyright (c) 2003-2018, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -137,7 +137,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
struct mei_device *dev;
struct mei_cl_cb *cb = NULL;
bool nonblock = !!(file->f_flags & O_NONBLOCK);
- int rets;
+ ssize_t rets;
if (WARN_ON(!cl || !cl->dev))
return -ENODEV;
@@ -170,7 +170,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
rets = mei_cl_read_start(cl, length, file);
if (rets && rets != -EBUSY) {
- cl_dbg(dev, cl, "mei start read failure status = %d\n", rets);
+ cl_dbg(dev, cl, "mei start read failure status = %zd\n", rets);
goto out;
}
@@ -204,7 +204,7 @@ copy_buffer:
/* now copy the data to user space */
if (cb->status) {
rets = cb->status;
- cl_dbg(dev, cl, "read operation failed %d\n", rets);
+ cl_dbg(dev, cl, "read operation failed %zd\n", rets);
goto free;
}
@@ -236,7 +236,7 @@ free:
*offset = 0;
out:
- cl_dbg(dev, cl, "end mei read rets = %d\n", rets);
+ cl_dbg(dev, cl, "end mei read rets = %zd\n", rets);
mutex_unlock(&dev->device_lock);
return rets;
}
@@ -256,7 +256,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
struct mei_cl *cl = file->private_data;
struct mei_cl_cb *cb;
struct mei_device *dev;
- int rets;
+ ssize_t rets;
if (WARN_ON(!cl || !cl->dev))
return -ENODEV;
@@ -312,7 +312,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
}
}
- *offset = 0;
cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file);
if (!cb) {
rets = -ENOMEM;
@@ -812,11 +811,39 @@ static ssize_t tx_queue_limit_store(struct device *device,
}
static DEVICE_ATTR_RW(tx_queue_limit);
+/**
+ * fw_ver_show - display ME FW version
+ *
+ * @device: device pointer
+ * @attr: attribute pointer
+ * @buf: char out buffer
+ *
+ * Return: number of the bytes printed into buf or error
+ */
+static ssize_t fw_ver_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct mei_device *dev = dev_get_drvdata(device);
+ struct mei_fw_version *ver;
+ ssize_t cnt = 0;
+ int i;
+
+ ver = dev->fw_ver;
+
+ for (i = 0; i < MEI_MAX_FW_VER_BLOCKS; i++)
+ cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, "%u:%u.%u.%u.%u\n",
+ ver[i].platform, ver[i].major, ver[i].minor,
+ ver[i].hotfix, ver[i].buildno);
+ return cnt;
+}
+static DEVICE_ATTR_RO(fw_ver);
+
static struct attribute *mei_attrs[] = {
&dev_attr_fw_status.attr,
&dev_attr_hbm_ver.attr,
&dev_attr_hbm_ver_drv.attr,
&dev_attr_tx_queue_limit.attr,
+ &dev_attr_fw_ver.attr,
NULL
};
ATTRIBUTE_GROUPS(mei);
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index be9c48415da9..377397e1b5a5 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -1,7 +1,7 @@
/*
*
* Intel Management Engine Interface (Intel MEI) Linux driver
- * Copyright (c) 2003-2012, Intel Corporation.
+ * Copyright (c) 2003-2018, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -26,7 +26,8 @@
#include "hw.h"
#include "hbm.h"
-#define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32))
+#define MEI_SLOT_SIZE sizeof(u32)
+#define MEI_RD_MSG_BUF_SIZE (128 * MEI_SLOT_SIZE)
/*
* Number of Maximum MEI Clients
@@ -174,7 +175,6 @@ struct mei_cl;
* @status: io status of the cb
* @internal: communication between driver and FW flag
* @blocking: transmission blocking mode
- * @completed: the transfer or reception has completed
*/
struct mei_cl_cb {
struct list_head list;
@@ -186,7 +186,6 @@ struct mei_cl_cb {
int status;
u32 internal:1;
u32 blocking:1;
- u32 completed:1;
};
/**
@@ -269,7 +268,7 @@ struct mei_cl {
*
* @hbuf_free_slots : query for write buffer empty slots
* @hbuf_is_ready : query if write buffer is empty
- * @hbuf_max_len : query for write buffer max len
+ * @hbuf_depth : query for write buffer depth
*
* @write : write a message to FW
*
@@ -299,10 +298,10 @@ struct mei_hw_ops {
int (*hbuf_free_slots)(struct mei_device *dev);
bool (*hbuf_is_ready)(struct mei_device *dev);
- size_t (*hbuf_max_len)(const struct mei_device *dev);
+ u32 (*hbuf_depth)(const struct mei_device *dev);
int (*write)(struct mei_device *dev,
- struct mei_msg_hdr *hdr,
- const unsigned char *buf);
+ const void *hdr, size_t hdr_len,
+ const void *data, size_t data_len);
int (*rdbuf_full_slots)(struct mei_device *dev);
@@ -317,7 +316,7 @@ void mei_cl_bus_dev_fixup(struct mei_cl_device *dev);
ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
unsigned int mode);
ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length,
- unsigned int mode);
+ unsigned int mode, unsigned long timeout);
bool mei_cl_bus_rx_event(struct mei_cl *cl);
bool mei_cl_bus_notify_event(struct mei_cl *cl);
void mei_cl_bus_remove_devices(struct mei_device *bus);
@@ -355,6 +354,25 @@ enum mei_pg_state {
const char *mei_pg_state_str(enum mei_pg_state state);
/**
+ * struct mei_fw_version - MEI FW version struct
+ *
+ * @platform: platform identifier
+ * @major: major version field
+ * @minor: minor version field
+ * @buildno: build number version field
+ * @hotfix: hotfix number version field
+ */
+struct mei_fw_version {
+ u8 platform;
+ u8 major;
+ u16 minor;
+ u16 buildno;
+ u16 hotfix;
+};
+
+#define MEI_MAX_FW_VER_BLOCKS 3
+
+/**
* struct mei_device - MEI private device struct
*
* @dev : device on a bus
@@ -390,7 +408,6 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @rd_msg_buf : control messages buffer
* @rd_msg_hdr : read message header storage
*
- * @hbuf_depth : depth of hardware host/write buffer is slots
* @hbuf_is_ready : query if the host host/write buffer is ready
*
* @version : HBM protocol version in use
@@ -401,6 +418,9 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @hbm_f_fa_supported : hbm feature fixed address client
* @hbm_f_ie_supported : hbm feature immediate reply to enum request
* @hbm_f_os_supported : hbm feature support OS ver message
+ * @hbm_f_dr_supported : hbm feature dma ring supported
+ *
+ * @fw_ver : FW versions
*
* @me_clients_rwsem: rw lock over me_clients list
* @me_clients : list of FW clients
@@ -466,7 +486,6 @@ struct mei_device {
u32 rd_msg_hdr;
/* write buffer */
- u8 hbuf_depth;
bool hbuf_is_ready;
struct hbm_version version;
@@ -477,6 +496,9 @@ struct mei_device {
unsigned int hbm_f_fa_supported:1;
unsigned int hbm_f_ie_supported:1;
unsigned int hbm_f_os_supported:1;
+ unsigned int hbm_f_dr_supported:1;
+
+ struct mei_fw_version fw_ver[MEI_MAX_FW_VER_BLOCKS];
struct rw_semaphore me_clients_rwsem;
struct list_head me_clients;
@@ -508,8 +530,7 @@ static inline unsigned long mei_secs_to_jiffies(unsigned long sec)
}
/**
- * mei_data2slots - get slots - number of (dwords) from a message length
- * + size of the mei header
+ * mei_data2slots - get slots number from a message length
*
* @length: size of the messages in bytes
*
@@ -517,7 +538,20 @@ static inline unsigned long mei_secs_to_jiffies(unsigned long sec)
*/
static inline u32 mei_data2slots(size_t length)
{
- return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, 4);
+ return DIV_ROUND_UP(length, MEI_SLOT_SIZE);
+}
+
+/**
+ * mei_hbm2slots - get slots number from a hbm message length
+ * length + size of the mei message header
+ *
+ * @length: size of the messages in bytes
+ *
+ * Return: number of slots
+ */
+static inline u32 mei_hbm2slots(size_t length)
+{
+ return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, MEI_SLOT_SIZE);
}
/**
@@ -529,7 +563,7 @@ static inline u32 mei_data2slots(size_t length)
*/
static inline u32 mei_slots2data(int slots)
{
- return slots * 4;
+ return slots * MEI_SLOT_SIZE;
}
/*
@@ -630,15 +664,16 @@ static inline int mei_hbuf_empty_slots(struct mei_device *dev)
return dev->ops->hbuf_free_slots(dev);
}
-static inline size_t mei_hbuf_max_len(const struct mei_device *dev)
+static inline u32 mei_hbuf_depth(const struct mei_device *dev)
{
- return dev->ops->hbuf_max_len(dev);
+ return dev->ops->hbuf_depth(dev);
}
static inline int mei_write_message(struct mei_device *dev,
- struct mei_msg_hdr *hdr, const void *buf)
+ const void *hdr, size_t hdr_len,
+ const void *data, size_t data_len)
{
- return dev->ops->write(dev, hdr, buf);
+ return dev->ops->write(dev, hdr, hdr_len, data, data_len);
}
static inline u32 mei_read_hdr(const struct mei_device *dev)
@@ -681,10 +716,10 @@ static inline void mei_dbgfs_deregister(struct mei_device *dev) {}
int mei_register(struct mei_device *dev, struct device *parent);
void mei_deregister(struct mei_device *dev);
-#define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d internal=%1d comp=%1d"
+#define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d dma=%1d internal=%1d comp=%1d"
#define MEI_HDR_PRM(hdr) \
(hdr)->host_addr, (hdr)->me_addr, \
- (hdr)->length, (hdr)->internal, (hdr)->msg_complete
+ (hdr)->length, (hdr)->dma_ring, (hdr)->internal, (hdr)->msg_complete
ssize_t mei_fw_status2str(struct mei_fw_status *fw_sts, char *buf, size_t len);
/**