/** * mei_txe_setup_satt2 - SATT2 configuration for DMA support. * * @dev: the device structure * @addr: physical address start of the range * @range: physical range size */ int mei_txe_setup_satt2(struct mei_device *dev, phys_addr_t addr, u32 range) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 lo32 = lower_32_bits(addr); u32 hi32 = upper_32_bits(addr); u32 ctrl; /* SATT is limited to 36 Bits */ if (hi32 & ~0xF) return -EINVAL; /* SATT has to be 16Byte aligned */ if (lo32 & 0xF) return -EINVAL; /* SATT range has to be 4Bytes aligned */ if (range & 0x4) return -EINVAL; /* SATT is limited to 32 MB range*/ if (range > SATT_RANGE_MAX) return -EINVAL; ctrl = SATT2_CTRL_VALID_MSK; ctrl |= hi32 << SATT2_CTRL_BR_BASE_ADDR_REG_SHIFT; mei_txe_br_reg_write(hw, SATT2_SAP_SIZE_REG, range); mei_txe_br_reg_write(hw, SATT2_BRG_BA_LSB_REG, lo32); mei_txe_br_reg_write(hw, SATT2_CTRL_REG, ctrl); dev_dbg(&dev->pdev->dev, "SATT2: SAP_SIZE_OFFSET=0x%08X, BRG_BA_LSB_OFFSET=0x%08X, CTRL_OFFSET=0x%08X\n", range, lo32, ctrl); return 0; }
/** * mei_txe_intr_enable - enable all interrupts * * @dev: the device structure */ static void mei_txe_intr_enable(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); mei_txe_br_reg_write(hw, HHIER_REG, IPC_HHIER_MSK); mei_txe_br_reg_write(hw, HIER_REG, HIER_INT_EN_MSK); }
/** * mei_txe_intr_disable - disable all interrupts * * @dev: the device structure */ static void mei_txe_intr_disable(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); mei_txe_br_reg_write(hw, HHIER_REG, 0); mei_txe_br_reg_write(hw, HIER_REG, 0); }
/** * mei_txe_intr_clear - clear all interrupts * * @dev: the device structure */ static inline void mei_txe_intr_clear(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); mei_txe_sec_reg_write_silent(hw, SEC_IPC_HOST_INT_STATUS_REG, SEC_IPC_HOST_INT_STATUS_PENDING); mei_txe_br_reg_write(hw, HISR_REG, HISR_INT_STS_MSK); mei_txe_br_reg_write(hw, HHISR_REG, IPC_HHIER_MSK); }
/** * 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_readiness_set_host_rdy * * @dev: the device structure */ static void mei_txe_readiness_set_host_rdy(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); mei_txe_br_reg_write(hw, SICR_HOST_IPC_READINESS_REQ_REG, SICR_HOST_IPC_READINESS_HOST_RDY); }
/** * mei_txe_readiness_clear - clear host readiness bit * * @dev: the device structure */ static void mei_txe_readiness_clear(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); mei_txe_br_reg_write(hw, SICR_HOST_IPC_READINESS_REQ_REG, SICR_HOST_IPC_READINESS_RDY_CLR); }
/** * mei_txe_aliveness_set - request for aliveness change * * @dev: the device structure * @req: requested aliveness value * * Request for aliveness change and returns true if the change is * really needed and false if aliveness is already * in the requested state * Requires device lock to be held */ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) { struct mei_txe_hw *hw = to_txe_hw(dev); bool do_req = hw->aliveness != req; dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n", hw->aliveness, req); if (do_req) { dev->pg_event = MEI_PG_EVENT_WAIT; mei_txe_br_reg_write(hw, SICR_HOST_ALIVENESS_REQ_REG, req); } return do_req; }
/** * 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_output_ready_set - Sets the SICR_SEC_IPC_OUTPUT_STATUS bit to 1 * * @dev: the device structure */ static void mei_txe_output_ready_set(struct mei_txe_hw *hw) { mei_txe_br_reg_write(hw, SICR_SEC_IPC_OUTPUT_STATUS_REG, SEC_IPC_OUTPUT_STATUS_RDY); }