summaryrefslogtreecommitdiffstats
path: root/drivers/misc/mei
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 17:35:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 17:39:31 +0000
commit85c675d0d09a45a135bddd15d7b385f8758c32fb (patch)
tree76267dbc9b9a130337be3640948fe397b04ac629 /drivers/misc/mei
parentAdding upstream version 6.6.15. (diff)
downloadlinux-85c675d0d09a45a135bddd15d7b385f8758c32fb.tar.xz
linux-85c675d0d09a45a135bddd15d7b385f8758c32fb.zip
Adding upstream version 6.7.7.upstream/6.7.7
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/misc/mei')
-rw-r--r--drivers/misc/mei/bus.c92
-rw-r--r--drivers/misc/mei/client.c20
-rw-r--r--drivers/misc/mei/dma-ring.c6
-rw-r--r--drivers/misc/mei/hbm.c6
-rw-r--r--drivers/misc/mei/hw-me-regs.h3
-rw-r--r--drivers/misc/mei/hw-me.c77
-rw-r--r--drivers/misc/mei/hw-me.h4
-rw-r--r--drivers/misc/mei/hw.h12
-rw-r--r--drivers/misc/mei/init.c27
-rw-r--r--drivers/misc/mei/interrupt.c6
-rw-r--r--drivers/misc/mei/main.c8
-rw-r--r--drivers/misc/mei/mei_dev.h51
-rw-r--r--drivers/misc/mei/pxp/mei_pxp.c85
13 files changed, 345 insertions, 52 deletions
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index 2e65ce6bde..f9bcff1976 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -257,7 +257,7 @@ out:
}
/**
- * mei_cldev_send_vtag - me device send with vtag (write)
+ * mei_cldev_send_vtag - me device send with vtag (write)
*
* @cldev: me client device
* @buf: buffer to send
@@ -279,6 +279,29 @@ ssize_t mei_cldev_send_vtag(struct mei_cl_device *cldev, const u8 *buf,
EXPORT_SYMBOL_GPL(mei_cldev_send_vtag);
/**
+ * mei_cldev_send_vtag_timeout - me device send with vtag and timeout (write)
+ *
+ * @cldev: me client device
+ * @buf: buffer to send
+ * @length: buffer length
+ * @vtag: virtual tag
+ * @timeout: send timeout in milliseconds, 0 for infinite timeout
+ *
+ * Return:
+ * * written size in bytes
+ * * < 0 on error
+ */
+
+ssize_t mei_cldev_send_vtag_timeout(struct mei_cl_device *cldev, const u8 *buf,
+ size_t length, u8 vtag, unsigned long timeout)
+{
+ struct mei_cl *cl = cldev->cl;
+
+ return __mei_cl_send_timeout(cl, buf, length, vtag, MEI_CL_IO_TX_BLOCKING, timeout);
+}
+EXPORT_SYMBOL_GPL(mei_cldev_send_vtag_timeout);
+
+/**
* mei_cldev_recv_vtag - client receive with vtag (read)
*
* @cldev: me client device
@@ -323,7 +346,49 @@ ssize_t mei_cldev_recv_nonblock_vtag(struct mei_cl_device *cldev, u8 *buf,
EXPORT_SYMBOL_GPL(mei_cldev_recv_nonblock_vtag);
/**
- * mei_cldev_send - me device send (write)
+ * mei_cldev_recv_timeout - client receive with timeout (read)
+ *
+ * @cldev: me client device
+ * @buf: buffer to receive
+ * @length: buffer length
+ * @timeout: send timeout in milliseconds, 0 for infinite timeout
+ *
+ * Return:
+ * * read size in bytes
+ * * < 0 on error
+ */
+ssize_t mei_cldev_recv_timeout(struct mei_cl_device *cldev, u8 *buf, size_t length,
+ unsigned long timeout)
+{
+ return mei_cldev_recv_vtag_timeout(cldev, buf, length, NULL, timeout);
+}
+EXPORT_SYMBOL_GPL(mei_cldev_recv_timeout);
+
+/**
+ * mei_cldev_recv_vtag_timeout - client receive with vtag (read)
+ *
+ * @cldev: me client device
+ * @buf: buffer to receive
+ * @length: buffer length
+ * @vtag: virtual tag
+ * @timeout: recv timeout in milliseconds, 0 for infinite timeout
+ *
+ * Return:
+ * * read size in bytes
+ * * < 0 on error
+ */
+
+ssize_t mei_cldev_recv_vtag_timeout(struct mei_cl_device *cldev, u8 *buf, size_t length,
+ u8 *vtag, unsigned long timeout)
+{
+ struct mei_cl *cl = cldev->cl;
+
+ return __mei_cl_recv(cl, buf, length, vtag, 0, timeout);
+}
+EXPORT_SYMBOL_GPL(mei_cldev_recv_vtag_timeout);
+
+/**
+ * mei_cldev_send - me device send (write)
*
* @cldev: me client device
* @buf: buffer to send
@@ -340,6 +405,25 @@ ssize_t mei_cldev_send(struct mei_cl_device *cldev, const u8 *buf, size_t length
EXPORT_SYMBOL_GPL(mei_cldev_send);
/**
+ * mei_cldev_send_timeout - me device send with timeout (write)
+ *
+ * @cldev: me client device
+ * @buf: buffer to send
+ * @length: buffer length
+ * @timeout: send timeout in milliseconds, 0 for infinite timeout
+ *
+ * Return:
+ * * written size in bytes
+ * * < 0 on error
+ */
+ssize_t mei_cldev_send_timeout(struct mei_cl_device *cldev, const u8 *buf, size_t length,
+ unsigned long timeout)
+{
+ return mei_cldev_send_vtag_timeout(cldev, buf, length, 0, timeout);
+}
+EXPORT_SYMBOL_GPL(mei_cldev_send_timeout);
+
+/**
* mei_cldev_recv - client receive (read)
*
* @cldev: me client device
@@ -1301,7 +1385,7 @@ static inline void mei_cl_bus_set_name(struct mei_cl_device *cldev)
* @bus: mei device
* @me_cl: me client
*
- * Return: allocated device structur or NULL on allocation failure
+ * Return: allocated device structure or NULL on allocation failure
*/
static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus,
struct mei_me_client *me_cl)
@@ -1361,7 +1445,7 @@ static bool mei_cl_bus_dev_setup(struct mei_device *bus,
*
* @cldev: me client device
*
- * Return: 0 on success; < 0 on failre
+ * Return: 0 on success; < 0 on failure
*/
static int mei_cl_bus_dev_add(struct mei_cl_device *cldev)
{
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 32f2287823..9d090fa075 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -48,9 +48,9 @@ struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl)
/**
* mei_me_cl_release - free me client
*
- * Locking: called under "dev->device_lock" lock
- *
* @ref: me_client refcount
+ *
+ * Locking: called under "dev->device_lock" lock
*/
static void mei_me_cl_release(struct kref *ref)
{
@@ -63,9 +63,9 @@ static void mei_me_cl_release(struct kref *ref)
/**
* mei_me_cl_put - decrease me client refcount and free client if necessary
*
- * Locking: called under "dev->device_lock" lock
- *
* @me_cl: me client
+ *
+ * Locking: called under "dev->device_lock" lock
*/
void mei_me_cl_put(struct mei_me_client *me_cl)
{
@@ -329,10 +329,10 @@ void mei_io_cb_free(struct mei_cl_cb *cb)
/**
* mei_tx_cb_enqueue - queue tx callback
*
- * Locking: called under "dev->device_lock" lock
- *
* @cb: mei callback struct
* @head: an instance of list to queue on
+ *
+ * Locking: called under "dev->device_lock" lock
*/
static inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb,
struct list_head *head)
@@ -344,9 +344,9 @@ static inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb,
/**
* mei_tx_cb_dequeue - dequeue tx callback
*
- * Locking: called under "dev->device_lock" lock
- *
* @cb: mei callback struct to dequeue and free
+ *
+ * Locking: called under "dev->device_lock" lock
*/
static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb)
{
@@ -359,10 +359,10 @@ static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb)
/**
* mei_cl_set_read_by_fp - set pending_read flag to vtag struct for given fp
*
- * Locking: called under "dev->device_lock" lock
- *
* @cl: mei client
* @fp: pointer to file structure
+ *
+ * Locking: called under "dev->device_lock" lock
*/
static void mei_cl_set_read_by_fp(const struct mei_cl *cl,
const struct file *fp)
diff --git a/drivers/misc/mei/dma-ring.c b/drivers/misc/mei/dma-ring.c
index ef56f849b2..651e77ef82 100644
--- a/drivers/misc/mei/dma-ring.c
+++ b/drivers/misc/mei/dma-ring.c
@@ -124,6 +124,8 @@ void mei_dma_ring_reset(struct mei_device *dev)
* @buf: data buffer
* @offset: offset in slots.
* @n: number of slots to copy.
+ *
+ * Return: number of bytes copied
*/
static size_t mei_dma_copy_from(struct mei_device *dev, unsigned char *buf,
u32 offset, u32 n)
@@ -144,6 +146,8 @@ static size_t mei_dma_copy_from(struct mei_device *dev, unsigned char *buf,
* @buf: data buffer
* @offset: offset in slots.
* @n: number of slots to copy.
+ *
+ * Return: number of bytes copied
*/
static size_t mei_dma_copy_to(struct mei_device *dev, unsigned char *buf,
u32 offset, u32 n)
@@ -161,7 +165,7 @@ static size_t mei_dma_copy_to(struct mei_device *dev, unsigned char *buf,
/**
* mei_dma_ring_read() - read data from the ring
* @dev: mei device
- * @buf: buffer to read into: may be NULL in case of droping the data.
+ * @buf: buffer to read into: may be NULL in case of dropping the data.
* @len: length to read.
*/
void mei_dma_ring_read(struct mei_device *dev, unsigned char *buf, u32 len)
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index 12a62a911e..026b1f686c 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -91,6 +91,8 @@ static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
* @dev: mei device
* @hdr: mei header
* @data: payload
+ *
+ * Return: >=0 on success, <0 on error
*/
static inline int mei_hbm_write_message(struct mei_device *dev,
struct mei_msg_hdr *hdr,
@@ -111,7 +113,7 @@ void mei_hbm_idle(struct mei_device *dev)
}
/**
- * mei_hbm_reset - reset hbm counters and book keeping data structurs
+ * mei_hbm_reset - reset hbm counters and book keeping data structures
*
* @dev: the device structure
*/
@@ -907,7 +909,7 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
}
/**
- * mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
+ * mei_hbm_cl_disconnect_rsp - sends disconnect response to the FW
*
* @dev: the device structure
* @cl: a client to disconnect from
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index bdc65d50b9..961e5d53a2 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -123,6 +123,9 @@
# define PCI_CFG_HFS_1_OPMODE_MSK 0xf0000 /* OP MODE Mask: SPS <= 4.0 */
# define PCI_CFG_HFS_1_OPMODE_SPS 0xf0000 /* SPS SKU : SPS <= 4.0 */
#define PCI_CFG_HFS_2 0x48
+# define PCI_CFG_HFS_2_PM_CMOFF_TO_CMX_ERROR 0x1000000 /* CMoff->CMx wake after an error */
+# define PCI_CFG_HFS_2_PM_CM_RESET_ERROR 0x5000000 /* CME reset due to exception */
+# define PCI_CFG_HFS_2_PM_EVENT_MASK 0xf000000
#define PCI_CFG_HFS_3 0x60
# define PCI_CFG_HFS_3_FW_SKU_MSK 0x00000070
# define PCI_CFG_HFS_3_FW_SKU_IGN 0x00000000
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index da4ef0b519..d11a0740b4 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -443,11 +443,22 @@ static void mei_gsc_pxp_check(struct mei_device *dev)
struct mei_me_hw *hw = to_me_hw(dev);
u32 fwsts5 = 0;
- if (dev->pxp_mode == MEI_DEV_PXP_DEFAULT)
+ if (!kind_is_gsc(dev) && !kind_is_gscfi(dev))
return;
hw->read_fws(dev, PCI_CFG_HFS_5, &fwsts5);
trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_5", PCI_CFG_HFS_5, fwsts5);
+
+ if ((fwsts5 & GSC_CFG_HFS_5_BOOT_TYPE_MSK) == GSC_CFG_HFS_5_BOOT_TYPE_PXP) {
+ if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_DEFAULT)
+ dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_PERFORMED;
+ } else {
+ dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DEFAULT;
+ }
+
+ if (dev->pxp_mode == MEI_DEV_PXP_DEFAULT)
+ return;
+
if ((fwsts5 & GSC_CFG_HFS_5_BOOT_TYPE_MSK) == GSC_CFG_HFS_5_BOOT_TYPE_PXP) {
dev_dbg(dev->dev, "pxp mode is ready 0x%08x\n", fwsts5);
dev->pxp_mode = MEI_DEV_PXP_READY;
@@ -483,6 +494,43 @@ static int mei_me_hw_ready_wait(struct mei_device *dev)
}
/**
+ * mei_me_check_fw_reset - check for the firmware reset error and exception conditions
+ *
+ * @dev: mei device
+ */
+static void mei_me_check_fw_reset(struct mei_device *dev)
+{
+ struct mei_fw_status fw_status;
+ char fw_sts_str[MEI_FW_STATUS_STR_SZ] = {0};
+ int ret;
+ u32 fw_pm_event = 0;
+
+ if (!dev->saved_fw_status_flag)
+ goto end;
+
+ if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_PERFORMED) {
+ ret = mei_fw_status(dev, &fw_status);
+ if (!ret) {
+ fw_pm_event = fw_status.status[1] & PCI_CFG_HFS_2_PM_EVENT_MASK;
+ if (fw_pm_event != PCI_CFG_HFS_2_PM_CMOFF_TO_CMX_ERROR &&
+ fw_pm_event != PCI_CFG_HFS_2_PM_CM_RESET_ERROR)
+ goto end;
+ } else {
+ dev_err(dev->dev, "failed to read firmware status: %d\n", ret);
+ }
+ }
+
+ mei_fw_status2str(&dev->saved_fw_status, fw_sts_str, sizeof(fw_sts_str));
+ dev_warn(dev->dev, "unexpected reset: fw_pm_event = 0x%x, dev_state = %u fw status = %s\n",
+ fw_pm_event, dev->saved_dev_state, fw_sts_str);
+
+end:
+ if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_PERFORMED)
+ dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DONE;
+ dev->saved_fw_status_flag = false;
+}
+
+/**
* mei_me_hw_start - hw start routine
*
* @dev: mei device
@@ -492,6 +540,8 @@ static int mei_me_hw_start(struct mei_device *dev)
{
int ret = mei_me_hw_ready_wait(dev);
+ if (kind_is_gsc(dev) || kind_is_gscfi(dev))
+ mei_me_check_fw_reset(dev);
if (ret)
return ret;
dev_dbg(dev->dev, "hw is ready\n");
@@ -1300,8 +1350,13 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
/* check if ME wants a reset */
if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) {
- dev_warn(dev->dev, "FW not ready: resetting: dev_state = %d pxp = %d\n",
- dev->dev_state, dev->pxp_mode);
+ if (kind_is_gsc(dev) || kind_is_gscfi(dev)) {
+ dev_dbg(dev->dev, "FW not ready: resetting: dev_state = %d\n",
+ dev->dev_state);
+ } else {
+ dev_warn(dev->dev, "FW not ready: resetting: dev_state = %d\n",
+ dev->dev_state);
+ }
if (dev->dev_state == MEI_DEV_POWERING_DOWN ||
dev->dev_state == MEI_DEV_POWER_DOWN)
mei_cl_all_disconnect(dev);
@@ -1379,6 +1434,8 @@ EXPORT_SYMBOL_GPL(mei_me_irq_thread_handler);
/**
* mei_me_polling_thread - interrupt register polling thread
*
+ * @_dev: mei device
+ *
* The thread monitors the interrupt source register and calls
* mei_me_irq_thread_handler() to handle the firmware
* input.
@@ -1388,8 +1445,6 @@ EXPORT_SYMBOL_GPL(mei_me_irq_thread_handler);
* time increases yet again by MEI_POLLING_TIMEOUT_ACTIVE
* up to MEI_POLLING_TIMEOUT_IDLE.
*
- * @_dev: mei device
- *
* Return: always 0
*/
int mei_me_polling_thread(void *_dev)
@@ -1468,12 +1523,12 @@ static const struct mei_hw_ops mei_me_hw_ops = {
/**
* mei_me_fw_type_nm() - check for nm sku
*
+ * @pdev: pci device
+ *
* Read ME FW Status register to check for the Node Manager (NM) Firmware.
* The NM FW is only signaled in PCI function 0.
* __Note__: Deprecated by PCH8 and newer.
*
- * @pdev: pci device
- *
* Return: true in case of NM firmware
*/
static bool mei_me_fw_type_nm(const struct pci_dev *pdev)
@@ -1494,12 +1549,12 @@ static bool mei_me_fw_type_nm(const struct pci_dev *pdev)
/**
* mei_me_fw_type_sps_4() - check for sps 4.0 sku
*
+ * @pdev: pci device
+ *
* Read ME FW Status register to check for SPS Firmware.
* The SPS FW is only signaled in the PCI function 0.
* __Note__: Deprecated by SPS 5.0 and newer.
*
- * @pdev: pci device
- *
* Return: true in case of SPS firmware
*/
static bool mei_me_fw_type_sps_4(const struct pci_dev *pdev)
@@ -1519,11 +1574,11 @@ static bool mei_me_fw_type_sps_4(const struct pci_dev *pdev)
/**
* mei_me_fw_type_sps_ign() - check for sps or ign sku
*
+ * @pdev: pci device
+ *
* Read ME FW Status register to check for SPS or IGN Firmware.
* The SPS/IGN FW is only signaled in pci function 0
*
- * @pdev: pci device
- *
* Return: true in case of SPS/IGN firmware
*/
static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev)
diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h
index 95cf830b7c..204b92af6c 100644
--- a/drivers/misc/mei/hw-me.h
+++ b/drivers/misc/mei/hw-me.h
@@ -102,10 +102,14 @@ static inline bool mei_me_hw_use_polling(const struct mei_me_hw *hw)
* @MEI_ME_PCH12_SPS_CFG: Platform Controller Hub Gen12 5.0 and newer
* servers platforms with quirk for
* SPS firmware exclusion.
+ * @MEI_ME_PCH12_SPS_ITOUCH_CFG: Platform Controller Hub Gen12
+ * client platforms (iTouch)
* @MEI_ME_PCH15_CFG: Platform Controller Hub Gen15 and newer
* @MEI_ME_PCH15_SPS_CFG: Platform Controller Hub Gen15 and newer
* servers platforms with quirk for
* SPS firmware exclusion.
+ * @MEI_ME_GSC_CFG: Graphics System Controller
+ * @MEI_ME_GSCFI_CFG: Graphics System Controller Firmware Interface
* @MEI_ME_NUM_CFG: Upper Sentinel.
*/
enum mei_cfg_idx {
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index e910302fcd..eb800a07a8 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -429,7 +429,7 @@ struct mei_bus_message {
} __packed;
/**
- * struct hbm_cl_cmd - client specific host bus command
+ * struct mei_hbm_cl_cmd - client specific host bus command
* CONNECT, DISCONNECT, and FlOW CONTROL
*
* @hbm_cmd: bus message command header
@@ -733,7 +733,7 @@ struct hbm_dma_setup_response {
} __packed;
/**
- * struct mei_dma_ring_ctrl - dma ring control block
+ * struct hbm_dma_ring_ctrl - dma ring control block
*
* @hbuf_wr_idx: host circular buffer write index in slots
* @reserved1: reserved for alignment
@@ -806,8 +806,8 @@ struct hbm_client_dma_map_request {
} __packed;
/**
- * struct hbm_client_dma_unmap_request
- * client dma unmap request from the host to the firmware
+ * struct hbm_client_dma_unmap_request - client dma unmap request
+ * from the host to the firmware
*
* @hbm_cmd: bus message command header
* @status: unmap status
@@ -822,8 +822,8 @@ struct hbm_client_dma_unmap_request {
} __packed;
/**
- * struct hbm_client_dma_response
- * client dma unmap response from the firmware to the host
+ * struct hbm_client_dma_response - client dma unmap response
+ * from the firmware to the host
*
* @hbm_cmd: bus message command header
* @status: command status
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index c35e005b26..8ef2b1df8a 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -89,6 +89,22 @@ void mei_cancel_work(struct mei_device *dev)
}
EXPORT_SYMBOL_GPL(mei_cancel_work);
+static void mei_save_fw_status(struct mei_device *dev)
+{
+ struct mei_fw_status fw_status;
+ int ret;
+
+ ret = mei_fw_status(dev, &fw_status);
+ if (ret) {
+ dev_err(dev->dev, "failed to read firmware status: %d\n", ret);
+ return;
+ }
+
+ dev->saved_dev_state = dev->dev_state;
+ dev->saved_fw_status_flag = true;
+ memcpy(&dev->saved_fw_status, &fw_status, sizeof(fw_status));
+}
+
/**
* mei_reset - resets host and fw.
*
@@ -109,8 +125,14 @@ int mei_reset(struct mei_device *dev)
char fw_sts_str[MEI_FW_STATUS_STR_SZ];
mei_fw_status_str(dev, fw_sts_str, MEI_FW_STATUS_STR_SZ);
- dev_warn(dev->dev, "unexpected reset: dev_state = %s fw status = %s\n",
- mei_dev_state_str(state), fw_sts_str);
+ if (kind_is_gsc(dev) || kind_is_gscfi(dev)) {
+ dev_dbg(dev->dev, "unexpected reset: dev_state = %s fw status = %s\n",
+ mei_dev_state_str(state), fw_sts_str);
+ mei_save_fw_status(dev);
+ } else {
+ dev_warn(dev->dev, "unexpected reset: dev_state = %s fw status = %s\n",
+ mei_dev_state_str(state), fw_sts_str);
+ }
}
mei_clear_interrupts(dev);
@@ -394,6 +416,7 @@ void mei_device_init(struct mei_device *dev,
dev->open_handle_count = 0;
dev->pxp_mode = MEI_DEV_PXP_DEFAULT;
+ dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DEFAULT;
/*
* Reserving the first client ID
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index 0a0e984e56..b09b79feda 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -72,7 +72,7 @@ static void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr,
discard_len = 0;
}
/*
- * no need to check for size as it is guarantied
+ * no need to check for size as it is guaranteed
* that length fits into rd_msg_buf
*/
mei_read_slots(dev, dev->rd_msg_buf, discard_len);
@@ -626,9 +626,9 @@ static void mei_connect_timeout(struct mei_cl *cl)
/**
* mei_schedule_stall_timer - re-arm stall_timer work
*
- * Schedule stall timer
- *
* @dev: the device structure
+ *
+ * Schedule stall timer
*/
void mei_schedule_stall_timer(struct mei_device *dev)
{
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index bb4e9eabda..79e6f3c134 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -460,11 +460,11 @@ end:
/**
* mei_vt_support_check - check if client support vtags
*
- * Locking: called under "dev->device_lock" lock
- *
* @dev: mei_device
* @uuid: client UUID
*
+ * Locking: called under "dev->device_lock" lock
+ *
* Return:
* 0 - supported
* -ENOTTY - no such client
@@ -587,8 +587,8 @@ static int mei_ioctl_connect_vtag(struct file *file,
}
/**
- * mei_ioctl_client_notify_request -
- * propagate event notification request to client
+ * mei_ioctl_client_notify_request - propagate event notification
+ * request to client
*
* @file: pointer to file structure
* @request: 0 - disable, 1 - enable
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index cdf8a2edf0..37d7fb15ca 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -70,9 +70,9 @@ enum mei_dev_state {
/**
* enum mei_dev_pxp_mode - MEI PXP mode state
*
- * @MEI_DEV_PXP_DEFAULT: PCH based device, no initailization required
+ * @MEI_DEV_PXP_DEFAULT: PCH based device, no initialization required
* @MEI_DEV_PXP_INIT: device requires initialization, send setup message to firmware
- * @MEI_DEV_PXP_SETUP: device is in setup stage, waiting for firmware repsonse
+ * @MEI_DEV_PXP_SETUP: device is in setup stage, waiting for firmware response
* @MEI_DEV_PXP_READY: device initialized
*/
enum mei_dev_pxp_mode {
@@ -82,6 +82,19 @@ enum mei_dev_pxp_mode {
MEI_DEV_PXP_READY = 3,
};
+/**
+ * enum mei_dev_reset_to_pxp - reset to PXP mode performed
+ *
+ * @MEI_DEV_RESET_TO_PXP_DEFAULT: before reset
+ * @MEI_DEV_RESET_TO_PXP_PERFORMED: reset performed
+ * @MEI_DEV_RESET_TO_PXP_DONE: reset processed
+ */
+enum mei_dev_reset_to_pxp {
+ MEI_DEV_RESET_TO_PXP_DEFAULT = 0,
+ MEI_DEV_RESET_TO_PXP_PERFORMED = 1,
+ MEI_DEV_RESET_TO_PXP_DONE = 2,
+};
+
const char *mei_dev_state_str(int state);
enum mei_file_transaction_states {
@@ -534,6 +547,11 @@ struct mei_dev_timeouts {
*
* @dbgfs_dir : debugfs mei root directory
*
+ * @saved_fw_status : saved firmware status
+ * @saved_dev_state : saved device state
+ * @saved_fw_status_flag : flag indicating that firmware status was saved
+ * @gsc_reset_to_pxp : state of reset to the PXP mode
+ *
* @ops: : hw specific operations
* @hw : hw specific data
*/
@@ -630,6 +648,11 @@ struct mei_device {
struct dentry *dbgfs_dir;
#endif /* CONFIG_DEBUG_FS */
+ struct mei_fw_status saved_fw_status;
+ enum mei_dev_state saved_dev_state;
+ bool saved_fw_status_flag;
+ enum mei_dev_reset_to_pxp gsc_reset_to_pxp;
+
const struct mei_hw_ops *ops;
char hw[] __aligned(sizeof(void *));
};
@@ -874,5 +897,29 @@ static inline ssize_t mei_fw_status_str(struct mei_device *dev,
return ret;
}
+/**
+ * kind_is_gsc - checks whether the device is gsc
+ *
+ * @dev: the device structure
+ *
+ * Return: whether the device is gsc
+ */
+static inline bool kind_is_gsc(struct mei_device *dev)
+{
+ /* check kind for NULL because it may be not set, like at the fist call to hw_start */
+ return dev->kind && (strcmp(dev->kind, "gsc") == 0);
+}
+/**
+ * kind_is_gscfi - checks whether the device is gscfi
+ *
+ * @dev: the device structure
+ *
+ * Return: whether the device is gscfi
+ */
+static inline bool kind_is_gscfi(struct mei_device *dev)
+{
+ /* check kind for NULL because it may be not set, like at the fist call to hw_start */
+ return dev->kind && (strcmp(dev->kind, "gscfi") == 0);
+}
#endif
diff --git a/drivers/misc/mei/pxp/mei_pxp.c b/drivers/misc/mei/pxp/mei_pxp.c
index 2dcb9169e4..787c6a27a4 100644
--- a/drivers/misc/mei/pxp/mei_pxp.c
+++ b/drivers/misc/mei/pxp/mei_pxp.c
@@ -11,6 +11,7 @@
* negotiation messages to ME FW command payloads and vice versa.
*/
+#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mei.h>
@@ -22,27 +23,67 @@
#include "mei_pxp.h"
+static inline int mei_pxp_reenable(const struct device *dev, struct mei_cl_device *cldev)
+{
+ int ret;
+
+ dev_warn(dev, "Trying to reset the channel...\n");
+ ret = mei_cldev_disable(cldev);
+ if (ret < 0)
+ dev_warn(dev, "mei_cldev_disable failed. %d\n", ret);
+ /*
+ * Explicitly ignoring disable failure,
+ * enable may fix the states and succeed
+ */
+ ret = mei_cldev_enable(cldev);
+ if (ret < 0)
+ dev_err(dev, "mei_cldev_enable failed. %d\n", ret);
+ return ret;
+}
+
/**
* mei_pxp_send_message() - Sends a PXP message to ME FW.
* @dev: device corresponding to the mei_cl_device
* @message: a message buffer to send
* @size: size of the message
- * Return: 0 on Success, <0 on Failure
+ * @timeout_ms: timeout in milliseconds, zero means wait indefinitely.
+ *
+ * Returns: 0 on Success, <0 on Failure with the following defined failures.
+ * -ENODEV: Client was not connected.
+ * Caller may attempt to try again immediately.
+ * -ENOMEM: Internal memory allocation failure experienced.
+ * Caller may sleep to allow kernel reclaim before retrying.
+ * -EINTR : Calling thread received a signal. Caller may choose
+ * to abandon with the same thread id.
+ * -ETIME : Request is timed out.
+ * Caller may attempt to try again immediately.
*/
static int
-mei_pxp_send_message(struct device *dev, const void *message, size_t size)
+mei_pxp_send_message(struct device *dev, const void *message, size_t size, unsigned long timeout_ms)
{
struct mei_cl_device *cldev;
ssize_t byte;
+ int ret;
if (!dev || !message)
return -EINVAL;
cldev = to_mei_cl_device(dev);
- byte = mei_cldev_send(cldev, message, size);
+ byte = mei_cldev_send_timeout(cldev, message, size, timeout_ms);
if (byte < 0) {
dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
+ switch (byte) {
+ case -ENOMEM:
+ fallthrough;
+ case -ENODEV:
+ fallthrough;
+ case -ETIME:
+ ret = mei_pxp_reenable(dev, cldev);
+ if (ret)
+ byte = ret;
+ break;
+ }
return byte;
}
@@ -54,23 +95,53 @@ mei_pxp_send_message(struct device *dev, const void *message, size_t size)
* @dev: device corresponding to the mei_cl_device
* @buffer: a message buffer to contain the received message
* @size: size of the buffer
- * Return: bytes sent on Success, <0 on Failure
+ * @timeout_ms: timeout in milliseconds, zero means wait indefinitely.
+ *
+ * Returns: number of bytes send on Success, <0 on Failure with the following defined failures.
+ * -ENODEV: Client was not connected.
+ * Caller may attempt to try again from send immediately.
+ * -ENOMEM: Internal memory allocation failure experienced.
+ * Caller may sleep to allow kernel reclaim before retrying.
+ * -EINTR : Calling thread received a signal. Caller will need to repeat calling
+ * (with a different owning thread) to retrieve existing unclaimed response
+ * (and may discard it).
+ * -ETIME : Request is timed out.
+ * Caller may attempt to try again from send immediately.
*/
static int
-mei_pxp_receive_message(struct device *dev, void *buffer, size_t size)
+mei_pxp_receive_message(struct device *dev, void *buffer, size_t size, unsigned long timeout_ms)
{
struct mei_cl_device *cldev;
ssize_t byte;
+ bool retry = false;
+ int ret;
if (!dev || !buffer)
return -EINVAL;
cldev = to_mei_cl_device(dev);
- byte = mei_cldev_recv(cldev, buffer, size);
+retry:
+ byte = mei_cldev_recv_timeout(cldev, buffer, size, timeout_ms);
if (byte < 0) {
dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
- return byte;
+ switch (byte) {
+ case -ENOMEM:
+ /* Retry the read when pages are reclaimed */
+ msleep(20);
+ if (!retry) {
+ retry = true;
+ goto retry;
+ }
+ fallthrough;
+ case -ENODEV:
+ fallthrough;
+ case -ETIME:
+ ret = mei_pxp_reenable(dev, cldev);
+ if (ret)
+ byte = ret;
+ break;
+ }
}
return byte;