From patchwork Tue Mar 18 20:51:59 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Winkler, Tomas" X-Patchwork-Id: 3855721 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 31FA79F369 for ; Wed, 19 Mar 2014 17:56:24 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 12E562021F for ; Wed, 19 Mar 2014 17:56:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D06BF2012E for ; Wed, 19 Mar 2014 17:56:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932975AbaCRUxk (ORCPT ); Tue, 18 Mar 2014 16:53:40 -0400 Received: from mga09.intel.com ([134.134.136.24]:33382 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932681AbaCRUxj (ORCPT ); Tue, 18 Mar 2014 16:53:39 -0400 Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga102.jf.intel.com with ESMTP; 18 Mar 2014 13:49:04 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.97,680,1389772800"; d="scan'208";a="475172412" Received: from twinkler-dhg.jer.intel.com ([10.12.87.116]) by orsmga001.jf.intel.com with ESMTP; 18 Mar 2014 13:53:34 -0700 From: Tomas Winkler To: gregkh@linuxfoundation.org Cc: arnd@arndb.de, rjw@rjwysocki.net, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, Tomas Winkler , Alexander Usyskin Subject: [char-misc-next 05/13] mei: expose hardware power gating state to mei layer Date: Tue, 18 Mar 2014 22:51:59 +0200 Message-Id: <1395175927-10562-6-git-send-email-tomas.winkler@intel.com> X-Mailer: git-send-email 1.8.5.3 In-Reply-To: <1395175927-10562-1-git-send-email-tomas.winkler@intel.com> References: <1395175927-10562-1-git-send-email-tomas.winkler@intel.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Since the runtime pm and the internal power gating cannot be in complete sync in regards to I/O operations, we need to expose the device hardware internal power gating state to mei layer 2. We add pg_state handler that translate the hw internal pg state to mei layer 2. We add power gating event variable to keep power track of power gating transitions Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Reviewed-by: Alexander Usyskin --- drivers/misc/mei/hw-me.c | 15 +++++++++++++++ drivers/misc/mei/hw-txe.c | 43 ++++++++++++++++++++++++++++++++----------- drivers/misc/mei/hw-txe.h | 14 ++++++-------- drivers/misc/mei/init.c | 2 ++ drivers/misc/mei/mei_dev.h | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 19 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 7a7e662..02f3b0c 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -113,6 +113,19 @@ static void mei_me_hw_config(struct mei_device *dev) /* Doesn't change in runtime */ dev->hbuf_depth = (hcsr & H_CBD) >> 24; } + +/** + * mei_me_pg_state - translate internal pg state + * to the mei power gating state + * + * @hw - me hardware + * returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise + */ +static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev) +{ + return MEI_PG_OFF; +} + /** * mei_clear_interrupts - clear and stop interrupts * @@ -601,6 +614,8 @@ end: } static const struct mei_hw_ops mei_me_hw_ops = { + .pg_state = mei_me_pg_state, + .host_is_ready = mei_me_host_is_ready, .hw_is_ready = mei_me_hw_is_ready, diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index 49f197a..4a6f567 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -158,7 +158,7 @@ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n", hw->aliveness, req); if (do_req) { - hw->recvd_aliveness = false; + dev->pg_event = MEI_PG_EVENT_WAIT; mei_txe_br_reg_write(hw, SICR_HOST_ALIVENESS_REQ_REG, req); } return do_req; @@ -213,6 +213,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) do { hw->aliveness = mei_txe_aliveness_get(dev); if (hw->aliveness == expected) { + dev->pg_event = MEI_PG_EVENT_IDLE; dev_dbg(&dev->pdev->dev, "aliveness settled after %d msecs\n", t); return t; @@ -223,6 +224,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) t += MSEC_PER_SEC / 5; } while (t < SEC_ALIVENESS_WAIT_TIMEOUT); + dev->pg_event = MEI_PG_EVENT_IDLE; dev_err(&dev->pdev->dev, "aliveness timed out\n"); return -ETIME; } @@ -249,19 +251,22 @@ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected) return 0; mutex_unlock(&dev->device_lock); - err = wait_event_timeout(hw->wait_aliveness, - hw->recvd_aliveness, timeout); + err = wait_event_timeout(hw->wait_aliveness_resp, + dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout); mutex_lock(&dev->device_lock); hw->aliveness = mei_txe_aliveness_get(dev); ret = hw->aliveness == expected ? 0 : -ETIME; if (ret) - dev_err(&dev->pdev->dev, "aliveness timed out"); + dev_warn(&dev->pdev->dev, "aliveness timed out = %ld aliveness = %d event = %d\n", + err, hw->aliveness, dev->pg_event); else - dev_dbg(&dev->pdev->dev, "aliveness settled after %d msecs\n", - jiffies_to_msecs(timeout - err)); - hw->recvd_aliveness = false; + dev_dbg(&dev->pdev->dev, "aliveness settled after = %d msec aliveness = %d event = %d\n", + jiffies_to_msecs(timeout - err), + hw->aliveness, dev->pg_event); + + dev->pg_event = MEI_PG_EVENT_IDLE; return ret; } @@ -292,6 +297,20 @@ static bool mei_txe_pg_is_enabled(struct mei_device *dev) } /** + * mei_txe_pg_state - translate aliveness register value + * to the mei power gating state + * + * @dev: the device structure + * + * returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise + */ +static inline enum mei_pg_state mei_txe_pg_state(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + return hw->aliveness ? MEI_PG_OFF : MEI_PG_ON; +} + +/** * mei_txe_input_ready_interrupt_enable - sets the Input Ready Interrupt * * @dev: the device structure @@ -972,9 +991,9 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) /* Clear the interrupt cause */ dev_dbg(&dev->pdev->dev, "Aliveness Interrupt: Status: %d\n", hw->aliveness); - hw->recvd_aliveness = true; - if (waitqueue_active(&hw->wait_aliveness)) - wake_up(&hw->wait_aliveness); + dev->pg_event = MEI_PG_EVENT_RECEIVED; + if (waitqueue_active(&hw->wait_aliveness_resp)) + wake_up(&hw->wait_aliveness_resp); } @@ -1024,6 +1043,8 @@ static const struct mei_hw_ops mei_txe_hw_ops = { .host_is_ready = mei_txe_host_is_ready, + .pg_state = mei_txe_pg_state, + .hw_is_ready = mei_txe_hw_is_ready, .hw_reset = mei_txe_hw_reset, .hw_config = mei_txe_hw_config, @@ -1069,7 +1090,7 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev) hw = to_txe_hw(dev); - init_waitqueue_head(&hw->wait_aliveness); + init_waitqueue_head(&hw->wait_aliveness_resp); dev->ops = &mei_txe_hw_ops; diff --git a/drivers/misc/mei/hw-txe.h b/drivers/misc/mei/hw-txe.h index 0812d98..799f9f2 100644 --- a/drivers/misc/mei/hw-txe.h +++ b/drivers/misc/mei/hw-txe.h @@ -35,12 +35,11 @@ /** * struct mei_txe_hw - txe hardware specifics * - * @mem_addr: SeC and BRIDGE bars - * @aliveness: aliveness (power gating) state of the hardware - * @readiness: readiness state of the hardware - * @wait_aliveness: aliveness wait queue - * @recvd_aliveness: aliveness interrupt was recived - * @intr_cause: translated interrupt cause + * @mem_addr: SeC and BRIDGE bars + * @aliveness: aliveness (power gating) state of the hardware + * @readiness: readiness state of the hardware + * @wait_aliveness_resp: aliveness wait queue + * @intr_cause: translated interrupt cause */ struct mei_txe_hw { void __iomem *mem_addr[NUM_OF_MEM_BARS]; @@ -48,8 +47,7 @@ struct mei_txe_hw { u32 readiness; u32 slots; - wait_queue_head_t wait_aliveness; - bool recvd_aliveness; + wait_queue_head_t wait_aliveness_resp; unsigned long intr_cause; }; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index cc604e1..4b1eb37 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -341,6 +341,8 @@ void mei_device_init(struct mei_device *dev) * 0: Reserved for MEI Bus Message communications */ bitmap_set(dev->host_clients_map, 0, 1); + + dev->pg_event = MEI_PG_EVENT_IDLE; } EXPORT_SYMBOL_GPL(mei_device_init); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index ca7581c..bec6ade 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -220,6 +220,7 @@ struct mei_cl { * @hw_start - start hw after reset * @hw_config - configure hw + * @pg_state - power gating state of the device * @pg_is_enabled - is power gating enabled * @intr_clear - clear pending interrupts @@ -246,6 +247,7 @@ struct mei_hw_ops { int (*hw_start)(struct mei_device *dev); void (*hw_config)(struct mei_device *dev); + enum mei_pg_state (*pg_state)(struct mei_device *dev); bool (*pg_is_enabled)(struct mei_device *dev); void (*intr_clear)(struct mei_device *dev); @@ -335,11 +337,37 @@ struct mei_cl_device { 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 + */ +enum mei_pg_event { + MEI_PG_EVENT_IDLE, + MEI_PG_EVENT_WAIT, + MEI_PG_EVENT_RECEIVED, +}; + +/** + * enum mei_pg_state - device internal power gating state + * + * @MEI_PG_OFF: device is not power gated - it is active + * @MEI_PG_ON: device is power gated - it is in lower power state + */ +enum mei_pg_state { + MEI_PG_OFF = 0, + MEI_PG_ON = 1, +}; + /** * struct mei_device - MEI private device struct * @reset_count - limits the number of consecutive resets * @hbm_state - state of host bus message protocol + * @pg_event - power gating event * @mem_addr - mem mapped base register address * @hbuf_depth - depth of hardware host/write buffer is slots @@ -387,6 +415,11 @@ struct mei_device { enum mei_hbm_state hbm_state; u16 init_clients_timer; + /* + * Power Gating support + */ + enum mei_pg_event pg_event; + unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */ u32 rd_msg_hdr; @@ -563,6 +596,11 @@ static inline void mei_hw_config(struct mei_device *dev) dev->ops->hw_config(dev); } +static inline enum mei_pg_state mei_pg_state(struct mei_device *dev) +{ + return dev->ops->pg_state(dev); +} + static inline bool mei_pg_is_enabled(struct mei_device *dev) { return dev->ops->pg_is_enabled(dev);