#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/errno.h>
#include "aq_common.h"
#include "aq_hw.h"
#include "aq_hw_llh.h"
#include "aq_hw_llh_internal.h"
#include "aq_fw.h"
#include "aq_dbg.h"
enum aq_fw1x_mode {
FW1X_MPI_DEINIT = 0,
FW1X_MPI_RESERVED = 1,
FW1X_MPI_INIT = 2,
FW1X_MPI_POWER = 4,
};
enum aq_fw1x_rate {
FW1X_RATE_10G = 1 << 0,
FW1X_RATE_5G = 1 << 1,
FW1X_RATE_5GSR = 1 << 2,
FW1X_RATE_2G5 = 1 << 3,
FW1X_RATE_1G = 1 << 4,
FW1X_RATE_100M = 1 << 5,
FW1X_RATE_INVALID = 1 << 6,
};
union aq_fw1x_state_reg {
uint32_t val;
struct {
uint8_t mode;
uint8_t reserved1;
uint8_t speed;
uint8_t reserved2 : 1;
uint8_t disableDirtyWake : 1;
uint8_t reserved3 : 2;
uint8_t downshift : 4;
};
};
static int aq_fw1x_reset(struct aq_hw* hw);
static int aq_fw1x_set_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state mode,
enum aq_fw_link_speed speed);
static int aq_fw1x_get_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state* mode,
enum aq_fw_link_speed* speed, enum aq_fw_link_fc* fc);
static int aq_fw1x_get_mac_addr(struct aq_hw* hw, uint8_t* mac_addr);
static int aq_fw1x_get_stats(struct aq_hw* hw, struct aq_hw_stats* stats);
static enum aq_fw1x_mode
mpi_mode_to_fw1x(enum aq_hw_fw_mpi_state mode)
{
switch (mode) {
case MPI_DEINIT:
return (FW1X_MPI_DEINIT);
case MPI_INIT:
return (FW1X_MPI_INIT);
case MPI_POWER:
return (FW1X_MPI_POWER);
case MPI_RESET:
return (FW1X_MPI_RESERVED);
}
return (FW1X_MPI_RESERVED);
}
static enum aq_fw1x_rate
link_speed_mask_to_fw1x(uint32_t speed)
{
uint32_t rate = 0;
if (speed & aq_fw_10G)
rate |= FW1X_RATE_10G;
if (speed & aq_fw_5G) {
rate |= FW1X_RATE_5G;
rate |= FW1X_RATE_5GSR;
}
if (speed & aq_fw_2G5)
rate |= FW1X_RATE_2G5;
if (speed & aq_fw_1G)
rate |= FW1X_RATE_1G;
if (speed & aq_fw_100M)
rate |= FW1X_RATE_100M;
return ((enum aq_fw1x_rate)rate);
}
static enum aq_fw_link_speed
aq_fw1x_rate_to_link_speed(enum aq_fw1x_rate rate)
{
switch (rate) {
case FW1X_RATE_10G:
return (aq_fw_10G);
case FW1X_RATE_5G:
case FW1X_RATE_5GSR:
return (aq_fw_5G);
case FW1X_RATE_2G5:
return (aq_fw_2G5);
case FW1X_RATE_1G:
return (aq_fw_1G);
case FW1X_RATE_100M:
return (aq_fw_100M);
case FW1X_RATE_INVALID:
return (aq_fw_none);
}
return (aq_fw_none);
}
static int
aq_fw1x_reset(struct aq_hw* hw)
{
uint32_t tid0 = ~0u;
struct aq_hw_fw_mbox mbox;
const int retryCount = 1000;
for (int i = 0; i < retryCount; ++i) {
aq_hw_fw_downld_dwords(hw, hw->mbox_addr, (uint32_t*)&mbox,
(uint32_t)((char*)&mbox.stats - (char*)&mbox) / sizeof(uint32_t));
if (tid0 == ~0U) {
tid0 = mbox.transaction_id;
continue;
} else if (mbox.transaction_id != tid0) {
return (0);
}
DELAY(10);
}
trace_error(dbg_init, "F/W 1.x reset finalize timeout");
return (EBUSY);
}
static int
aq_fw1x_set_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state mode,
enum aq_fw_link_speed speed)
{
union aq_fw1x_state_reg state = {0};
state.mode = mpi_mode_to_fw1x(mode);
state.speed = link_speed_mask_to_fw1x(speed);
trace(dbg_init, "fw1x> set mode %d, rate mask = %#x; raw = %#x",
state.mode, state.speed, state.val);
AQ_WRITE_REG(hw, AQ_HW_MPI_CONTROL_ADR, state.val);
return (0);
}
static int
aq_fw1x_get_mode(struct aq_hw* hw, enum aq_hw_fw_mpi_state* mode,
enum aq_fw_link_speed* speed, enum aq_fw_link_fc* fc)
{
union aq_fw1x_state_reg state = { .val = AQ_READ_REG(hw, AQ_HW_MPI_STATE_ADR) };
trace(dbg_init, "fw1x> get_mode(): 0x36c -> %x, 0x368 -> %x",
state.val, AQ_READ_REG(hw, AQ_HW_MPI_CONTROL_ADR));
enum aq_hw_fw_mpi_state md = MPI_DEINIT;
switch (state.mode) {
case FW1X_MPI_DEINIT:
md = MPI_DEINIT;
break;
case FW1X_MPI_RESERVED:
md = MPI_RESET;
break;
case FW1X_MPI_INIT:
md = MPI_INIT;
break;
case FW1X_MPI_POWER:
md = MPI_POWER;
break;
}
if (mode)
*mode = md;
if (speed)
*speed = aq_fw1x_rate_to_link_speed(state.speed);
*fc = aq_fw_fc_none;
AQ_DBG_EXIT(0);
return (0);
}
static int
aq_fw1x_get_mac_addr(struct aq_hw* hw, uint8_t* mac)
{
int err = EFAULT;
uint32_t mac_addr[2];
AQ_DBG_ENTER();
uint32_t efuse_shadow_addr = AQ_READ_REG(hw, 0x374);
if (efuse_shadow_addr == 0) {
trace_error(dbg_init, "couldn't read eFUSE Shadow Address");
AQ_DBG_EXIT(EFAULT);
return (EFAULT);
}
err = aq_hw_fw_downld_dwords(hw, efuse_shadow_addr + (40 * 4),
mac_addr, nitems(mac_addr));
if (err != 0) {
mac_addr[0] = 0;
mac_addr[1] = 0;
AQ_DBG_EXIT(err);
return (err);
}
mac_addr[0] = bswap32(mac_addr[0]);
mac_addr[1] = bswap32(mac_addr[1]);
memcpy(mac, (uint8_t*)mac_addr, ETHER_ADDR_LEN);
trace(dbg_init, "fw1x> eFUSE MAC addr -> %02x-%02x-%02x-%02x-%02x-%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
AQ_DBG_EXIT(0);
return (0);
}
_Static_assert(sizeof(struct aq_fw1x_mbox_stats) ==
__offsetof(struct aq_hw_stats, brc),
"fw1x mailbox stats must match the aq_hw_stats prefix");
static int
aq_fw1x_get_stats(struct aq_hw* hw, struct aq_hw_stats* stats)
{
int err = 0;
AQ_DBG_ENTER();
err = aq_hw_fw_downld_dwords(hw, hw->mbox_addr,
(uint32_t*)(void*)&hw->mbox, sizeof hw->mbox / sizeof(uint32_t));
if (err == 0)
memcpy(stats, &hw->mbox.stats, sizeof hw->mbox.stats);
AQ_DBG_EXIT(err);
return (err);
}
const struct aq_firmware_ops aq_fw1x_ops =
{
.reset = aq_fw1x_reset,
.set_mode = aq_fw1x_set_mode,
.get_mode = aq_fw1x_get_mode,
.get_mac_addr = aq_fw1x_get_mac_addr,
.get_stats = aq_fw1x_get_stats,
};