Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 964a2331 authored by Tomas Winkler's avatar Tomas Winkler Committed by Greg Kroah-Hartman
Browse files

mei: expose hardware power gating state to mei layer



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: default avatarTomas Winkler <tomas.winkler@intel.com>
Signed-off-by: default avatarAlexander Usyskin <alexander.usyskin@intel.com>
Reviewed-by: default avatarAlexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ee7e5afd
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -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 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
}
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,
+32 −11
Original line number Diff line number Diff line
@@ -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;
}

@@ -291,6 +296,20 @@ static bool mei_txe_pg_is_enabled(struct mei_device *dev)
	return true;
}

/**
 * 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
 *
@@ -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;

+6 −8
Original line number Diff line number Diff line
@@ -38,8 +38,7 @@
 * @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
 * @wait_aliveness_resp: aliveness wait queue
 * @intr_cause:          translated interrupt cause
 */
struct mei_txe_hw {
@@ -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;
};
+2 −0
Original line number Diff line number Diff line
@@ -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);
+38 −0
Original line number Diff line number Diff line
@@ -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);