// SPDX-License-Identifier: GPL-2.0-only /* * H/W layer of ISHTP provider device (ISH) * * Copyright (c) 2014-2016, Intel Corporation. */ #include <linux/devm-helpers.h> #include <linux/sched.h> #include <linux/spinlock.h> #include <linux/delay.h> #include <linux/jiffies.h> #include "client.h" #include "hw-ish.h" #include "hbm.h" /* For FW reset flow */ static struct work_struct fw_reset_work; static struct ishtp_device *ishtp_dev; /** * ish_reg_read() - Read register * @dev: ISHTP device pointer * @offset: Register offset * * Read 32 bit register at a given offset * * Return: Read register value */ static inline uint32_t ish_reg_read(const struct ishtp_device *dev, unsigned long offset) { … } /** * ish_reg_write() - Write register * @dev: ISHTP device pointer * @offset: Register offset * @value: Value to write * * Writes 32 bit register at a give offset */ static inline void ish_reg_write(struct ishtp_device *dev, unsigned long offset, uint32_t value) { … } /** * _ish_read_fw_sts_reg() - Read FW status register * @dev: ISHTP device pointer * * Read FW status register * * Return: Read register value */ static inline uint32_t _ish_read_fw_sts_reg(struct ishtp_device *dev) { … } /** * check_generated_interrupt() - Check if ISH interrupt * @dev: ISHTP device pointer * * Check if an interrupt was generated for ISH * * Return: Read true or false */ static bool check_generated_interrupt(struct ishtp_device *dev) { … } /** * ish_is_input_ready() - Check if FW ready for RX * @dev: ISHTP device pointer * * Check if ISH FW is ready for receiving data * * Return: Read true or false */ static bool ish_is_input_ready(struct ishtp_device *dev) { … } /** * set_host_ready() - Indicate host ready * @dev: ISHTP device pointer * * Set host ready indication to FW */ static void set_host_ready(struct ishtp_device *dev) { … } /** * ishtp_fw_is_ready() - Check if FW ready * @dev: ISHTP device pointer * * Check if ISH FW is ready * * Return: Read true or false */ static bool ishtp_fw_is_ready(struct ishtp_device *dev) { … } /** * ish_set_host_rdy() - Indicate host ready * @dev: ISHTP device pointer * * Set host ready indication to FW */ static void ish_set_host_rdy(struct ishtp_device *dev) { … } /** * ish_clr_host_rdy() - Indicate host not ready * @dev: ISHTP device pointer * * Send host not ready indication to FW */ static void ish_clr_host_rdy(struct ishtp_device *dev) { … } static bool ish_chk_host_rdy(struct ishtp_device *dev) { … } /** * ish_set_host_ready() - reconfig ipc host registers * @dev: ishtp device pointer * * Set host to ready state * This API is called in some case: * fw is still on, but ipc is powered down. * such as OOB case. * * Return: 0 for success else error fault code */ void ish_set_host_ready(struct ishtp_device *dev) { … } /** * _ishtp_read_hdr() - Read message header * @dev: ISHTP device pointer * * Read header of 32bit length * * Return: Read register value */ static uint32_t _ishtp_read_hdr(const struct ishtp_device *dev) { … } /** * _ishtp_read - Read message * @dev: ISHTP device pointer * @buffer: message buffer * @buffer_length: length of message buffer * * Read message from FW * * Return: Always 0 */ static int _ishtp_read(struct ishtp_device *dev, unsigned char *buffer, unsigned long buffer_length) { … } /** * write_ipc_from_queue() - try to write ipc msg from Tx queue to device * @dev: ishtp device pointer * * Check if DRBL is cleared. if it is - write the first IPC msg, then call * the callback function (unless it's NULL) * * Return: 0 for success else failure code */ static int write_ipc_from_queue(struct ishtp_device *dev) { … } /** * write_ipc_to_queue() - write ipc msg to Tx queue * @dev: ishtp device instance * @ipc_send_compl: Send complete callback * @ipc_send_compl_prm: Parameter to send in complete callback * @msg: Pointer to message * @length: Length of message * * Recived msg with IPC (and upper protocol) header and add it to the device * Tx-to-write list then try to send the first IPC waiting msg * (if DRBL is cleared) * This function returns negative value for failure (means free list * is empty, or msg too long) and 0 for success. * * Return: 0 for success else failure code */ static int write_ipc_to_queue(struct ishtp_device *dev, void (*ipc_send_compl)(void *), void *ipc_send_compl_prm, unsigned char *msg, int length) { … } /** * ipc_send_mng_msg() - Send management message * @dev: ishtp device instance * @msg_code: Message code * @msg: Pointer to message * @size: Length of message * * Send management message to FW * * Return: 0 for success else failure code */ static int ipc_send_mng_msg(struct ishtp_device *dev, uint32_t msg_code, void *msg, size_t size) { … } #define WAIT_FOR_FW_RDY … #define WAIT_FOR_INPUT_RDY … /** * timed_wait_for_timeout() - wait special event with timeout * @dev: ISHTP device pointer * @condition: indicate the condition for waiting * @timeinc: time slice for every wait cycle, in ms * @timeout: time in ms for timeout * * This function will check special event to be ready in a loop, the loop * period is specificd in timeinc. Wait timeout will causes failure. * * Return: 0 for success else failure code */ static int timed_wait_for_timeout(struct ishtp_device *dev, int condition, unsigned int timeinc, unsigned int timeout) { … } #define TIME_SLICE_FOR_FW_RDY_MS … #define TIME_SLICE_FOR_INPUT_RDY_MS … #define TIMEOUT_FOR_FW_RDY_MS … #define TIMEOUT_FOR_INPUT_RDY_MS … /** * ish_fw_reset_handler() - FW reset handler * @dev: ishtp device pointer * * Handle FW reset * * Return: 0 for success else failure code */ static int ish_fw_reset_handler(struct ishtp_device *dev) { … } #define TIMEOUT_FOR_HW_RDY_MS … /** * fw_reset_work_fn() - FW reset worker function * @work: Work item * * Call ish_fw_reset_handler to complete FW reset */ static void fw_reset_work_fn(struct work_struct *work) { … } /** * _ish_sync_fw_clock() -Sync FW clock with the OS clock * @dev: ishtp device pointer * * Sync FW and OS time */ static void _ish_sync_fw_clock(struct ishtp_device *dev) { … } /** * recv_ipc() - Receive and process IPC management messages * @dev: ishtp device instance * @doorbell_val: doorbell value * * This function runs in ISR context. * NOTE: Any other mng command than reset_notify and reset_notify_ack * won't wake BH handler */ static void recv_ipc(struct ishtp_device *dev, uint32_t doorbell_val) { … } /** * ish_irq_handler() - ISH IRQ handler * @irq: irq number * @dev_id: ishtp device pointer * * ISH IRQ handler. If interrupt is generated and is for ISH it will process * the interrupt. */ irqreturn_t ish_irq_handler(int irq, void *dev_id) { … } /** * ish_disable_dma() - disable dma communication between host and ISHFW * @dev: ishtp device pointer * * Clear the dma enable bit and wait for dma inactive. * * Return: 0 for success else error code. */ int ish_disable_dma(struct ishtp_device *dev) { … } /** * ish_wakeup() - wakeup ishfw from waiting-for-host state * @dev: ishtp device pointer * * Set the dma enable bit and send a void message to FW, * it wil wakeup FW from waiting-for-host state. */ static void ish_wakeup(struct ishtp_device *dev) { … } /** * _ish_hw_reset() - HW reset * @dev: ishtp device pointer * * Reset ISH HW to recover if any error * * Return: 0 for success else error fault code */ static int _ish_hw_reset(struct ishtp_device *dev) { … } /** * _ish_ipc_reset() - IPC reset * @dev: ishtp device pointer * * Resets host and fw IPC and upper layers * * Return: 0 for success else error fault code */ static int _ish_ipc_reset(struct ishtp_device *dev) { … } /** * ish_hw_start() -Start ISH HW * @dev: ishtp device pointer * * Set host to ready state and wait for FW reset * * Return: 0 for success else error fault code */ int ish_hw_start(struct ishtp_device *dev) { … } /** * ish_ipc_get_header() -Get doorbell value * @dev: ishtp device pointer * @length: length of message * @busy: busy status * * Get door bell value from message header * * Return: door bell value */ static uint32_t ish_ipc_get_header(struct ishtp_device *dev, int length, int busy) { … } /** * _dma_no_cache_snooping() * * Check on current platform, DMA supports cache snooping or not. * This callback is used to notify uplayer driver if manully cache * flush is needed when do DMA operation. * * Please pay attention to this callback implementation, if declare * having cache snooping on a cache snooping not supported platform * will cause uplayer driver receiving mismatched data; and if * declare no cache snooping on a cache snooping supported platform * will cause cache be flushed twice and performance hit. * * @dev: ishtp device pointer * * Return: false - has cache snooping capability * true - no cache snooping, need manually cache flush */ static bool _dma_no_cache_snooping(struct ishtp_device *dev) { … } static const struct ishtp_hw_ops ish_hw_ops = …; /** * ish_dev_init() -Initialize ISH devoce * @pdev: PCI device * * Allocate ISHTP device and initialize IPC processing * * Return: ISHTP device instance on success else NULL */ struct ishtp_device *ish_dev_init(struct pci_dev *pdev) { … } /** * ish_device_disable() - Disable ISH device * @dev: ISHTP device pointer * * Disable ISH by clearing host ready to inform firmware. */ void ish_device_disable(struct ishtp_device *dev) { … }