#include "ice_type.h"
#include "ice_common.h"
#include "ice_ptp_hw.h"
#include "ice.h"
#include "ice_cpi.h"
static enum ice_sbq_dev_id ice_cpi_get_dest_dev(struct ice_hw *hw, u8 phy)
{
u8 curr_phy = hw->lane_num / hw->ptp.ports_per_phy;
if ((!ice_is_dual(hw) && phy) ||
(ice_is_dual(hw) && phy != curr_phy))
return ice_sbq_dev_phy_0_peer;
else
return ice_sbq_dev_phy_0;
}
static int ice_cpi_write_phy(struct ice_hw *hw, u8 phy, u32 addr, u32 val)
{
struct ice_sbq_msg_input msg = {
.dest_dev = ice_cpi_get_dest_dev(hw, phy),
.opcode = ice_sbq_msg_wr_np,
.msg_addr_low = lower_16_bits(addr),
.msg_addr_high = upper_16_bits(addr),
.data = val
};
int err;
err = ice_sbq_rw_reg(hw, &msg, LIBIE_AQ_FLAG_RD);
if (err)
ice_debug(hw, ICE_DBG_PTP,
"Failed to write CPI msg to phy %d, err: %d\n",
phy, err);
return err;
}
static int ice_cpi_read_phy(struct ice_hw *hw, u8 phy, u32 addr, u32 *val)
{
struct ice_sbq_msg_input msg = {
.dest_dev = ice_cpi_get_dest_dev(hw, phy),
.opcode = ice_sbq_msg_rd,
.msg_addr_low = lower_16_bits(addr),
.msg_addr_high = upper_16_bits(addr)
};
int err;
err = ice_sbq_rw_reg(hw, &msg, LIBIE_AQ_FLAG_RD);
if (err) {
ice_debug(hw, ICE_DBG_PTP,
"Failed to read CPI msg from phy %d, err: %d\n",
phy, err);
return err;
}
*val = msg.data;
return 0;
}
static int ice_cpi_wait_req0_ack0(struct ice_hw *hw, int phy)
{
u32 phy_val;
u32 lm_val;
for (int i = 0; i < CPI_RETRIES_COUNT; i++) {
int err;
err = ice_cpi_read_phy(hw, phy, CPI0_LM1_CMD_DATA, &lm_val);
if (err)
return err;
if (FIELD_GET(CPI_LM_CMD_REQ_M, lm_val))
goto retry;
err = ice_cpi_read_phy(hw, phy, CPI0_PHY1_CMD_DATA, &phy_val);
if (err)
return err;
if (!FIELD_GET(CPI_PHY_CMD_ACK_M, phy_val))
return 0;
retry:
msleep(CPI_RETRIES_CADENCE_MS);
}
return -ETIMEDOUT;
}
static int ice_cpi_wait_ack(struct ice_hw *hw, u8 phy, bool asserted,
u32 *data)
{
u32 phy_val;
for (int i = 0; i < CPI_RETRIES_COUNT; i++) {
int err;
err = ice_cpi_read_phy(hw, phy, CPI0_PHY1_CMD_DATA, &phy_val);
if (err)
return err;
if (asserted && FIELD_GET(CPI_PHY_CMD_ERROR_M, phy_val))
return -EFAULT;
if (asserted && FIELD_GET(CPI_PHY_CMD_ACK_M, phy_val)) {
if (data)
*data = phy_val;
return 0;
}
if (!asserted && !FIELD_GET(CPI_PHY_CMD_ACK_M, phy_val))
return 0;
msleep(CPI_RETRIES_CADENCE_MS);
}
return -ETIMEDOUT;
}
#define ice_cpi_wait_ack0(hw, port) \
ice_cpi_wait_ack(hw, port, false, NULL)
#define ice_cpi_wait_ack1(hw, port, data) \
ice_cpi_wait_ack(hw, port, true, data)
static int ice_cpi_req0(struct ice_hw *hw, u8 phy, u32 data)
{
data &= ~CPI_LM_CMD_REQ_M;
return ice_cpi_write_phy(hw, phy, CPI0_LM1_CMD_DATA, data);
}
static int ice_cpi_exec_cmd(struct ice_hw *hw, int phy, u32 data)
{
return ice_cpi_write_phy(hw, phy, CPI0_LM1_CMD_DATA, data);
}
static struct mutex *ice_cpi_phy_lock(struct ice_hw *hw, u8 phy)
{
struct ice_pf *pf = hw->back;
if (!pf || !pf->adapter || phy >= ICE_E825_MAX_PHYS)
return NULL;
return &pf->adapter->cpi_phy_lock[phy];
}
int ice_cpi_exec(struct ice_hw *hw, u8 phy,
const struct ice_cpi_cmd *cmd,
struct ice_cpi_resp *resp)
{
struct mutex *cpi_lock;
u32 phy_cmd, lm_cmd = 0;
int err, err1 = 0;
if (!cmd || !resp)
return -EINVAL;
cpi_lock = ice_cpi_phy_lock(hw, phy);
if (!cpi_lock)
return -EINVAL;
mutex_lock(cpi_lock);
lm_cmd =
FIELD_PREP(CPI_LM_CMD_REQ_M, CPI_LM_CMD_REQ) |
FIELD_PREP(CPI_LM_CMD_GET_SET_M, cmd->set) |
FIELD_PREP(CPI_LM_CMD_OPCODE_M, cmd->opcode) |
FIELD_PREP(CPI_LM_CMD_PORTLANE_M, cmd->port) |
FIELD_PREP(CPI_LM_CMD_DATA_M, cmd->data);
err = ice_cpi_wait_req0_ack0(hw, phy);
if (err)
goto cpi_exec_exit;
err = ice_cpi_exec_cmd(hw, phy, lm_cmd);
if (err)
goto cpi_deassert;
err = ice_cpi_wait_ack1(hw, phy, &phy_cmd);
if (err)
goto cpi_deassert;
if (FIELD_GET(CPI_LM_CMD_OPCODE_M, lm_cmd) !=
FIELD_GET(CPI_PHY_CMD_OPCODE_M, phy_cmd)) {
err = -EFAULT;
goto cpi_deassert;
}
resp->opcode = FIELD_GET(CPI_PHY_CMD_OPCODE_M, phy_cmd);
resp->data = FIELD_GET(CPI_PHY_CMD_DATA_M, phy_cmd);
resp->port = FIELD_GET(CPI_PHY_CMD_PORTLANE_M, phy_cmd);
cpi_deassert:
err1 = ice_cpi_req0(hw, phy, lm_cmd);
if (err1)
goto cpi_exec_exit;
err1 = ice_cpi_wait_ack0(hw, phy);
cpi_exec_exit:
if (!err)
err = err1;
mutex_unlock(cpi_lock);
return err;
}
static int ice_cpi_set_cmd(struct ice_hw *hw, u8 opcode, u8 phy, u8 port_lane,
u16 data)
{
struct ice_cpi_resp cpi_resp = {0};
struct ice_cpi_cmd cpi_cmd = {
.opcode = opcode,
.set = true,
.port = port_lane,
.data = data,
};
return ice_cpi_exec(hw, phy, &cpi_cmd, &cpi_resp);
}
int ice_cpi_ena_dis_clk_ref(struct ice_hw *hw, u8 phy,
enum ice_e825c_ref_clk clk, bool enable)
{
u16 val;
val = FIELD_PREP(CPI_OPCODE_PHY_CLK_PHY_SEL_M, phy) |
FIELD_PREP(CPI_OPCODE_PHY_CLK_REF_CTRL_M,
enable ? CPI_OPCODE_PHY_CLK_ENABLE :
CPI_OPCODE_PHY_CLK_DISABLE) |
FIELD_PREP(CPI_OPCODE_PHY_CLK_REF_SEL_M, clk);
return ice_cpi_set_cmd(hw, CPI_OPCODE_PHY_CLK, phy, 0, val);
}