#include <linux/errno.h>
#include <linux/bitfield.h>
#include <linux/iopoll.h>
#include "rnpgbe_mbx.h"
static u32 mbx_data_rd32(struct mucse_mbx_info *mbx, u32 reg)
{
struct mucse_hw *hw = container_of(mbx, struct mucse_hw, mbx);
return readl(hw->hw_addr + mbx->fwpf_shm_base + reg);
}
static void mbx_data_wr32(struct mucse_mbx_info *mbx, u32 reg, u32 value)
{
struct mucse_hw *hw = container_of(mbx, struct mucse_hw, mbx);
writel(value, hw->hw_addr + mbx->fwpf_shm_base + reg);
}
static u32 mbx_ctrl_rd32(struct mucse_mbx_info *mbx, u32 reg)
{
struct mucse_hw *hw = container_of(mbx, struct mucse_hw, mbx);
return readl(hw->hw_addr + mbx->fwpf_ctrl_base + reg);
}
static void mbx_ctrl_wr32(struct mucse_mbx_info *mbx, u32 reg, u32 value)
{
struct mucse_hw *hw = container_of(mbx, struct mucse_hw, mbx);
writel(value, hw->hw_addr + mbx->fwpf_ctrl_base + reg);
}
static u32 mucse_mbx_get_lock_pf(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
u32 reg = MUCSE_MBX_PF2FW_CTRL(mbx);
mbx_ctrl_wr32(mbx, reg, MUCSE_MBX_PFU);
return mbx_ctrl_rd32(mbx, reg);
}
static int mucse_obtain_mbx_lock_pf(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
u32 val;
return read_poll_timeout_atomic(mucse_mbx_get_lock_pf,
val, val & MUCSE_MBX_PFU,
mbx->delay_us,
mbx->timeout_us,
false, hw);
}
static void mucse_release_mbx_lock_pf(struct mucse_hw *hw, bool req)
{
struct mucse_mbx_info *mbx = &hw->mbx;
u32 reg = MUCSE_MBX_PF2FW_CTRL(mbx);
mbx_ctrl_wr32(mbx, reg, req ? MUCSE_MBX_REQ : 0);
}
static u16 mucse_mbx_get_fwreq(struct mucse_mbx_info *mbx)
{
u32 val = mbx_data_rd32(mbx, MUCSE_MBX_FW2PF_CNT);
return FIELD_GET(GENMASK_U32(15, 0), val);
}
static void mucse_mbx_inc_pf_ack(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
u16 ack;
u32 val;
val = mbx_data_rd32(mbx, MUCSE_MBX_PF2FW_CNT);
ack = FIELD_GET(GENMASK_U32(31, 16), val);
ack++;
val &= ~GENMASK_U32(31, 16);
val |= FIELD_PREP(GENMASK_U32(31, 16), ack);
mbx_data_wr32(mbx, MUCSE_MBX_PF2FW_CNT, val);
}
static int mucse_read_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size)
{
const int size_in_words = size / sizeof(u32);
struct mucse_mbx_info *mbx = &hw->mbx;
int err;
err = mucse_obtain_mbx_lock_pf(hw);
if (err)
return err;
for (int i = 0; i < size_in_words; i++)
msg[i] = mbx_data_rd32(mbx, MUCSE_MBX_FWPF_SHM + 4 * i);
mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM, 0);
hw->mbx.fw_req = mucse_mbx_get_fwreq(mbx);
mucse_mbx_inc_pf_ack(hw);
mucse_release_mbx_lock_pf(hw, false);
return 0;
}
static int mucse_check_for_msg_pf(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
u16 fw_req;
fw_req = mucse_mbx_get_fwreq(mbx);
if (fw_req == 0 || fw_req == hw->mbx.fw_req)
return -EIO;
return 0;
}
static int mucse_poll_for_msg(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
int val;
return read_poll_timeout(mucse_check_for_msg_pf,
val, !val, mbx->delay_us,
mbx->timeout_us,
false, hw);
}
int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size)
{
int err;
err = mucse_poll_for_msg(hw);
if (err)
return err;
return mucse_read_mbx_pf(hw, msg, size);
}
static u16 mucse_mbx_get_fwack(struct mucse_mbx_info *mbx)
{
u32 val = mbx_data_rd32(mbx, MUCSE_MBX_FW2PF_CNT);
return FIELD_GET(GENMASK_U32(31, 16), val);
}
static void mucse_mbx_inc_pf_req(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
u16 req;
u32 val;
val = mbx_data_rd32(mbx, MUCSE_MBX_PF2FW_CNT);
req = FIELD_GET(GENMASK_U32(15, 0), val);
req++;
val &= ~GENMASK_U32(15, 0);
val |= FIELD_PREP(GENMASK_U32(15, 0), req);
mbx_data_wr32(mbx, MUCSE_MBX_PF2FW_CNT, val);
}
static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size)
{
const int size_in_words = size / sizeof(u32);
struct mucse_mbx_info *mbx = &hw->mbx;
int err;
err = mucse_obtain_mbx_lock_pf(hw);
if (err)
return err;
for (int i = 0; i < size_in_words; i++)
mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4, msg[i]);
hw->mbx.fw_ack = mucse_mbx_get_fwack(mbx);
mucse_mbx_inc_pf_req(hw);
mucse_release_mbx_lock_pf(hw, true);
return 0;
}
static int mucse_check_for_ack_pf(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
u16 fw_ack;
fw_ack = mucse_mbx_get_fwack(mbx);
if (fw_ack == 0 || fw_ack == hw->mbx.fw_ack)
return -EIO;
return 0;
}
static int mucse_poll_for_ack(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
int val;
return read_poll_timeout(mucse_check_for_ack_pf,
val, !val, mbx->delay_us,
mbx->timeout_us,
false, hw);
}
int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size)
{
int err;
err = mucse_write_mbx_pf(hw, msg, size);
if (err)
return err;
return mucse_poll_for_ack(hw);
}
static void mucse_mbx_reset(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
u32 val;
val = mbx_data_rd32(mbx, MUCSE_MBX_FW2PF_CNT);
hw->mbx.fw_req = FIELD_GET(GENMASK_U32(15, 0), val);
hw->mbx.fw_ack = FIELD_GET(GENMASK_U32(31, 16), val);
mbx_ctrl_wr32(mbx, MUCSE_MBX_PF2FW_CTRL(mbx), 0);
mbx_ctrl_wr32(mbx, MUCSE_MBX_FWPF_MASK(mbx), GENMASK_U32(31, 16));
}
void mucse_init_mbx_params_pf(struct mucse_hw *hw)
{
struct mucse_mbx_info *mbx = &hw->mbx;
mbx->delay_us = 100;
mbx->timeout_us = 4 * USEC_PER_SEC;
mutex_init(&mbx->lock);
mucse_mbx_reset(hw);
}