/** * 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_out_data_read - read dword from the device buffer * at offset idx * * @dev: the device structure * @idx: index in the device buffer * * returns register value at index */ static u32 mei_txe_out_data_read(const struct mei_device *dev, unsigned long idx) { struct mei_txe_hw *hw = to_txe_hw(dev); return mei_txe_br_reg_read(hw, BRIDGE_IPC_OUTPUT_PAYLOAD_REG + (idx * sizeof(u32))); }
/** * mei_txe_aliveness_get - get aliveness response register value * @dev: the device structure * * Extract HICR_HOST_ALIVENESS_RESP_ACK bit * from HICR_HOST_ALIVENESS_RESP register value */ static u32 mei_txe_aliveness_get(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 reg; reg = mei_txe_br_reg_read(hw, HICR_HOST_ALIVENESS_RESP_REG); return reg & HICR_HOST_ALIVENESS_RESP_ACK; }
/** * mei_txe_aliveness_req_get - get aliveness requested register value * * @dev: the device structure * * Extract HICR_HOST_ALIVENESS_RESP_ACK bit from * from HICR_HOST_ALIVENESS_REQ register value */ static u32 mei_txe_aliveness_req_get(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 reg; reg = mei_txe_br_reg_read(hw, SICR_HOST_ALIVENESS_REQ_REG); return reg & SICR_HOST_ALIVENESS_REQ_REQUESTED; }
/** * mei_txe_host_is_ready - check if the host is ready * * @dev: the device structure * * Return: true if host is ready */ static inline bool mei_txe_host_is_ready(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 reg = mei_txe_br_reg_read(hw, HICR_SEC_IPC_READINESS_REG); return !!(reg & HICR_SEC_IPC_READINESS_HOST_RDY); }
/** * mei_txe_hw_start - start the hardware after reset * * @dev: the device structure * * Return: 0 on success an error code otherwise */ static int mei_txe_hw_start(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); int ret; u32 hisr; /* bring back interrupts */ mei_txe_intr_enable(dev); ret = mei_txe_readiness_wait(dev); if (ret < 0) { dev_err(dev->dev, "waiting for readiness failed\n"); return ret; } /* * If HISR.INT2_STS interrupt status bit is set then clear it. */ hisr = mei_txe_br_reg_read(hw, HISR_REG); if (hisr & HISR_INT_2_STS) mei_txe_br_reg_write(hw, HISR_REG, HISR_INT_2_STS); /* Clear the interrupt cause of OutputDoorbell */ clear_bit(TXE_INTR_OUT_DB_BIT, &hw->intr_cause); ret = mei_txe_aliveness_set_sync(dev, 1); if (ret < 0) { dev_err(dev->dev, "wait for aliveness failed ... bailing out\n"); return ret; } pm_runtime_set_active(dev->dev); /* enable input ready interrupts: * SEC_IPC_HOST_INT_MASK.IPC_INPUT_READY_INT_MASK */ mei_txe_input_ready_interrupt_enable(dev); /* Set the SICR_SEC_IPC_OUTPUT_STATUS.IPC_OUTPUT_READY bit */ mei_txe_output_ready_set(hw); /* Set bit SICR_HOST_IPC_READINESS.HOST_RDY */ mei_txe_readiness_set_host_rdy(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_readiness_get - Reads and returns * the HICR_SEC_IPC_READINESS register value * * @dev: the device structure */ static u32 mei_txe_readiness_get(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); return mei_txe_br_reg_read(hw, HICR_SEC_IPC_READINESS_REG); }