diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/mei/client.c | 2 | ||||
-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/mei_dev.h | 11 |
4 files changed, 90 insertions, 15 deletions
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index bc9ba5359bc6..351e4bc07a51 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -478,7 +478,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; } diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 350a28af98aa..c1ef64c5e81e 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -627,11 +627,27 @@ int mei_me_pg_unset_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; @@ -639,6 +655,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 @@ -670,6 +699,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 @@ -727,6 +774,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)) { @@ -763,9 +812,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); } @@ -790,6 +840,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 c5e1902e493f..c11a01a1ec21 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 @@ -1139,6 +1151,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/mei_dev.h b/drivers/misc/mei/mei_dev.h index 71744b16cc8c..909b2d81895b 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -264,6 +264,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 @@ -293,6 +294,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); @@ -390,11 +392,15 @@ struct mei_cl_device { * @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, }; /** @@ -721,6 +727,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); |