// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2022, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ #include <linux/pci.h> #include <linux/jiffies.h> #include <linux/ktime.h> #include <linux/delay.h> #include <linux/kthread.h> #include <linux/interrupt.h> #include <linux/pm_runtime.h> #include <linux/mei.h> #include "mei_dev.h" #include "hw-txe.h" #include "client.h" #include "hbm.h" #include "mei-trace.h" #define TXE_HBUF_DEPTH … /** * mei_txe_reg_read - Reads 32bit data from the txe device * * @base_addr: registers base address * @offset: register offset * * Return: register value */ static inline u32 mei_txe_reg_read(void __iomem *base_addr, unsigned long offset) { … } /** * mei_txe_reg_write - Writes 32bit data to the txe device * * @base_addr: registers base address * @offset: register offset * @value: the value to write */ static inline void mei_txe_reg_write(void __iomem *base_addr, unsigned long offset, u32 value) { … } /** * mei_txe_sec_reg_read_silent - Reads 32bit data from the SeC BAR * * @hw: the txe hardware structure * @offset: register offset * * Doesn't check for aliveness while Reads 32bit data from the SeC BAR * * Return: register value */ static inline u32 mei_txe_sec_reg_read_silent(struct mei_txe_hw *hw, unsigned long offset) { … } /** * mei_txe_sec_reg_read - Reads 32bit data from the SeC BAR * * @hw: the txe hardware structure * @offset: register offset * * Reads 32bit data from the SeC BAR and shout loud if aliveness is not set * * Return: register value */ static inline u32 mei_txe_sec_reg_read(struct mei_txe_hw *hw, unsigned long offset) { … } /** * mei_txe_sec_reg_write_silent - Writes 32bit data to the SeC BAR * doesn't check for aliveness * * @hw: the txe hardware structure * @offset: register offset * @value: value to write * * Doesn't check for aliveness while writes 32bit data from to the SeC BAR */ static inline void mei_txe_sec_reg_write_silent(struct mei_txe_hw *hw, unsigned long offset, u32 value) { … } /** * mei_txe_sec_reg_write - Writes 32bit data to the SeC BAR * * @hw: the txe hardware structure * @offset: register offset * @value: value to write * * Writes 32bit data from the SeC BAR and shout loud if aliveness is not set */ static inline void mei_txe_sec_reg_write(struct mei_txe_hw *hw, unsigned long offset, u32 value) { … } /** * mei_txe_br_reg_read - Reads 32bit data from the Bridge BAR * * @hw: the txe hardware structure * @offset: offset from which to read the data * * Return: the byte read. */ static inline u32 mei_txe_br_reg_read(struct mei_txe_hw *hw, unsigned long offset) { … } /** * mei_txe_br_reg_write - Writes 32bit data to the Bridge BAR * * @hw: the txe hardware structure * @offset: offset from which to write the data * @value: the byte to write */ static inline void mei_txe_br_reg_write(struct mei_txe_hw *hw, unsigned long offset, u32 value) { … } /** * 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 * * Locking: called under "dev->device_lock" lock * * Return: true if request was send */ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) { … } /** * mei_txe_aliveness_req_get - get aliveness requested register value * * @dev: the device structure * * Extract HICR_HOST_ALIVENESS_RESP_ACK bit from * HICR_HOST_ALIVENESS_REQ register value * * Return: SICR_HOST_ALIVENESS_REQ_REQUESTED bit value */ static u32 mei_txe_aliveness_req_get(struct mei_device *dev) { … } /** * mei_txe_aliveness_get - get aliveness response register value * * @dev: the device structure * * Return: HICR_HOST_ALIVENESS_RESP_ACK bit from HICR_HOST_ALIVENESS_RESP * register */ static u32 mei_txe_aliveness_get(struct mei_device *dev) { … } /** * 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) { … } /** * 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 * * Return: 0 on success and < 0 otherwise */ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected) { … } /** * mei_txe_aliveness_set_sync - sets an wait for aliveness to complete * * @dev: the device structure * @req: requested aliveness value * * Return: 0 on success and < 0 otherwise */ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req) { … } /** * mei_txe_pg_in_transition - is device now in pg transition * * @dev: the device structure * * Return: true if in pg transition, false otherwise */ static bool mei_txe_pg_in_transition(struct mei_device *dev) { … } /** * mei_txe_pg_is_enabled - detect if PG is supported by HW * * @dev: the device structure * * Return: true is pg supported, false otherwise */ static bool mei_txe_pg_is_enabled(struct mei_device *dev) { … } /** * mei_txe_pg_state - translate aliveness register value * to the mei power gating state * * @dev: the device structure * * Return: 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) { … } /** * mei_txe_input_ready_interrupt_enable - sets the Input Ready Interrupt * * @dev: the device structure */ static void mei_txe_input_ready_interrupt_enable(struct mei_device *dev) { … } /** * mei_txe_input_doorbell_set - sets bit 0 in * SEC_IPC_INPUT_DOORBELL.IPC_INPUT_DOORBELL. * * @hw: the txe hardware structure */ static void mei_txe_input_doorbell_set(struct mei_txe_hw *hw) { … } /** * mei_txe_output_ready_set - Sets the SICR_SEC_IPC_OUTPUT_STATUS bit to 1 * * @hw: the txe hardware structure */ static void mei_txe_output_ready_set(struct mei_txe_hw *hw) { … } /** * mei_txe_is_input_ready - check if TXE is ready for receiving data * * @dev: the device structure * * Return: true if INPUT STATUS READY bit is set */ static bool mei_txe_is_input_ready(struct mei_device *dev) { … } /** * mei_txe_intr_clear - clear all interrupts * * @dev: the device structure */ static inline void mei_txe_intr_clear(struct mei_device *dev) { … } /** * mei_txe_intr_disable - disable all interrupts * * @dev: the device structure */ static void mei_txe_intr_disable(struct mei_device *dev) { … } /** * mei_txe_intr_enable - enable all interrupts * * @dev: the device structure */ static void mei_txe_intr_enable(struct mei_device *dev) { … } /** * mei_txe_synchronize_irq - wait for pending IRQ handlers * * @dev: the device structure */ static void mei_txe_synchronize_irq(struct mei_device *dev) { … } /** * mei_txe_pending_interrupts - check if there are pending interrupts * only Aliveness, Input ready, and output doorbell are of relevance * * @dev: the device structure * * Checks if there are pending interrupts * only Aliveness, Readiness, Input ready, and Output doorbell are relevant * * Return: true if there are pending interrupts */ static bool mei_txe_pending_interrupts(struct mei_device *dev) { … } /** * mei_txe_input_payload_write - write a dword to the host buffer * at offset idx * * @dev: the device structure * @idx: index in the host buffer * @value: value */ static void mei_txe_input_payload_write(struct mei_device *dev, unsigned long idx, u32 value) { … } /** * mei_txe_out_data_read - read dword from the device buffer * at offset idx * * @dev: the device structure * @idx: index in the device buffer * * Return: register value at index */ static u32 mei_txe_out_data_read(const struct mei_device *dev, unsigned long idx) { … } /* Readiness */ /** * mei_txe_readiness_set_host_rdy - set host readiness bit * * @dev: the device structure */ static void mei_txe_readiness_set_host_rdy(struct mei_device *dev) { … } /** * mei_txe_readiness_clear - clear host readiness bit * * @dev: the device structure */ static void mei_txe_readiness_clear(struct mei_device *dev) { … } /** * mei_txe_readiness_get - Reads and returns * the HICR_SEC_IPC_READINESS register value * * @dev: the device structure * * Return: the HICR_SEC_IPC_READINESS register value */ static u32 mei_txe_readiness_get(struct mei_device *dev) { … } /** * mei_txe_readiness_is_sec_rdy - check readiness * for HICR_SEC_IPC_READINESS_SEC_RDY * * @readiness: cached readiness state * * Return: true if readiness bit is set */ static inline bool mei_txe_readiness_is_sec_rdy(u32 readiness) { … } /** * mei_txe_hw_is_ready - check if the hw is ready * * @dev: the device structure * * Return: true if sec is ready */ static bool mei_txe_hw_is_ready(struct mei_device *dev) { … } /** * 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) { … } /** * mei_txe_readiness_wait - wait till readiness settles * * @dev: the device structure * * Return: 0 on success and -ETIME on timeout */ static int mei_txe_readiness_wait(struct mei_device *dev) { … } static const struct mei_fw_status mei_txe_fw_sts = …; /** * mei_txe_fw_status - read fw status register from pci config space * * @dev: mei device * @fw_status: fw status register values * * Return: 0 on success, error otherwise */ static int mei_txe_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status) { … } /** * 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 * * Return: always 0 */ static int mei_txe_hw_config(struct mei_device *dev) { … } /** * mei_txe_write - writes a message to device. * * @dev: the device structure * @hdr: header of message * @hdr_len: header length in bytes - must multiplication of a slot (4bytes) * @data: payload * @data_len: paylead length in bytes * * Return: 0 if success, < 0 - otherwise. */ static int mei_txe_write(struct mei_device *dev, const void *hdr, size_t hdr_len, const void *data, size_t data_len) { … } /** * mei_txe_hbuf_depth - mimics the me hbuf circular buffer * * @dev: the device structure * * Return: the TXE_HBUF_DEPTH */ static u32 mei_txe_hbuf_depth(const struct mei_device *dev) { … } /** * mei_txe_hbuf_empty_slots - mimics the me hbuf circular buffer * * @dev: the device structure * * Return: always TXE_HBUF_DEPTH */ static int mei_txe_hbuf_empty_slots(struct mei_device *dev) { … } /** * mei_txe_count_full_read_slots - mimics the me device circular buffer * * @dev: the device structure * * Return: always buffer size in dwords count */ static int mei_txe_count_full_read_slots(struct mei_device *dev) { … } /** * mei_txe_read_hdr - read message header which is always in 4 first bytes * * @dev: the device structure * * Return: mei message header */ static u32 mei_txe_read_hdr(const struct mei_device *dev) { … } /** * mei_txe_read - reads a message from the txe device. * * @dev: the device structure * @buf: message buffer will be written * @len: message size will be read * * Return: -EINVAL on error wrong argument and 0 on success */ static int mei_txe_read(struct mei_device *dev, unsigned char *buf, unsigned long len) { … } /** * mei_txe_hw_reset - resets host and fw. * * @dev: the device structure * @intr_enable: if interrupt should be enabled after reset. * * Return: 0 on success and < 0 in case of error */ static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable) { … } /** * 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) { … } /** * 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) { … } /** * mei_txe_irq_quick_handler - The ISR of the MEI device * * @irq: The irq number * @dev_id: pointer to the device structure * * Return: IRQ_WAKE_THREAD if interrupt is designed for the device * IRQ_NONE otherwise */ irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id) { … } /** * mei_txe_irq_thread_handler - txe interrupt thread * * @irq: The irq number * @dev_id: pointer to the device structure * * Return: IRQ_HANDLED */ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) { … } static const struct mei_hw_ops mei_txe_hw_ops = …; /** * mei_txe_dev_init - allocates and initializes txe hardware specific structure * * @pdev: pci device * * Return: struct mei_device * on success or NULL */ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev) { … } /** * mei_txe_setup_satt2 - SATT2 configuration for DMA support. * * @dev: the device structure * @addr: physical address start of the range * @range: physical range size * * Return: 0 on success an error code otherwise */ int mei_txe_setup_satt2(struct mei_device *dev, phys_addr_t addr, u32 range) { … }