Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Tomas Winkler | 3653 | 96.97% | 23 | 62.16% |
Alexander Usyskin | 110 | 2.92% | 11 | 29.73% |
Stephen Rothwell | 2 | 0.05% | 1 | 2.70% |
Dan Carpenter | 1 | 0.03% | 1 | 2.70% |
Fengguang Wu | 1 | 0.03% | 1 | 2.70% |
Total | 3767 | 37 |
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2013-2014, 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 (PAYLOAD_SIZE / MEI_SLOT_SIZE) /** * 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) { return ioread32(base_addr + 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) { iowrite32(value, base_addr + offset); } /** * 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) { return mei_txe_reg_read(hw->mem_addr[SEC_BAR], 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) { WARN(!hw->aliveness, "sec read: aliveness not asserted\n"); return mei_txe_sec_reg_read_silent(hw, 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_reg_write(hw->mem_addr[SEC_BAR], offset, 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) { WARN(!hw->aliveness, "sec write: aliveness not asserted\n"); mei_txe_sec_reg_write_silent(hw, offset, 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) { return mei_txe_reg_read(hw->mem_addr[BRIDGE_BAR], 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_reg_write(hw->mem_addr[BRIDGE_BAR], offset, 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) { struct mei_txe_hw *hw = to_txe_hw(dev); bool do_req = hw->aliveness != req; dev_dbg(dev->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_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 * * Return: SICR_HOST_ALIVENESS_REQ_REQUESTED bit 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_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) { 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_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; } /** * 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) { 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->dev, "aliveness timed out = %ld aliveness = %d event = %d\n", err, hw->aliveness, dev->pg_event); else dev_dbg(dev->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; } /** * 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) { if (mei_txe_aliveness_set(dev, req)) return mei_txe_aliveness_wait(dev, req); return 0; } /** * 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) { return dev->pg_event == MEI_PG_EVENT_WAIT; } /** * 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) { return true; } /** * 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) { struct mei_txe_hw *hw = to_txe_hw(dev); return hw->aliveness ? MEI_PG_OFF : MEI_PG_ON; } /** * 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) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 hintmsk; /* Enable the SEC_IPC_HOST_INT_MASK_IN_RDY interrupt */ hintmsk = mei_txe_sec_reg_read(hw, SEC_IPC_HOST_INT_MASK_REG); hintmsk |= SEC_IPC_HOST_INT_MASK_IN_RDY; mei_txe_sec_reg_write(hw, SEC_IPC_HOST_INT_MASK_REG, hintmsk); } /** * 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) { /* Clear the interrupt cause */ clear_bit(TXE_INTR_IN_READY_BIT, &hw->intr_cause); mei_txe_sec_reg_write(hw, SEC_IPC_INPUT_DOORBELL_REG, 1); } /** * 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_br_reg_write(hw, SICR_SEC_IPC_OUTPUT_STATUS_REG, SEC_IPC_OUTPUT_STATUS_RDY); } /** * 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) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 status; status = mei_txe_sec_reg_read(hw, SEC_IPC_INPUT_STATUS_REG); return !!(SEC_IPC_INPUT_STATUS_RDY & status); } /** * 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_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_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_synchronize_irq - wait for pending IRQ handlers * * @dev: the device structure */ static void mei_txe_synchronize_irq(struct mei_device *dev) { struct pci_dev *pdev = to_pci_dev(dev->dev); synchronize_irq(pdev->irq); } /** * 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) { struct mei_txe_hw *hw = to_txe_hw(dev); bool ret = (hw->intr_cause & (TXE_INTR_READINESS | TXE_INTR_ALIVENESS | TXE_INTR_IN_READY | TXE_INTR_OUT_DB)); if (ret) { dev_dbg(dev->dev, "Pending Interrupts InReady=%01d Readiness=%01d, Aliveness=%01d, OutDoor=%01d\n", !!(hw->intr_cause & TXE_INTR_IN_READY), !!(hw->intr_cause & TXE_INTR_READINESS), !!(hw->intr_cause & TXE_INTR_ALIVENESS), !!(hw->intr_cause & TXE_INTR_OUT_DB)); } return ret; } /** * 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) { struct mei_txe_hw *hw = to_txe_hw(dev); mei_txe_sec_reg_write(hw, SEC_IPC_INPUT_PAYLOAD_REG + (idx * sizeof(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) { struct mei_txe_hw *hw = to_txe_hw(dev); return mei_txe_br_reg_read(hw, BRIDGE_IPC_OUTPUT_PAYLOAD_REG + (idx * sizeof(u32))); } /* 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) { 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_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) { struct mei_txe_hw *hw = to_txe_hw(dev); return mei_txe_br_reg_read(hw, HICR_SEC_IPC_READINESS_REG); } /** * 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) { return !!(readiness & HICR_SEC_IPC_READINESS_SEC_RDY); } /** * 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) { u32 readiness = mei_txe_readiness_get(dev); return mei_txe_readiness_is_sec_rdy(readiness); } /** * 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_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) { if (mei_txe_hw_is_ready(dev)) return 0; mutex_unlock(&dev->device_lock); wait_event_timeout(dev->wait_hw_ready, dev->recvd_hw_ready, msecs_to_jiffies(SEC_RESET_WAIT_TIMEOUT)); mutex_lock(&dev->device_lock); if (!dev->recvd_hw_ready) { dev_err(dev->dev, "wait for readiness failed\n"); return -ETIME; } dev->recvd_hw_ready = false; return 0; } static const struct mei_fw_status mei_txe_fw_sts = { .count = 2, .status[0] = PCI_CFG_TXE_FW_STS0, .status[1] = PCI_CFG_TXE_FW_STS1 }; /** * 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) { const struct mei_fw_status *fw_src = &mei_txe_fw_sts; struct pci_dev *pdev = to_pci_dev(dev->dev); int ret; int i; if (!fw_status) return -EINVAL; fw_status->count = fw_src->count; for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { ret = pci_read_config_dword(pdev, fw_src->status[i], &fw_status->status[i]); trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HSF_X", fw_src->status[i], fw_status->status[i]); if (ret) return ret; } return 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); hw->aliveness = mei_txe_aliveness_get(dev); hw->readiness = mei_txe_readiness_get(dev); dev_dbg(dev->dev, "aliveness_resp = 0x%08x, readiness = 0x%08x.\n", hw->aliveness, hw->readiness); } /** * 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) { struct mei_txe_hw *hw = to_txe_hw(dev); unsigned long rem; const u32 *reg_buf; u32 slots = TXE_HBUF_DEPTH; u32 dw_cnt; unsigned long i, j; if (WARN_ON(!hdr || !data || hdr_len & 0x3)) return -EINVAL; dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM((struct mei_msg_hdr *)hdr)); dw_cnt = mei_data2slots(hdr_len + data_len); if (dw_cnt > slots) return -EMSGSIZE; if (WARN(!hw->aliveness, "txe write: aliveness not asserted\n")) return -EAGAIN; /* Enable Input Ready Interrupt. */ mei_txe_input_ready_interrupt_enable(dev); if (!mei_txe_is_input_ready(dev)) { char fw_sts_str[MEI_FW_STATUS_STR_SZ]; mei_fw_status_str(dev, fw_sts_str, MEI_FW_STATUS_STR_SZ); dev_err(dev->dev, "Input is not ready %s\n", fw_sts_str); return -EAGAIN; } reg_buf = hdr; for (i = 0; i < hdr_len / MEI_SLOT_SIZE; i++) mei_txe_input_payload_write(dev, i, reg_buf[i]); reg_buf = data; for (j = 0; j < data_len / MEI_SLOT_SIZE; j++) mei_txe_input_payload_write(dev, i + j, reg_buf[j]); rem = data_len & 0x3; if (rem > 0) { u32 reg = 0; memcpy(®, (const u8 *)data + data_len - rem, rem); mei_txe_input_payload_write(dev, i + j, reg); } /* after each write the whole buffer is consumed */ hw->slots = 0; /* Set Input-Doorbell */ mei_txe_input_doorbell_set(hw); return 0; } /** * 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) { return TXE_HBUF_DEPTH; } /** * 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) { struct mei_txe_hw *hw = to_txe_hw(dev); return hw->slots; } /** * 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) { /* read buffers has static size */ return TXE_HBUF_DEPTH; } /** * 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) { return mei_txe_out_data_read(dev, 0); } /** * 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) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 *reg_buf, reg; u32 rem; u32 i; if (WARN_ON(!buf || !len)) return -EINVAL; reg_buf = (u32 *)buf; rem = len & 0x3; dev_dbg(dev->dev, "buffer-length = %lu buf[0]0x%08X\n", len, mei_txe_out_data_read(dev, 0)); for (i = 0; i < len / MEI_SLOT_SIZE; i++) { /* skip header: index starts from 1 */ reg = mei_txe_out_data_read(dev, i + 1); dev_dbg(dev->dev, "buf[%d] = 0x%08X\n", i, reg); *reg_buf++ = reg; } if (rem) { reg = mei_txe_out_data_read(dev, i + 1); memcpy(reg_buf, ®, rem); } mei_txe_output_ready_set(hw); return 0; } /** * 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) { 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->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->dev, "wait for aliveness failed ... bailing out\n"); return -EIO; } } /* * Set readiness RDY_CLR bit */ mei_txe_readiness_clear(dev); return 0; } /** * 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_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_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) { struct mei_device *dev = dev_id; if (mei_txe_check_and_ack_intrs(dev, true)) return IRQ_WAKE_THREAD; return IRQ_NONE; } /** * 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) { struct mei_device *dev = (struct mei_device *) dev_id; struct mei_txe_hw *hw = to_txe_hw(dev); struct list_head cmpl_list; s32 slots; int rets = 0; dev_dbg(dev->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); INIT_LIST_HEAD(&cmpl_list); if (pci_dev_msi_enabled(to_pci_dev(dev->dev))) 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->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->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->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->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, &cmpl_list, &slots); if (rets && (dev->dev_state != MEI_DEV_RESETTING && dev->dev_state != MEI_DEV_POWER_DOWN)) { dev_err(dev->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 = TXE_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, &cmpl_list); if (rets && rets != -EMSGSIZE) dev_err(dev->dev, "mei_irq_write_handler ret = %d.\n", rets); dev->hbuf_is_ready = mei_hbuf_is_ready(dev); } mei_irq_compl_handler(dev, &cmpl_list); end: dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); mutex_unlock(&dev->device_lock); mei_enable_interrupts(dev); return IRQ_HANDLED; } static const struct mei_hw_ops mei_txe_hw_ops = { .host_is_ready = mei_txe_host_is_ready, .fw_status = mei_txe_fw_status, .pg_state = mei_txe_pg_state, .hw_is_ready = mei_txe_hw_is_ready, .hw_reset = mei_txe_hw_reset, .hw_config = mei_txe_hw_config, .hw_start = mei_txe_hw_start, .pg_in_transition = mei_txe_pg_in_transition, .pg_is_enabled = mei_txe_pg_is_enabled, .intr_clear = mei_txe_intr_clear, .intr_enable = mei_txe_intr_enable, .intr_disable = mei_txe_intr_disable, .synchronize_irq = mei_txe_synchronize_irq, .hbuf_free_slots = mei_txe_hbuf_empty_slots, .hbuf_is_ready = mei_txe_is_input_ready, .hbuf_depth = mei_txe_hbuf_depth, .write = mei_txe_write, .rdbuf_full_slots = mei_txe_count_full_read_slots, .read_hdr = mei_txe_read_hdr, .read = mei_txe_read, }; /** * 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) { struct mei_device *dev; struct mei_txe_hw *hw; dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) + sizeof(struct mei_txe_hw), GFP_KERNEL); if (!dev) return NULL; mei_device_init(dev, &pdev->dev, &mei_txe_hw_ops); hw = to_txe_hw(dev); init_waitqueue_head(&hw->wait_aliveness_resp); return dev; } /** * 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) { 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->dev, "SATT2: SAP_SIZE_OFFSET=0x%08X, BRG_BA_LSB_OFFSET=0x%08X, CTRL_OFFSET=0x%08X\n", range, lo32, ctrl); return 0; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1