diff options
author | Alexander Usyskin <alexander.usyskin@intel.com> | 2018-06-24 23:11:41 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-07-03 13:11:20 +0200 |
commit | 3cfaeb33530592b02b2ceb76b379364c55ca612e (patch) | |
tree | 16b29db30b99b2a233b0817a6f694d6c15bf02b0 /drivers/misc | |
parent | mei: add optional timeout to internal bus recv (diff) | |
download | linux-3cfaeb33530592b02b2ceb76b379364c55ca612e.tar.xz linux-3cfaeb33530592b02b2ceb76b379364c55ca612e.zip |
mei: expose fw version to sysfs
The ME FW version is constantly used by detection and update tools.
To improve the reliability and simplify these tools provide
a sysfs interface to access version of the platform ME firmware
in the following format:
<platform>:<major>.<minor>.<milestone>.<build>.
There can be up to three such blocks for different FW components.
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/mei/bus-fixup.c | 86 | ||||
-rw-r--r-- | drivers/misc/mei/main.c | 30 | ||||
-rw-r--r-- | drivers/misc/mei/mei_dev.h | 25 |
3 files changed, 133 insertions, 8 deletions
diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index b17c46a43812..e45fe826d87d 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 < 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 (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); } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 7465f17e1559..f690918f7817 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, @@ -812,11 +812,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 4058ab5ad2d8..d522585b71b7 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, @@ -355,6 +355,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 @@ -402,6 +421,8 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @hbm_f_ie_supported : hbm feature immediate reply to enum request * @hbm_f_os_supported : hbm feature support OS ver message * + * @fw_ver : FW versions + * * @me_clients_rwsem: rw lock over me_clients list * @me_clients : list of FW clients * @me_clients_map : FW clients bit map @@ -478,6 +499,8 @@ struct mei_device { unsigned int hbm_f_ie_supported:1; unsigned int hbm_f_os_supported:1; + struct mei_fw_version fw_ver[MEI_MAX_FW_VER_BLOCKS]; + struct rw_semaphore me_clients_rwsem; struct list_head me_clients; DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX); |