// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2003-2019, Intel Corporation. All rights reserved.
* Intel Management Engine Interface (Intel MEI) Linux driver
*/
#include <linux/pci.h>
#include <linux/kthread.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <linux/sizes.h>
#include "mei_dev.h"
#include "hbm.h"
#include "hw-me.h"
#include "hw-me-regs.h"
#include "mei-trace.h"
/**
* mei_me_reg_read - Reads 32bit data from the mei device
*
* @hw: the me hardware structure
* @offset: offset from which to read the data
*
* Return: register value (u32)
*/
static inline u32 mei_me_reg_read(const struct mei_me_hw *hw,
unsigned long offset)
{
return ioread32(hw->mem_addr + offset);
}
/**
* mei_me_reg_write - Writes 32bit data to the mei device
*
* @hw: the me hardware structure
* @offset: offset from which to write the data
* @value: register value to write (u32)
*/
static inline void mei_me_reg_write(const struct mei_me_hw *hw,
unsigned long offset, u32 value)
{
iowrite32(value, hw->mem_addr + offset);
}
/**
* mei_me_mecbrw_read - Reads 32bit data from ME circular buffer
* read window register
*
* @dev: the device structure
*
* Return: ME_CB_RW register value (u32)
*/
static inline u32 mei_me_mecbrw_read(const struct mei_device *dev)
{
return mei_me_reg_read(to_me_hw(dev), ME_CB_RW);
}
/**
* mei_me_hcbww_write - write 32bit data to the host circular buffer
*
* @dev: the device structure
* @data: 32bit data to be written to the host circular buffer
*/
static inline void mei_me_hcbww_write(struct mei_device *dev, u32 data)
{
mei_me_reg_write(to_me_hw(dev), H_CB_WW, data);
}
/**
* mei_me_mecsr_read - Reads 32bit data from the ME CSR
*
* @dev: the device structure
*
* Return: ME_CSR_HA register value (u32)
*/
static inline u32 mei_me_mecsr_read(const struct mei_device *dev)
{
u32 reg;
reg = mei_me_reg_read(to_me_hw(dev), ME_CSR_HA);
trace_mei_reg_read(dev->dev, "ME_CSR_HA", ME_CSR_HA, reg);
return reg;
}
/**
* mei_hcsr_read - Reads 32bit data from the host CSR
*
* @dev: the device structure
*
* Return: H_CSR register value (u32)
*/
static inline u32 mei_hcsr_read(const struct mei_device *dev)
{
u32 reg;
reg = mei_me_reg_read(to_me_hw(dev), H_CSR);
trace_mei_reg_read(dev->dev, "H_CSR", H_CSR, reg);
return reg;
}
/**
* mei_hcsr_write - writes H_CSR register to the mei device
*
* @dev: the device structure
* @reg: new register value
*/
static inline void mei_hcsr_write(struct mei_device *dev, u32 reg)
{
trace_mei_reg_write(dev->dev, "H_CSR", H_CSR, reg);
mei_me_reg_write(to_me_hw(dev), H_CSR, reg);
}
/**
* mei_hcsr_set - writes H_CSR register to the mei device,
* and ignores the H_IS bit for it is write-one-to-zero.
*
* @dev: the device structure
* @reg: new register value
*/
static inline void mei_hcsr_set(struct mei_device *dev, u32 reg)
{
reg &= ~H_CSR_IS_MASK;
mei_hcsr_write(dev, reg);
}
/**
* mei_hcsr_set_hig - set host interrupt (set H_IG)
*
* @dev: the device structure
*/
static inline void mei_hcsr_set_hig(struct mei_device *dev)
{
u32 hcsr;
hcsr = mei_hcsr_read(dev) | H_IG;
mei_hcsr_set(dev, hcsr);
}
/**
* mei_me_d0i3c_read - Reads 32bit data from the D0I3C register
*
* @dev: the device structure
*
* Return: H_D0I3C register value (u32)
*/
static inline u32 mei_me_d0i3c_read(const struct mei_device *dev)
{
u32 reg;
reg = mei_me_reg_read(to_me_hw(dev), H_D0I3C);
trace_mei_reg_read(dev->dev, "H_D0I3C", H_D0I3C, reg);
return reg;
}
/**
* mei_me_d0i3c_write - writes H_D0I3C register to device
*
* @dev: the device structure
* @reg: new register value
*/
static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg)
{
trace_mei_reg_write(dev->dev, "H_D0I3C", H_D0I3C, reg);
mei_me_reg_write(to_me_hw(dev), H_D0I3C, reg);
}
/**
* mei_me_trc_status - read trc status register
*
* @dev: mei device
* @trc: trc status register value
*
* Return: 0 on success, error otherwise
*/
static int mei_me_trc_status(struct mei_device *dev, u32 *trc)
{
struct mei_me_hw *hw = to_me_hw(dev);
if (!hw->cfg->hw_trc_supported)
return -EOPNOTSUPP;
*trc = mei_me_reg_read(hw, ME_TRC);
trace_mei_reg_read(dev->dev, "ME_TRC", ME_TRC, *trc);
return 0;
}
/**
* mei_me_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_me_fw_status(struct mei_device *dev,
struct mei_fw_status *fw_status)
{
struct mei_me_hw *hw = to_me_hw(dev);
const struct mei_fw_status *fw_src = &hw->cfg->fw_status;
int ret;
int i;
if (!fw_status || !hw->read_fws)
return -EINVAL;
fw_status->count = fw_src->count;
for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) {
ret = hw->read_fws(dev, fw_src->status[i],
&fw_status->status[i]);
trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_X",
fw_src->status[i],
fw_status->status[i]);
if (ret)
return ret;
}
return 0;
}
/**
* mei_me_hw_config - configure hw dependent settings
*
* @dev: mei device
*
* Return:
* * -EINVAL when read_fws is not set
* * 0 on success
*
*/
static int mei_me_hw_config(struct mei_device *dev)
{
struct mei_me_hw *hw = to_me_hw(dev);
u32 hcsr, reg;
if (WARN_ON(!hw->read_fws))
return -EINVAL;
/* Doesn't change in runtime */
hcsr = mei_hcsr_read(dev);
hw->hbuf_depth = (hcsr & H_CBD) >> 24;
reg = 0;
hw->read_fws(dev, PCI_CFG_HFS_1, ®);
trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg);
hw->d0i3_supported =
((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK);
hw->pg_state = MEI_PG_OFF;
if (hw->d0i3_supported) {
reg = mei_me_d0i3c_read(dev);
if (reg & H_D0I3C_I3)
hw->pg_state = MEI_PG_ON;
}
return 0;
}
/**
* mei_me_pg_state - translate internal pg state
* to the mei power gating state
*
* @dev: mei device
*
* Return: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise
*/
static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev)
{
struct mei_me_hw *hw = to_me_hw(dev);
return hw->pg_state;
}
static inline u32 me_intr_src(u32 hcsr)
{
return hcsr & H_CSR_IS_MASK;
}
/**
* me_intr_disable - disables mei device interrupts
* using supplied hcsr register value.
*
* @dev: the device structure
* @hcsr: supplied hcsr register value
*/
static inline void me_intr_disable(struct mei_device *dev, u32 hcsr)
{
hcsr &= ~H_CSR_IE_MASK;
mei_hcsr_set(dev, hcsr);
}
/**
* me_intr_clear - clear and stop interrupts
*
* @dev: the device structure
* @hcsr: supplied hcsr register value
*/
static inline void me_intr_clear(struct mei_device *dev, u32 hcsr)
{
if (me_intr_src(hcsr))
mei_hcsr_write(dev, hcsr);
}
/**
* mei_me_intr_clear - clear and stop interrupts
*
* @dev: the device structure
*/
static void mei_me_intr_clear(struct mei_device *dev)
{
u32 hcsr = mei_hcsr_read(dev);
me_intr_clear(dev, hcsr);
}
/**
* mei_me_intr_enable - enables mei device interrupts
*
* @dev: the device structure
*/
static void mei_me_intr_enable(struct mei_device *dev)
{
u32 h
|