Ejemplo n.º 1
0
/**
 * mei_txe_aliveness_wait - waits for aliveness to settle
 *
 * @dev: the device structure
 * @expected: expected aliveness value
 *
 * Waits for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set
 * returns returns 0 on success and < 0 otherwise
 */
static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected)
{
	struct mei_txe_hw *hw = to_txe_hw(dev);
	const unsigned long timeout =
			msecs_to_jiffies(SEC_ALIVENESS_WAIT_TIMEOUT);
	long err;
	int ret;

	hw->aliveness = mei_txe_aliveness_get(dev);
	if (hw->aliveness == expected)
		return 0;

	mutex_unlock(&dev->device_lock);
	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_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 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;
}
Ejemplo n.º 2
0
/**
 *  mei_txe_hw_config - configure hardware at the start of the devices
 *
 * @dev: the device structure
 *
 * Configure hardware at the start of the device should be done only
 *   once at the device probe time
 */
static void mei_txe_hw_config(struct mei_device *dev)
{

	struct mei_txe_hw *hw = to_txe_hw(dev);
	/* Doesn't change in runtime */
	dev->hbuf_depth = PAYLOAD_SIZE / 4;

	hw->aliveness = mei_txe_aliveness_get(dev);
	hw->readiness = mei_txe_readiness_get(dev);

	dev_dbg(&dev->pdev->dev, "aliveness_resp = 0x%08x, readiness = 0x%08x.\n",
		hw->aliveness, hw->readiness);
}
Ejemplo n.º 3
0
/**
 * mei_txe_check_and_ack_intrs - translate multi BAR interrupt into
 *  single bit mask and acknowledge the interrupts
 *
 * @dev: the device structure
 * @do_ack: acknowledge interrupts
 *
 * Return: true if found interrupts to process.
 */
static bool mei_txe_check_and_ack_intrs(struct mei_device *dev, bool do_ack)
{
	struct mei_txe_hw *hw = to_txe_hw(dev);
	u32 hisr;
	u32 hhisr;
	u32 ipc_isr;
	u32 aliveness;
	bool generated;

	/* read interrupt registers */
	hhisr = mei_txe_br_reg_read(hw, HHISR_REG);
	generated = (hhisr & IPC_HHIER_MSK);
	if (!generated)
		goto out;

	hisr = mei_txe_br_reg_read(hw, HISR_REG);

	aliveness = mei_txe_aliveness_get(dev);
	if (hhisr & IPC_HHIER_SEC && aliveness) {
		ipc_isr = mei_txe_sec_reg_read_silent(hw,
				SEC_IPC_HOST_INT_STATUS_REG);
	} else {
		ipc_isr = 0;
		hhisr &= ~IPC_HHIER_SEC;
	}

	generated = generated ||
		(hisr & HISR_INT_STS_MSK) ||
		(ipc_isr & SEC_IPC_HOST_INT_STATUS_PENDING);

	if (generated && do_ack) {
		/* Save the interrupt causes */
		hw->intr_cause |= hisr & HISR_INT_STS_MSK;
		if (ipc_isr & SEC_IPC_HOST_INT_STATUS_IN_RDY)
			hw->intr_cause |= TXE_INTR_IN_READY;


		mei_txe_intr_disable(dev);
		/* Clear the interrupts in hierarchy:
		 * IPC and Bridge, than the High Level */
		mei_txe_sec_reg_write_silent(hw,
			SEC_IPC_HOST_INT_STATUS_REG, ipc_isr);
		mei_txe_br_reg_write(hw, HISR_REG, hisr);
		mei_txe_br_reg_write(hw, HHISR_REG, hhisr);
	}

out:
	return generated;
}
Ejemplo n.º 4
0
/**
 * mei_txe_hw_reset - resets host and fw.
 *
 * @dev: the device structure
 * @intr_enable: if interrupt should be enabled after reset.
 *
 * returns 0 on success and < 0 in case of error
 */
static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable)
{
	struct mei_txe_hw *hw = to_txe_hw(dev);

	u32 aliveness_req;
	/*
	 * read input doorbell to ensure consistency between  Bridge and SeC
	 * return value might be garbage return
	 */
	(void)mei_txe_sec_reg_read_silent(hw, SEC_IPC_INPUT_DOORBELL_REG);

	aliveness_req = mei_txe_aliveness_req_get(dev);
	hw->aliveness = mei_txe_aliveness_get(dev);

	/* Disable interrupts in this stage we will poll */
	mei_txe_intr_disable(dev);

	/*
	 * If Aliveness Request and Aliveness Response are not equal then
	 * wait for them to be equal
	 * Since we might have interrupts disabled - poll for it
	 */
	if (aliveness_req != hw->aliveness)
		if (mei_txe_aliveness_poll(dev, aliveness_req) < 0) {
			dev_err(&dev->pdev->dev,
				"wait for aliveness settle failed ... bailing out\n");
			return -EIO;
		}

	/*
	 * If Aliveness Request and Aliveness Response are set then clear them
	 */
	if (aliveness_req) {
		mei_txe_aliveness_set(dev, 0);
		if (mei_txe_aliveness_poll(dev, 0) < 0) {
			dev_err(&dev->pdev->dev,
				"wait for aliveness failed ... bailing out\n");
			return -EIO;
		}
	}

	/*
	 * Set rediness RDY_CLR bit
	 */
	mei_txe_readiness_clear(dev);

	return 0;
}
Ejemplo n.º 5
0
/**
 * mei_txe_aliveness_poll - waits for aliveness to settle
 *
 * @dev: the device structure
 * @expected: expected aliveness value
 *
 * Polls for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set
 *
 * 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);
	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 %lld usecs\n",
				ktime_to_us(ktime_sub(ktime_get(), start)));
			return 0;
		}
		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");
	return -ETIME;
}
Ejemplo n.º 6
0
/**
 * mei_txe_aliveness_poll - waits for aliveness to settle
 *
 * @dev: the device structure
 * @expected: expected aliveness value
 *
 * Polls for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set
 * returns > 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;

	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;
		}
		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);

	dev->pg_event = MEI_PG_EVENT_IDLE;
	dev_err(&dev->pdev->dev, "aliveness timed out\n");
	return -ETIME;
}
Ejemplo n.º 7
0
/**
 * mei_txe_irq_thread_handler - txe interrupt thread
 *
 * @irq: The irq number
 * @dev_id: pointer to the device structure
 *
 * returns irqreturn_t
 *
 */
irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
{
	struct mei_device *dev = (struct mei_device *) dev_id;
	struct mei_txe_hw *hw = to_txe_hw(dev);
	struct mei_cl_cb complete_list;
	s32 slots;
	int rets = 0;

	dev_dbg(&dev->pdev->dev, "irq thread: Interrupt Registers HHISR|HISR|SEC=%02X|%04X|%02X\n",
		mei_txe_br_reg_read(hw, HHISR_REG),
		mei_txe_br_reg_read(hw, HISR_REG),
		mei_txe_sec_reg_read_silent(hw, SEC_IPC_HOST_INT_STATUS_REG));


	/* initialize our complete list */
	mutex_lock(&dev->device_lock);
	mei_io_list_init(&complete_list);

	if (pci_dev_msi_enabled(dev->pdev))
		mei_txe_check_and_ack_intrs(dev, true);

	/* show irq events */
	mei_txe_pending_interrupts(dev);

	hw->aliveness = mei_txe_aliveness_get(dev);
	hw->readiness = mei_txe_readiness_get(dev);

	/* Readiness:
	 * Detection of TXE driver going through reset
	 * or TXE driver resetting the HECI interface.
	 */
	if (test_and_clear_bit(TXE_INTR_READINESS_BIT, &hw->intr_cause)) {
		dev_dbg(&dev->pdev->dev, "Readiness Interrupt was received...\n");

		/* Check if SeC is going through reset */
		if (mei_txe_readiness_is_sec_rdy(hw->readiness)) {
			dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
			dev->recvd_hw_ready = true;
		} else {
			dev->recvd_hw_ready = false;
			if (dev->dev_state != MEI_DEV_RESETTING) {

				dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n");
				schedule_work(&dev->reset_work);
				goto end;

			}
		}
		wake_up(&dev->wait_hw_ready);
	}

	/************************************************************/
	/* Check interrupt cause:
	 * Aliveness: Detection of SeC acknowledge of host request that
	 * it remain alive or host cancellation of that request.
	 */

	if (test_and_clear_bit(TXE_INTR_ALIVENESS_BIT, &hw->intr_cause)) {
		/* Clear the interrupt cause */
		dev_dbg(&dev->pdev->dev,
			"Aliveness Interrupt: Status: %d\n", hw->aliveness);
		dev->pg_event = MEI_PG_EVENT_RECEIVED;
		if (waitqueue_active(&hw->wait_aliveness_resp))
			wake_up(&hw->wait_aliveness_resp);
	}


	/* Output Doorbell:
	 * Detection of SeC having sent output to host
	 */
	slots = mei_count_full_read_slots(dev);
	if (test_and_clear_bit(TXE_INTR_OUT_DB_BIT, &hw->intr_cause)) {
		/* Read from TXE */
		rets = mei_irq_read_handler(dev, &complete_list, &slots);
		if (rets && dev->dev_state != MEI_DEV_RESETTING) {
			dev_err(&dev->pdev->dev,
				"mei_irq_read_handler ret = %d.\n", rets);

			schedule_work(&dev->reset_work);
			goto end;
		}
	}
	/* Input Ready: Detection if host can write to SeC */
	if (test_and_clear_bit(TXE_INTR_IN_READY_BIT, &hw->intr_cause)) {
		dev->hbuf_is_ready = true;
		hw->slots = dev->hbuf_depth;
	}

	if (hw->aliveness && dev->hbuf_is_ready) {
		/* get the real register value */
		dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
		rets = mei_irq_write_handler(dev, &complete_list);
		if (rets && rets != -EMSGSIZE)
			dev_err(&dev->pdev->dev, "mei_irq_write_handler ret = %d.\n",
				rets);
		dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
	}

	mei_irq_compl_handler(dev, &complete_list);

end:
	dev_dbg(&dev->pdev->dev, "interrupt thread end ret = %d\n", rets);

	mutex_unlock(&dev->device_lock);

	mei_enable_interrupts(dev);
	return IRQ_HANDLED;
}