/** * 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; }
/** * 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; }
/** * 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; }
/** * mei_txe_sec_reg_read - Reads 32bit data from the SeC BAR * * @dev: the device structure * @offset: register offset * * Reads 32bit data from the SeC BAR and shout loud if aliveness is not set */ static inline u32 mei_txe_sec_reg_read(struct mei_txe_hw *hw, unsigned long offset) { WARN(!hw->aliveness, "sec read: aliveness not asserted\n"); return mei_txe_sec_reg_read_silent(hw, offset); }