// 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)
{
struct ish_hw *hw = to_ish_hw(dev);
return readl(hw->mem_addr + 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)
{
struct ish_hw *hw = to_ish_hw(dev);
writel(value, hw->mem_addr + offset);
}
/**
* _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)
{
return ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
}
/**
* 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)
{
bool interrupt_generated = true;
uint32_t pisr_val = 0;
if (dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV) {
pisr_val = ish_reg_read(dev, IPC_REG_PISR_CHV_AB);
interrupt_generated =
IPC_INT_FROM_ISH_TO_HOST_CHV_AB(pisr_val);
} else {
pisr_val = ish_reg_read(dev, IPC_REG_PISR_BXT);
interrupt_generated = !!pisr_val;
/* only busy-clear bit is RW, others are RO */
if (pisr_val)
ish_reg_write(dev, IPC_REG_PISR_BXT, pisr_val);
}
return interrupt_generated;
}
/**
* 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)
{
uint32_t doorbell_val;
doorbell_val = ish_reg_read(dev, IPC_REG_HOST2ISH_DRBL);
return !IPC_IS_BUSY(doorbell_val);
}
/**
* 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)
{
if (dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV) {
if (dev->pdev->revision == REVISION_ID_CHT_A0 ||
(dev->pdev->revision & REVISION_ID_SI_MASK) ==
REVISION_ID_CHT_Ax_SI)
ish_reg_write(dev, IPC_REG_HOST_COMM, 0x81);
else if (dev->pdev->revision == REVISION_ID_CHT_B0 ||
(dev->pdev->revision & REVISION_ID_SI_MASK) ==
REVISION_ID_CHT_Bx_SI ||
(dev->pdev->revision & REVISION_ID_SI_MASK) ==
REVISION_ID_CHT_Kx_SI ||
(dev->pdev->revision & REVISION_ID_SI_MASK) ==
REVISION_ID_CHT_Dx_SI) {
uint32_t host_comm_val;
host_comm_val = ish_reg_read(dev, IPC_REG_HOST_COMM);
host_comm_val |= IPC_HOSTCOMM_INT_EN_BIT_CHV_AB | 0x81;
ish_reg_write(dev, IPC_REG_HOST_COMM, host_comm_val);
}
} else {
uint32_t host_pimr_val;
host_pimr_val = ish_reg_read(dev, IPC_REG_PIMR_BXT);
host_pimr_val |= IPC_PIMR_INT_EN_BIT_BXT;
/*
* disable interrupt generated instead of
* RX_complete_msg
*/
host_pimr_val &= ~IPC_HOST2ISH_BUSYCLEAR_MASK_BIT;
ish_reg_write(dev, IPC_REG_PIMR_BXT, host_pimr_val);
}
}
/**
* 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)
{
uint32_t ish_status = _ish_read_fw_sts_reg(dev);
return IPC_IS_ISH_ILUP(ish_status) &&
IPC_IS_ISH_ISHTP_READY(ish_status);
}
/**
* 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)
{
uint32_t